LCOV - code coverage report
Current view:top level - file/lib/file/local_directory.dart (source / functions)CoverageTotalHitMissed
Test:core.lcovLines:73.8 %423111
Test Date:2026-05-09 12:09:36Functions:-0
Legend:Lines: hit not hit

           TLA  Line data    Source code
       1                 : // Copyright (c) 2026 Flipdare Pty Ltd. All rights reserved.
       2                 : // 
       3                 : // This file is part of Flipdare's proprietary software and contains
       4                 : // confidential and copyrighted material. Unauthorised copying,
       5                 : // modification, distribution, or use of this file is strictly
       6                 : // prohibited without prior written permission from Flipdare Pty Ltd.
       7                 : // 
       8                 : // This software includes third-party components licensed under MIT,
       9                 : // BSD, and Apache 2.0 licences. See THIRD_PARTY_NOTICES for details.
      10                 : //
      11                 : 
      12                 : import 'dart:io' show Directory;
      13                 : 
      14                 : import 'package:core/core.dart';
      15                 : import 'package:core/typedefs.dart';
      16                 : import 'package:cross_file/cross_file.dart';
      17                 : import 'package:meta/meta.dart';
      18                 : import 'package:path_provider/path_provider.dart';
      19                 : import 'package:path/path.dart' as path;
      20                 : 
      21                 : enum LocalDirectoryType {
      22                 :   temp('temp'),
      23                 :   persistent('persistent');
      24                 : 
      25                 :   final String name;
      26                 :   const LocalDirectoryType(this.name);
      27                 : }
      28                 : 
      29                 : /// NOTE: the cache can fallback to persistent if needed,
      30                 : /// NOTE: but persistent cannot fallback to cache.
      31                 : /// For persistent:
      32                 : ///  - prefer external if available
      33                 : ///  - else app directory
      34                 : /// For cache:
      35                 : ///  - prefer temp directory
      36                 : ///  - else system temp directory
      37                 : class LocalDirectory {
      38                 :   final LocalDirectoryType type;
      39                 : 
      40                 :   final DirCallback? _getAndroidAppDocDir;
      41                 :   final DirCallback? _getAppDocDir;
      42                 :   final DirCallback? _getTempDir;
      43                 :   final MaybeDirCallback? _getExtDir;
      44                 : 
      45                 :   final PermissionCheckInterface perms;
      46                 : 
      47                 :   @visibleForTesting
      48                 :   final bool useSystemTempFallback;
      49                 : 
      50 MIS           0 :   factory LocalDirectory.create(
      51                 :     LocalDirectoryType type, {
      52                 :     required PermissionCheckInterface perms,
      53                 :   }) {
      54               0 :     return type == LocalDirectoryType.temp
      55               0 :         ? LocalDirectory.temp(perms: perms)
      56               0 :         : LocalDirectory.persistent(perms: perms);
      57                 :   }
      58                 : 
      59               0 :   const LocalDirectory.temp({required this.perms})
      60                 :     : _getTempDir = getTemporaryDirectory, // cache
      61                 :       useSystemTempFallback = true, // cache
      62                 :       _getAndroidAppDocDir = getApplicationDocumentsDirectory, // persistent
      63                 :       _getAppDocDir = getLibraryDirectory, // persistent
      64                 :       _getExtDir = getExternalStorageDirectory, // persistent
      65                 :       type = LocalDirectoryType.temp;
      66                 : 
      67               0 :   const LocalDirectory.persistent({required this.perms})
      68                 :     : _getAndroidAppDocDir = getApplicationDocumentsDirectory, // persistent
      69                 :       _getAppDocDir = getLibraryDirectory, // persistent
      70                 :       _getExtDir = getExternalStorageDirectory, // persistent
      71                 :       _getTempDir = null,
      72                 :       useSystemTempFallback = false,
      73                 :       type = LocalDirectoryType.persistent;
      74                 : 
      75                 :   // for testing
      76 HIT           1 :   const LocalDirectory._custom(
      77                 :     this.type,
      78                 :     this.perms,
      79                 :     this._getAndroidAppDocDir,
      80                 :     this._getAppDocDir,
      81                 :     this._getTempDir,
      82                 :     this._getExtDir,
      83                 :     this.useSystemTempFallback,
      84                 :   );
      85                 : 
      86               1 :   Future<Directory?> getDirectory() async {
      87               2 :     if (!await perms.hasStoragePermission()) {
      88 MIS           0 :       LOG.w('No storage permission, cannot get local directory of type ${type.name}');
      89                 :       return null;
      90                 :     }
      91                 : 
      92 HIT           2 :     if (type == LocalDirectoryType.persistent) {
      93               1 :       return await _getPersistent();
      94                 :     } else {
      95               1 :       return await _getCache();
      96                 :     }
      97                 :   }
      98                 : 
      99               1 :   Future<XFile?> getFile({required String suffix}) async {
     100               2 :     if (!await perms.hasStoragePermission()) {
     101 MIS           0 :       LOG.w('No storage permission, cannot create file of type ${type.name}');
     102                 :       return null;
     103                 :     }
     104                 : 
     105 HIT           1 :     if (suffix.isEmpty) {
     106 MIS           0 :       LOG.e('Suffix is empty, cannot create file');
     107                 :       return null;
     108                 :     }
     109                 : 
     110 HIT           1 :     final dir = await getDirectory();
     111                 :     if (dir == null) {
     112 MIS           0 :       LOG.d('No directory available for temp file');
     113                 :       return null;
     114                 :     }
     115                 : 
     116 HIT           2 :     final filePostfix = type.name;
     117               3 :     final fileName = path.join(dir.path, '${kStripePrefixSmall}_$filePostfix.$suffix');
     118               3 :     LOG.d('Created file path: $fileName');
     119               1 :     return XFile(fileName);
     120                 :   }
     121                 : 
     122               1 :   Future<Directory?> _getPersistent() async {
     123                 :     // try external first
     124               2 :     Directory? dir = await _tryMaybe(_getExtDir);
     125                 :     if (dir != null) return dir;
     126                 : 
     127                 :     // then app directory
     128               3 :     dir = await _try(kIsAndroidDevice ? _getAndroidAppDocDir : _getAppDocDir);
     129                 :     if (dir != null) return dir;
     130                 : 
     131               2 :     LOG.w('Failed to get persistent directory..');
     132                 :     return null;
     133                 :   }
     134                 : 
     135               1 :   Future<Directory?> _getCache() async {
     136                 :     // try temp directory first
     137               2 :     Directory? dir = await _try(_getTempDir);
     138                 :     if (dir != null) return dir;
     139                 : 
     140               1 :     if (useSystemTempFallback) {
     141                 :       try {
     142               1 :         return Directory.systemTemp;
     143                 :       } catch (e) {
     144 MIS           0 :         LOG.e('Error getting system temp directory: $e');
     145                 :         return null;
     146                 :       }
     147                 :     }
     148                 : 
     149 HIT           2 :     LOG.w('Failed to get cache directory..');
     150                 :     return null;
     151                 :   }
     152                 : 
     153               1 :   Future<Directory?> _try(DirCallback? callback) async {
     154                 :     if (callback == null) return null;
     155                 :     try {
     156               1 :       Directory dir = await callback();
     157                 :       return dir;
     158                 :     } catch (e) {
     159               3 :       LOG.e('Error getting directory: $e');
     160                 :       return null;
     161                 :     }
     162                 :   }
     163                 : 
     164               1 :   Future<Directory?> _tryMaybe(MaybeDirCallback? callback) async {
     165                 :     if (callback == null) return null;
     166                 :     try {
     167               1 :       Directory? dir = await callback();
     168                 :       return dir;
     169                 :     } catch (e) {
     170               3 :       LOG.e('Error getting directory: $e');
     171                 :       return null;
     172                 :     }
     173                 :   }
     174                 : }
     175                 : 
     176                 : @visibleForTesting
     177                 : class MockLocalDirectory extends LocalDirectory {
     178               1 :   const MockLocalDirectory({
     179                 :     required LocalDirectoryType type,
     180                 :     required PermissionCheckInterface perms,
     181                 :     required DirCallback? getAndroidAppDocDir,
     182                 :     required DirCallback? getAppDocDir,
     183                 :     required DirCallback? getTempDir,
     184                 :     required MaybeDirCallback? getExtDir,
     185                 :     required bool useSystemTempFallback,
     186               1 :   }) : super._custom(
     187                 :          type,
     188                 :          perms,
     189                 :          getAndroidAppDocDir,
     190                 :          getAppDocDir,
     191                 :          getTempDir,
     192                 :          getExtDir,
     193                 :          useSystemTempFallback,
     194                 :        );
     195                 : }
        

Generated by: LCOV version 2.0-1