diff --git a/app/lib/entity/file/file_cache_manager.dart b/app/lib/entity/file/file_cache_manager.dart index 9d759c8e..493debcf 100644 --- a/app/lib/entity/file/file_cache_manager.dart +++ b/app/lib/entity/file/file_cache_manager.dart @@ -138,16 +138,16 @@ class FileSqliteCacheUpdater { final dirFiles = await dirFileQuery.get(); final diff = list_util.diff(dirFiles.map((e) => e.child), _childRowIds.sorted(Comparable.compare)); - if (diff.item1.isNotEmpty) { + if (diff.onlyInB.isNotEmpty) { await db.batch((batch) { // insert new children batch.insertAll(db.dirFiles, - diff.item1.map((k) => sql.DirFile(dir: _dirRowId!, child: k))); + diff.onlyInB.map((k) => sql.DirFile(dir: _dirRowId!, child: k))); }); } - if (diff.item2.isNotEmpty) { + if (diff.onlyInA.isNotEmpty) { // remove entries from the DirFiles table first - await diff.item2.withPartitionNoReturn((sublist) async { + await diff.onlyInA.withPartitionNoReturn((sublist) async { final deleteQuery = db.delete(db.dirFiles) ..where((t) => t.child.isIn(sublist)) ..where((t) => @@ -157,7 +157,7 @@ class FileSqliteCacheUpdater { // select files having another dir parent under this account (i.e., // moved files) - final moved = await diff.item2.withPartition((sublist) async { + final moved = await diff.onlyInA.withPartition((sublist) async { final query = db.selectOnly(db.dirFiles).join([ sql.innerJoin(db.accountFiles, db.accountFiles.file.equalsExp(db.dirFiles.dir)), @@ -168,7 +168,7 @@ class FileSqliteCacheUpdater { ..where(db.dirFiles.child.isIn(sublist)); return query.map((r) => r.read(db.dirFiles.child)!).get(); }, sql.maxByFileIdsSize); - final removed = diff.item2.where((e) => !moved.contains(e)).toList(); + final removed = diff.onlyInA.where((e) => !moved.contains(e)).toList(); if (removed.isNotEmpty) { // delete obsolete children await _removeSqliteFiles(db, dbAccount, removed); diff --git a/app/lib/list_util.dart b/app/lib/list_util.dart index e426c1dc..3e992e23 100644 --- a/app/lib/list_util.dart +++ b/app/lib/list_util.dart @@ -1,14 +1,24 @@ import 'package:nc_photos/iterator_extension.dart'; -import 'package:tuple/tuple.dart'; + +/// Contain results from the diff functions +/// +/// [onlyInA] contains items exist in a but not b, [onlyInB] contains items +/// exist in b but not a +class DiffResult { + const DiffResult({ + required this.onlyInA, + required this.onlyInB, + }); + + final List onlyInB; + final List onlyInA; +} /// Return the difference between two sorted lists, [a] and [b] /// /// [a] and [b] MUST be sorted in ascending order, otherwise the result is -/// undefined. -/// -/// The first returned list contains items exist in [b] but not [a], the second -/// returned list contains items exist in [a] but not [b] -Tuple2, List> diffWith( +/// undefined +DiffResult diffWith( Iterable a, Iterable b, int Function(T a, T b) comparator) { final aIt = a.iterator, bIt = b.iterator; final aMissing = [], bMissing = []; @@ -16,47 +26,46 @@ Tuple2, List> diffWith( if (!aIt.moveNext()) { // no more elements in a bIt.iterate((obj) => aMissing.add(obj)); - return Tuple2(aMissing, bMissing); + return DiffResult(onlyInB: aMissing, onlyInA: bMissing); } if (!bIt.moveNext()) { // no more elements in b // needed because aIt has already advanced bMissing.add(aIt.current); aIt.iterate((obj) => bMissing.add(obj)); - return Tuple2(aMissing, bMissing); + return DiffResult(onlyInB: aMissing, onlyInA: bMissing); } final result = _diffUntilEqual(aIt, bIt, comparator); - aMissing.addAll(result.item1); - bMissing.addAll(result.item2); + aMissing.addAll(result.onlyInB); + bMissing.addAll(result.onlyInA); } } -Tuple2, List> diff( - Iterable a, Iterable b) => +DiffResult diff(Iterable a, Iterable b) => diffWith(a, b, Comparable.compare); -Tuple2, List> _diffUntilEqual( +DiffResult _diffUntilEqual( Iterator aIt, Iterator bIt, int Function(T a, T b) comparator) { final a = aIt.current, b = bIt.current; final diff = comparator(a, b); if (diff < 0) { // a < b if (!aIt.moveNext()) { - return Tuple2([b] + bIt.toList(), [a]); + return DiffResult(onlyInB: [b] + bIt.toList(), onlyInA: [a]); } else { final result = _diffUntilEqual(aIt, bIt, comparator); - return Tuple2(result.item1, [a] + result.item2); + return DiffResult(onlyInB: result.onlyInB, onlyInA: [a] + result.onlyInA); } } else if (diff > 0) { // a > b if (!bIt.moveNext()) { - return Tuple2([b], [a] + aIt.toList()); + return DiffResult(onlyInB: [b], onlyInA: [a] + aIt.toList()); } else { final result = _diffUntilEqual(aIt, bIt, comparator); - return Tuple2([b] + result.item1, result.item2); + return DiffResult(onlyInB: [b] + result.onlyInB, onlyInA: result.onlyInA); } } else { // a == b - return const Tuple2([], []); + return const DiffResult(onlyInB: [], onlyInA: []); } } diff --git a/app/lib/use_case/cache_favorite.dart b/app/lib/use_case/cache_favorite.dart index 1b15aee0..c4f4cc30 100644 --- a/app/lib/use_case/cache_favorite.dart +++ b/app/lib/use_case/cache_favorite.dart @@ -34,9 +34,9 @@ class CacheFavorite { Map.fromEntries(cache.map((e) => MapEntry(e.fileId, e.rowId))); final diff = list_util.diff(cacheMap.keys.sorted(Comparable.compare), remote); - final newFileIds = diff.item1; + final newFileIds = diff.onlyInB; _log.info("[call] New favorites: ${newFileIds.toReadableString()}"); - final removedFildIds = diff.item2; + final removedFildIds = diff.onlyInA; _log.info( "[call] Removed favorites: ${removedFildIds.toReadableString()}"); diff --git a/app/lib/use_case/sync_person.dart b/app/lib/use_case/sync_person.dart index 7459df98..b26d1f77 100644 --- a/app/lib/use_case/sync_person.dart +++ b/app/lib/use_case/sync_person.dart @@ -39,9 +39,9 @@ class SyncPerson { final cache = await _c.personRepoLocal.list(account); int personSorter(Person a, Person b) => a.name.compareTo(b.name); final diff = list_util.diffWith(cache, remote, personSorter); - final inserts = diff.item1; + final inserts = diff.onlyInB; _log.info("[call] New people: ${inserts.toReadableString()}"); - final deletes = diff.item2; + final deletes = diff.onlyInA; _log.info("[call] Removed people: ${deletes.toReadableString()}"); final updates = remote.where((r) { final c = cache.firstWhereOrNull((c) => c.name == r.name); diff --git a/app/lib/use_case/sync_tag.dart b/app/lib/use_case/sync_tag.dart index edbd4700..b452b73b 100644 --- a/app/lib/use_case/sync_tag.dart +++ b/app/lib/use_case/sync_tag.dart @@ -28,9 +28,9 @@ class SyncTag { final remote = (await _c.tagRepoRemote.list(account))..sort(tagSorter); final cache = (await _c.tagRepoLocal.list(account))..sort(tagSorter); final diff = list_util.diffWith(cache, remote, tagSorter); - final inserts = diff.item1; + final inserts = diff.onlyInB; _log.info("[call] New tags: ${inserts.toReadableString()}"); - final deletes = diff.item2; + final deletes = diff.onlyInA; _log.info("[call] Removed tags: ${deletes.toReadableString()}"); final updates = remote.where((r) { final c = cache.firstWhereOrNull((c) => c.id == r.id); diff --git a/app/test/list_util_test.dart b/app/test/list_util_test.dart index 08a48866..3e46ed76 100644 --- a/app/test/list_util_test.dart +++ b/app/test/list_util_test.dart @@ -27,8 +27,8 @@ void main() { /// Expect: [1, 2], [] void _diffExtraBBegin() { final diff = list_util.diff([3, 4, 5], [1, 2, 3, 4, 5]); - expect(diff.item1, [1, 2]); - expect(diff.item2, []); + expect(diff.onlyInB, [1, 2]); + expect(diff.onlyInA, []); } /// Diff with extra elements at the end of list b @@ -38,8 +38,8 @@ void _diffExtraBBegin() { /// Expect: [4, 5], [] void _diffExtraBEnd() { final diff = list_util.diff([1, 2, 3], [1, 2, 3, 4, 5]); - expect(diff.item1, [4, 5]); - expect(diff.item2, []); + expect(diff.onlyInB, [4, 5]); + expect(diff.onlyInA, []); } /// Diff with extra elements in the middle of list b @@ -49,8 +49,8 @@ void _diffExtraBEnd() { /// Expect: [3, 4], [] void _diffExtraBMid() { final diff = list_util.diff([1, 2, 5], [1, 2, 3, 4, 5]); - expect(diff.item1, [3, 4]); - expect(diff.item2, []); + expect(diff.onlyInB, [3, 4]); + expect(diff.onlyInA, []); } /// Diff with list a being empty @@ -60,8 +60,8 @@ void _diffExtraBMid() { /// Expect: [1, 2, 3], [] void _diffAEmpty() { final diff = list_util.diff([], [1, 2, 3]); - expect(diff.item1, [1, 2, 3]); - expect(diff.item2, []); + expect(diff.onlyInB, [1, 2, 3]); + expect(diff.onlyInA, []); } /// Diff with extra elements at the beginning of list a @@ -71,8 +71,8 @@ void _diffAEmpty() { /// Expect: [], [1, 2] void _diffExtraABegin() { final diff = list_util.diff([1, 2, 3, 4, 5], [3, 4, 5]); - expect(diff.item1, []); - expect(diff.item2, [1, 2]); + expect(diff.onlyInB, []); + expect(diff.onlyInA, [1, 2]); } /// Diff with extra elements at the end of list a @@ -82,8 +82,8 @@ void _diffExtraABegin() { /// Expect: [], [4, 5] void _diffExtraAEnd() { final diff = list_util.diff([1, 2, 3, 4, 5], [1, 2, 3]); - expect(diff.item1, []); - expect(diff.item2, [4, 5]); + expect(diff.onlyInB, []); + expect(diff.onlyInA, [4, 5]); } /// Diff with extra elements in the middle of list a @@ -93,8 +93,8 @@ void _diffExtraAEnd() { /// Expect: [], [3, 4] void _diffExtraAMid() { final diff = list_util.diff([1, 2, 3, 4, 5], [1, 2, 5]); - expect(diff.item1, []); - expect(diff.item2, [3, 4]); + expect(diff.onlyInB, []); + expect(diff.onlyInA, [3, 4]); } /// Diff with list b being empty @@ -104,8 +104,8 @@ void _diffExtraAMid() { /// Expect: [], [1, 2, 3] void _diffBEmpty() { final diff = list_util.diff([1, 2, 3], []); - expect(diff.item1, []); - expect(diff.item2, [1, 2, 3]); + expect(diff.onlyInB, []); + expect(diff.onlyInA, [1, 2, 3]); } /// Diff with no matches between list a and b @@ -115,8 +115,8 @@ void _diffBEmpty() { /// Expect: [2, 4], [1, 3, 5] void _diffNoMatches() { final diff = list_util.diff([1, 3, 5], [2, 4]); - expect(diff.item1, [2, 4]); - expect(diff.item2, [1, 3, 5]); + expect(diff.onlyInB, [2, 4]); + expect(diff.onlyInA, [1, 3, 5]); } /// Diff between list a and b with repeated elements @@ -126,8 +126,8 @@ void _diffNoMatches() { /// Expect: [2], [] void _diffRepeatedElements() { final diff = list_util.diff([1, 2, 3], [1, 2, 2, 3]); - expect(diff.item1, [2]); - expect(diff.item2, []); + expect(diff.onlyInB, [2]); + expect(diff.onlyInA, []); } /// Diff between list a and b with repeated elements @@ -137,8 +137,8 @@ void _diffRepeatedElements() { /// Expect: [2, 2], [4, 4] void _diffRepeatedElements2() { final diff = list_util.diff([1, 3, 4, 4, 5], [1, 2, 2, 3, 5]); - expect(diff.item1, [2, 2]); - expect(diff.item2, [4, 4]); + expect(diff.onlyInB, [2, 2]); + expect(diff.onlyInA, [4, 4]); } /// Diff between list a and b @@ -148,6 +148,6 @@ void _diffRepeatedElements2() { /// Expect: [1, 4, 8, 13, 14], [2, 7, 10, 11, 12] void _diffMix() { final diff = list_util.diff([2, 3, 7, 10, 11, 12], [1, 3, 4, 8, 13, 14]); - expect(diff.item1, [1, 4, 8, 13, 14]); - expect(diff.item2, [2, 7, 10, 11, 12]); + expect(diff.onlyInB, [1, 4, 8, 13, 14]); + expect(diff.onlyInA, [2, 7, 10, 11, 12]); }