LCOV - code coverage report
Current view: top level - src - worker_results.dart Coverage Total Hit
Test: lcov.info Lines: 100.0 % 105 105
Test Date: 2026-04-30 18:23:23 Functions: - 0 0

            Line data    Source code
       1              : import 'package:flutter/foundation.dart';
       2              : 
       3              : /// Typed result helpers for built-in workers.
       4              : ///
       5              : /// Each class wraps the raw `resultData: Map<String, dynamic>?` returned by
       6              : /// [TaskEvent] and exposes typed, named fields.  This eliminates runtime
       7              : /// `as` casts and makes result handling refactor-safe.
       8              : ///
       9              : /// ## Usage
      10              : ///
      11              : /// ```dart
      12              : /// NativeWorkManager.events.listen((event) {
      13              : ///   if (!event.success) return;
      14              : ///
      15              : ///   switch (event.workerClassName) {
      16              : ///     case 'HttpDownloadWorker':
      17              : ///       final r = DownloadResult.from(event.resultData);
      18              : ///       print('Saved to ${r?.filePath} (${r?.fileSize} bytes)');
      19              : ///     case 'HttpUploadWorker':
      20              : ///       final r = UploadResult.from(event.resultData);
      21              : ///       print('Uploaded ${r?.fileCount} files, ${r?.uploadedSize} bytes');
      22              : ///     case 'CryptoWorker':
      23              : ///       final r = CryptoResult.from(event.resultData);
      24              : ///       print('Hash: ${r?.hash}');
      25              : ///   }
      26              : /// });
      27              : /// ```
      28              : 
      29              : // ── Download ─────────────────────────────────────────────────────────────────
      30              : 
      31              : /// Result data from [HttpDownloadWorker] and [ParallelHttpDownloadWorker].
      32              : @immutable
      33              : class DownloadResult {
      34            1 :   const DownloadResult({
      35              :     required this.filePath,
      36              :     required this.fileName,
      37              :     required this.fileSize,
      38              :     this.contentType,
      39              :     this.finalUrl,
      40              :     this.serverSuggestedName,
      41              :     this.skipped = false,
      42              :   });
      43              : 
      44              :   /// Absolute path of the saved file.
      45              :   final String filePath;
      46              : 
      47              :   /// Filename (last segment of [filePath]).
      48              :   final String fileName;
      49              : 
      50              :   /// File size in bytes.
      51              :   final int fileSize;
      52              : 
      53              :   /// MIME type from the `Content-Type` response header, if present.
      54              :   final String? contentType;
      55              : 
      56              :   /// Final URL after any redirects.
      57              :   final String? finalUrl;
      58              : 
      59              :   /// Filename suggested by the server's `Content-Disposition` header.
      60              :   final String? serverSuggestedName;
      61              : 
      62              :   /// `true` when the download was skipped because the file already existed
      63              :   /// and `skipExisting` or `onDuplicate: skip` was set.
      64              :   final bool skipped;
      65              : 
      66              :   /// Parse from a raw [TaskEvent.resultData] map. Returns `null` if [data] is
      67              :   /// `null` or missing required fields.
      68            1 :   static DownloadResult? from(Map<String, dynamic>? data) {
      69              :     if (data == null) return null;
      70            1 :     final fp = data['filePath'] as String?;
      71            1 :     final fn = data['fileName'] as String?;
      72            1 :     final fs = data['fileSize'];
      73              :     if (fp == null || fn == null || fs == null) return null;
      74            1 :     return DownloadResult(
      75              :       filePath: fp,
      76              :       fileName: fn,
      77            1 :       fileSize: (fs as num).toInt(),
      78            1 :       contentType: data['contentType'] as String?,
      79            1 :       finalUrl: data['finalUrl'] as String?,
      80            1 :       serverSuggestedName: data['serverSuggestedName'] as String?,
      81            1 :       skipped: (data['skipped'] as bool?) ?? false,
      82              :     );
      83              :   }
      84              : 
      85            1 :   @override
      86              :   String toString() =>
      87            4 :       'DownloadResult(filePath: $filePath, fileSize: $fileSize, skipped: $skipped)';
      88              : }
      89              : 
      90              : // ── Parallel download ─────────────────────────────────────────────────────────
      91              : 
      92              : /// Per-file outcome inside [ParallelDownloadResult.files].
      93              : @immutable
      94              : class DownloadFileOutcome {
      95            1 :   const DownloadFileOutcome({
      96              :     required this.url,
      97              :     required this.success,
      98              :     this.filePath,
      99              :     this.fileName,
     100              :     this.fileSize,
     101              :     this.error,
     102              :   });
     103              : 
     104              :   final String url;
     105              :   final bool success;
     106              :   final String? filePath;
     107              :   final String? fileName;
     108              :   final int? fileSize;
     109              :   final String? error;
     110              : 
     111            1 :   static DownloadFileOutcome _from(Map<String, dynamic> m) =>
     112            1 :       DownloadFileOutcome(
     113            1 :         url: (m['url'] as String?) ?? '',
     114            1 :         success: (m['success'] as bool?) ?? false,
     115            1 :         filePath: m['filePath'] as String?,
     116            1 :         fileName: m['fileName'] as String?,
     117            2 :         fileSize: (m['fileSize'] as num?)?.toInt(),
     118            1 :         error: m['error'] as String?,
     119              :       );
     120              : }
     121              : 
     122              : /// Result data from [ParallelHttpDownloadWorker].
     123              : @immutable
     124              : class ParallelDownloadResult {
     125            1 :   const ParallelDownloadResult({
     126              :     required this.downloadedCount,
     127              :     required this.failedCount,
     128              :     required this.totalBytes,
     129              :     required this.files,
     130              :   });
     131              : 
     132              :   final int downloadedCount;
     133              :   final int failedCount;
     134              :   final int totalBytes;
     135              :   final List<DownloadFileOutcome> files;
     136              : 
     137            1 :   static ParallelDownloadResult? from(Map<String, dynamic>? data) {
     138              :     if (data == null) return null;
     139            1 :     final rawFiles = data['fileResults'] as List?;
     140            1 :     return ParallelDownloadResult(
     141            2 :       downloadedCount: (data['downloadedCount'] as num?)?.toInt() ?? 0,
     142            2 :       failedCount: (data['failedCount'] as num?)?.toInt() ?? 0,
     143            2 :       totalBytes: (data['totalBytes'] as num?)?.toInt() ?? 0,
     144              :       files: rawFiles
     145            1 :               ?.whereType<Map>()
     146            2 :               .map((e) =>
     147            2 :                   DownloadFileOutcome._from(Map<String, dynamic>.from(e)))
     148            1 :               .toList() ??
     149              :           const [],
     150              :     );
     151              :   }
     152              : }
     153              : 
     154              : // ── Upload ────────────────────────────────────────────────────────────────────
     155              : 
     156              : /// Result data from [HttpUploadWorker].
     157              : @immutable
     158              : class UploadResult {
     159            1 :   const UploadResult({
     160              :     required this.statusCode,
     161              :     required this.uploadedSize,
     162              :     required this.fileCount,
     163              :     this.responseBody,
     164              :   });
     165              : 
     166              :   final int statusCode;
     167              : 
     168              :   /// Total bytes sent.
     169              :   final int uploadedSize;
     170              : 
     171              :   /// Number of files included in the upload.
     172              :   final int fileCount;
     173              : 
     174              :   /// Raw response body from the server, if any.
     175              :   final String? responseBody;
     176              : 
     177            1 :   static UploadResult? from(Map<String, dynamic>? data) {
     178              :     if (data == null) return null;
     179            1 :     return UploadResult(
     180            2 :       statusCode: (data['statusCode'] as num?)?.toInt() ?? 0,
     181            2 :       uploadedSize: (data['uploadedSize'] as num?)?.toInt() ?? 0,
     182            2 :       fileCount: (data['fileCount'] as num?)?.toInt() ?? 0,
     183            1 :       responseBody: data['responseBody'] as String?,
     184              :     );
     185              :   }
     186              : }
     187              : 
     188              : // ── Parallel upload ───────────────────────────────────────────────────────────
     189              : 
     190              : /// Per-file outcome inside [ParallelUploadResult.files].
     191              : @immutable
     192              : class UploadFileOutcome {
     193            1 :   const UploadFileOutcome({
     194              :     required this.fileName,
     195              :     required this.filePath,
     196              :     required this.fileSize,
     197              :     required this.success,
     198              :     this.statusCode,
     199              :     this.responseBody,
     200              :     this.error,
     201              :   });
     202              : 
     203              :   final String fileName;
     204              :   final String filePath;
     205              :   final int fileSize;
     206              :   final bool success;
     207              :   final int? statusCode;
     208              :   final String? responseBody;
     209              :   final String? error;
     210              : 
     211            2 :   static UploadFileOutcome _from(Map<String, dynamic> m) => UploadFileOutcome(
     212            1 :         fileName: (m['fileName'] as String?) ?? '',
     213            1 :         filePath: (m['filePath'] as String?) ?? '',
     214            2 :         fileSize: (m['fileSize'] as num?)?.toInt() ?? 0,
     215            1 :         success: (m['success'] as bool?) ?? false,
     216            2 :         statusCode: (m['statusCode'] as num?)?.toInt(),
     217            1 :         responseBody: m['responseBody'] as String?,
     218            1 :         error: m['error'] as String?,
     219              :       );
     220              : }
     221              : 
     222              : /// Result data from [ParallelHttpUploadWorker].
     223              : @immutable
     224              : class ParallelUploadResult {
     225            1 :   const ParallelUploadResult({
     226              :     required this.uploadedCount,
     227              :     required this.failedCount,
     228              :     required this.totalBytes,
     229              :     required this.files,
     230              :   });
     231              : 
     232              :   final int uploadedCount;
     233              :   final int failedCount;
     234              :   final int totalBytes;
     235              :   final List<UploadFileOutcome> files;
     236              : 
     237            1 :   static ParallelUploadResult? from(Map<String, dynamic>? data) {
     238              :     if (data == null) return null;
     239            1 :     final rawFiles = data['fileResults'] as List?;
     240            1 :     return ParallelUploadResult(
     241            2 :       uploadedCount: (data['uploadedCount'] as num?)?.toInt() ?? 0,
     242            2 :       failedCount: (data['failedCount'] as num?)?.toInt() ?? 0,
     243            2 :       totalBytes: (data['totalBytes'] as num?)?.toInt() ?? 0,
     244              :       files: rawFiles
     245            1 :               ?.whereType<Map>()
     246            4 :               .map((e) => UploadFileOutcome._from(Map<String, dynamic>.from(e)))
     247            1 :               .toList() ??
     248              :           const [],
     249              :     );
     250              :   }
     251              : }
     252              : 
     253              : // ── HTTP request ──────────────────────────────────────────────────────────────
     254              : 
     255              : /// Result data from [HttpRequestWorker].
     256              : @immutable
     257              : class HttpRequestResult {
     258            1 :   const HttpRequestResult({
     259              :     required this.statusCode,
     260              :     required this.body,
     261              :     required this.contentLength,
     262              :   });
     263              : 
     264              :   final int statusCode;
     265              :   final String body;
     266              :   final int contentLength;
     267              : 
     268            1 :   static HttpRequestResult? from(Map<String, dynamic>? data) {
     269              :     if (data == null) return null;
     270            1 :     return HttpRequestResult(
     271            2 :       statusCode: (data['statusCode'] as num?)?.toInt() ?? 0,
     272            1 :       body: (data['body'] as String?) ?? '',
     273            2 :       contentLength: (data['contentLength'] as num?)?.toInt() ?? 0,
     274              :     );
     275              :   }
     276              : }
     277              : 
     278              : // ── Crypto ────────────────────────────────────────────────────────────────────
     279              : 
     280              : /// Result data from [CryptoHashWorker], [CryptoEncryptWorker], or [CryptoDecryptWorker] operations.
     281              : @immutable
     282              : class CryptoResult {
     283            1 :   const CryptoResult({
     284              :     this.hash,
     285              :     this.algorithm,
     286              :     this.outputPath,
     287              :     this.fileSize,
     288              :     this.operation,
     289              :   });
     290              : 
     291              :   /// Hex-encoded hash digest (for `hash` operations).
     292              :   final String? hash;
     293              : 
     294              :   /// Hash algorithm used (e.g. `'SHA-256'`).
     295              :   final String? algorithm;
     296              : 
     297              :   /// Output file path (for encrypt/decrypt operations).
     298              :   final String? outputPath;
     299              : 
     300              :   /// Output file size in bytes.
     301              :   final int? fileSize;
     302              : 
     303              :   /// Operation performed: `'hash'`, `'encrypt'`, or `'decrypt'`.
     304              :   final String? operation;
     305              : 
     306            1 :   static CryptoResult? from(Map<String, dynamic>? data) {
     307              :     if (data == null) return null;
     308            1 :     return CryptoResult(
     309            1 :       hash: data['hash'] as String?,
     310            1 :       algorithm: data['algorithm'] as String?,
     311            1 :       outputPath: data['outputPath'] as String?,
     312            2 :       fileSize: (data['fileSize'] as num?)?.toInt(),
     313            1 :       operation: data['operation'] as String?,
     314              :     );
     315              :   }
     316              : }
     317              : 
     318              : // ── File compression / decompression ─────────────────────────────────────────
     319              : 
     320              : /// Result data from [FileCompressionWorker].
     321              : @immutable
     322              : class CompressionResult {
     323            1 :   const CompressionResult({
     324              :     required this.outputPath,
     325              :     required this.fileCount,
     326              :     required this.totalSize,
     327              :     required this.compressedSize,
     328              :   });
     329              : 
     330              :   final String outputPath;
     331              :   final int fileCount;
     332              :   final int totalSize;
     333              :   final int compressedSize;
     334              : 
     335            1 :   double get compressionRatio =>
     336            5 :       totalSize > 0 ? compressedSize / totalSize : 1.0;
     337              : 
     338            1 :   static CompressionResult? from(Map<String, dynamic>? data) {
     339              :     if (data == null) return null;
     340            1 :     final op = data['outputPath'] as String?;
     341              :     if (op == null) return null;
     342            1 :     return CompressionResult(
     343              :       outputPath: op,
     344            2 :       fileCount: (data['fileCount'] as num?)?.toInt() ?? 0,
     345            2 :       totalSize: (data['totalSize'] as num?)?.toInt() ?? 0,
     346            2 :       compressedSize: (data['compressedSize'] as num?)?.toInt() ?? 0,
     347              :     );
     348              :   }
     349              : }
     350              : 
     351              : /// Result data from [FileDecompressionWorker].
     352              : @immutable
     353              : class DecompressionResult {
     354            1 :   const DecompressionResult({
     355              :     required this.outputPath,
     356              :     required this.extractedCount,
     357              :     required this.totalSize,
     358              :   });
     359              : 
     360              :   final String outputPath;
     361              :   final int extractedCount;
     362              :   final int totalSize;
     363              : 
     364            1 :   static DecompressionResult? from(Map<String, dynamic>? data) {
     365              :     if (data == null) return null;
     366            1 :     final op = data['outputPath'] as String?;
     367              :     if (op == null) return null;
     368            1 :     return DecompressionResult(
     369              :       outputPath: op,
     370            2 :       extractedCount: (data['extractedCount'] as num?)?.toInt() ?? 0,
     371            2 :       totalSize: (data['totalSize'] as num?)?.toInt() ?? 0,
     372              :     );
     373              :   }
     374              : }
     375              : 
     376              : // ── Image processing ──────────────────────────────────────────────────────────
     377              : 
     378              : /// Result data from [ImageProcessWorker].
     379              : @immutable
     380              : class ImageProcessResult {
     381            1 :   const ImageProcessResult({
     382              :     required this.outputPath,
     383              :     required this.width,
     384              :     required this.height,
     385              :     required this.fileSize,
     386              :     this.format,
     387              :   });
     388              : 
     389              :   final String outputPath;
     390              :   final int width;
     391              :   final int height;
     392              :   final int fileSize;
     393              : 
     394              :   /// Output image format (e.g. `'jpeg'`, `'png'`, `'webp'`).
     395              :   final String? format;
     396              : 
     397            1 :   static ImageProcessResult? from(Map<String, dynamic>? data) {
     398              :     if (data == null) return null;
     399            1 :     final op = data['outputPath'] as String?;
     400              :     if (op == null) return null;
     401            1 :     return ImageProcessResult(
     402              :       outputPath: op,
     403            2 :       width: (data['width'] as num?)?.toInt() ?? 0,
     404            2 :       height: (data['height'] as num?)?.toInt() ?? 0,
     405            2 :       fileSize: (data['fileSize'] as num?)?.toInt() ?? 0,
     406            1 :       format: data['format'] as String?,
     407              :     );
     408              :   }
     409              : }
     410              : 
     411              : // ── File system ───────────────────────────────────────────────────────────────
     412              : 
     413              : /// Result data from file system workers ([FileSystemCopyWorker], [FileSystemMoveWorker], etc.).
     414              : @immutable
     415              : class FileSystemResult {
     416            1 :   const FileSystemResult({
     417              :     required this.operation,
     418              :     this.sourcePath,
     419              :     this.destinationPath,
     420              :     this.entries,
     421              :     this.count,
     422              :   });
     423              : 
     424              :   /// Operation performed: `'copy'`, `'move'`, `'delete'`, `'list'`, `'mkdir'`.
     425              :   final String operation;
     426              :   final String? sourcePath;
     427              :   final String? destinationPath;
     428              : 
     429              :   /// For `'list'` operations: list of file/directory paths.
     430              :   final List<String>? entries;
     431              : 
     432              :   /// For `'delete'` or batch operations: number of items affected.
     433              :   final int? count;
     434              : 
     435            1 :   static FileSystemResult? from(Map<String, dynamic>? data) {
     436              :     if (data == null) return null;
     437            1 :     final op = data['operation'] as String?;
     438              :     if (op == null) return null;
     439            1 :     final rawEntries = data['entries'] as List?;
     440            1 :     return FileSystemResult(
     441              :       operation: op,
     442            1 :       sourcePath: data['sourcePath'] as String?,
     443            1 :       destinationPath: data['destinationPath'] as String?,
     444            3 :       entries: rawEntries?.map((e) => e as String).toList(),
     445            2 :       count: (data['count'] as num?)?.toInt(),
     446              :     );
     447              :   }
     448              : }
        

Generated by: LCOV version 2.4-0