LCOV - code coverage report
Current view: top level - src - storage.dart Coverage Total Hit
Test: filtered_lcov.info Lines: 90.9 % 66 60
Test Date: 2026-04-08 10:01:09 Functions: - 0 0

            Line data    Source code
       1              : import 'dart:async';
       2              : 
       3              : import 'package:collection/collection.dart';
       4              : import 'package:drift/drift.dart';
       5              : import 'package:flutter/widgets.dart';
       6              : import 'package:logging/logging.dart';
       7              : import 'package:the_storage/src/abstract_storage.dart';
       8              : import 'package:the_storage/src/db/storage_database.dart';
       9              : 
      10              : const _sqLiteSliceSize = 512;
      11              : 
      12              : /// Storage (db backend)
      13              : class Storage implements AbstractStorage<StorageValue> {
      14              :   /// Create a new storage
      15            3 :   Storage();
      16              : 
      17              :   late StorageDatabase _database;
      18              :   final _log = Logger('TheStorage: Storage');
      19              : 
      20              :   /// Init storage
      21            3 :   Future<void> init([
      22              :     String dbName = AbstractStorage.storageFileName,
      23              :     @visibleForTesting StorageDatabase? database,
      24              :   ]) async {
      25            3 :     WidgetsFlutterBinding.ensureInitialized();
      26              : 
      27            3 :     _database = database ?? StorageDatabase.withName(dbName);
      28              : 
      29            6 :     _log.finest('initialized');
      30              :   }
      31              : 
      32              :   /// Dispose storage
      33            3 :   Future<void> dispose() async {
      34            6 :     await _database.close();
      35              :   }
      36              : 
      37            2 :   @override
      38              :   Future<void> reset() async {
      39            2 :     WidgetsFlutterBinding.ensureInitialized();
      40              : 
      41            4 :     await _database.close();
      42              : 
      43            4 :     _log.finest('reset');
      44              :   }
      45              : 
      46            3 :   @override
      47              :   Future<void> clearAll() async {
      48           15 :     await _database.delete(_database.storageEntries).go();
      49              : 
      50            6 :     _log.finest('storage cleared');
      51              :   }
      52              : 
      53            3 :   @override
      54              :   Future<void> clearDomain([
      55              :     String? domain = AbstractStorage.defaultDomain,
      56              :   ]) async {
      57            6 :     await (_database.delete(
      58            6 :       _database.storageEntries,
      59           15 :     )..where((t) => t.domain.equals(domain!))).go();
      60              : 
      61            9 :     _log.finest('domain $domain cleared');
      62              :   }
      63              : 
      64            3 :   @override
      65              :   Future<void> set(
      66              :     String key,
      67              :     StorageValue value, {
      68              :     String domain = AbstractStorage.defaultDomain,
      69              :     bool overwrite = true,
      70              :   }) async {
      71            3 :     return setDomain(
      72            3 :       {
      73              :         key: value,
      74              :       },
      75              :       domain: domain,
      76              :       overwrite: overwrite,
      77              :     );
      78              :   }
      79              : 
      80            3 :   @override
      81              :   Future<void> setDomain(
      82              :     Map<String, StorageValue> pairs, {
      83              :     String domain = AbstractStorage.defaultDomain,
      84              :     bool overwrite = true,
      85              :   }) async {
      86            3 :     if (pairs.isEmpty) {
      87            0 :       _log.info('setAll called with empty pair map');
      88              : 
      89              :       return;
      90              :     }
      91              : 
      92              :     final mode = overwrite ? InsertMode.replace : InsertMode.insertOrIgnore;
      93              : 
      94            9 :     await _database.batch((batch) {
      95            6 :       for (final entry in pairs.entries) {
      96            3 :         batch.insert(
      97            6 :           _database.storageEntries,
      98            3 :           StorageEntriesCompanion.insert(
      99              :             domain: domain,
     100            3 :             key: entry.key,
     101            6 :             value: entry.value.value,
     102            6 :             iv: entry.value.iv,
     103              :           ),
     104              :           mode: mode,
     105              :         );
     106              :       }
     107              :     });
     108              :   }
     109              : 
     110            3 :   @override
     111              :   Future<void> delete(
     112              :     String key, {
     113              :     String domain = AbstractStorage.defaultDomain,
     114              :   }) async {
     115            6 :     return deleteDomain([key], domain: domain);
     116              :   }
     117              : 
     118            3 :   @override
     119              :   Future<void> deleteDomain(
     120              :     List<String> keys, {
     121              :     String domain = AbstractStorage.defaultDomain,
     122              :   }) async {
     123            3 :     if (keys.isEmpty) {
     124            0 :       _log.info('deleteDomain called with empty key list');
     125              : 
     126              :       return;
     127              :     }
     128              : 
     129              :     // SQLite has a limit of 999 variables per query
     130            6 :     for (final slice in keys.slices(_sqLiteSliceSize)) {
     131           15 :       await (_database.delete(_database.storageEntries)..where(
     132           18 :             (t) => t.domain.equals(domain) & t.key.isIn(slice),
     133              :           ))
     134            3 :           .go();
     135              :     }
     136              :   }
     137              : 
     138            3 :   @override
     139              :   Future<StorageValue?> get(
     140              :     String key, {
     141              :     StorageValue? defaultValue,
     142              :     String domain = AbstractStorage.defaultDomain,
     143              :   }) async {
     144              :     final row =
     145           15 :         await (_database.select(_database.storageEntries)..where(
     146           18 :               (t) => t.domain.equals(domain) & t.key.equals(key),
     147              :             ))
     148            3 :             .getSingleOrNull();
     149              : 
     150            9 :     return row != null ? StorageValue(row.value, row.iv) : defaultValue;
     151              :   }
     152              : 
     153            3 :   @override
     154              :   Future<Map<String, StorageValue>> getDomain({
     155              :     String domain = AbstractStorage.defaultDomain,
     156              :   }) async {
     157            6 :     final rows = await (_database.select(
     158            6 :       _database.storageEntries,
     159           15 :     )..where((t) => t.domain.equals(domain))).get();
     160              : 
     161            3 :     return {
     162           18 :       for (final row in rows) row.key: StorageValue(row.value, row.iv),
     163              :     };
     164              :   }
     165              : 
     166            3 :   @override
     167              :   Future<List<String>> getDomainKeys({
     168              :     String domain = AbstractStorage.defaultDomain,
     169              :   }) async {
     170            6 :     final rows = await (_database.select(
     171            6 :       _database.storageEntries,
     172           15 :     )..where((t) => t.domain.equals(domain))).get();
     173              : 
     174            3 :     return [
     175            6 :       for (final row in rows) row.key,
     176              :     ];
     177              :   }
     178              : }
     179              : 
     180              : /// Storage value unit
     181              : @immutable
     182              : class StorageValue implements Comparable<StorageValue> {
     183              :   /// Create a new storage value unit
     184            3 :   const StorageValue(this.value, this.iv);
     185              : 
     186              :   /// Value
     187              :   final String value;
     188              : 
     189              :   /// Initialization vector
     190              :   final String iv;
     191              : 
     192            0 :   @override
     193              :   int compareTo(StorageValue other) {
     194            0 :     return (value.compareTo(other.value) == 0 && iv.compareTo(other.iv) == 0)
     195              :         ? 0
     196              :         : 1;
     197              :   }
     198              : 
     199            2 :   @override
     200              :   bool operator ==(Object other) {
     201           14 :     return other is StorageValue && other.value == value && other.iv == iv;
     202              :   }
     203              : 
     204            0 :   @override
     205              :   int get hashCode {
     206            0 :     return value.hashCode + iv.hashCode;
     207              :   }
     208              : }
        

Generated by: LCOV version 2.4-0