diff --git a/lib/src/snapshot.dart b/lib/src/snapshot.dart index 5908180..d28527e 100644 --- a/lib/src/snapshot.dart +++ b/lib/src/snapshot.dart @@ -105,6 +105,15 @@ abstract class Snapshot implements DeepImmutable { /// /// Unmodified children and grandchildren are recycled. So, also their /// conversions are reused. + /// + /// [value] may either be a JSON-like object or a [Snapshot]. + /// + /// When the new value equals the old value, this Snapshot will be returned. + /// In case the [value] argument was a compatible (i.e. with same decoder) + /// [Snapshot], the cache of the argument will be merged into this snapshot. + /// + /// When [value] is a compatible snapshot, value will be returned with the + /// cache of this snapshot merged. Snapshot set(dynamic value); /// Returns a snapshot with updated content at [path]. @@ -220,9 +229,45 @@ class _SnapshotImpl extends Snapshot { @override Snapshot set(newValue) { - if (newValue is Snapshot) { - // TODO recycle cache + if (newValue is _SnapshotImpl && _decoder == newValue._decoder) { + // the new value is a snapshot + + if (DeepCollectionEquality().equals(value, newValue.value)) { + // content is identical: return this with cache from newValue + + for (var k in newValue._childrenCache.keys) { + if (_childrenCache.containsKey(k)) { + _childrenCache[k] = + _childrenCache[k].set(newValue._childrenCache[k]); + } else { + _childrenCache[k] = newValue._childrenCache[k]; + } + } + + for (var t in newValue._decodingCache.keys) { + for (var f in newValue._decodingCache[t].keys) { + _decodingCache + .putIfAbsent(t, () => {}) + .putIfAbsent(f, () => newValue._decodingCache[t][f]); + } + } + return this; + } else { + // we will return the new value with cache values from old value + + for (var k in _childrenCache.keys) { + if (newValue._childrenCache.containsKey(k)) { + newValue._childrenCache[k] = + _childrenCache[k].set(newValue._childrenCache[k]); + } else { + newValue._childrenCache[k] = _childrenCache[k]; + } + } + + return newValue; + } } + newValue = newValue is Snapshot ? newValue.as() : newValue; var isEqual = DeepCollectionEquality().equals(value, newValue); if (isEqual) return this; diff --git a/test/snapshot_test.dart b/test/snapshot_test.dart index 9b7b153..9d8e0ef 100644 --- a/test/snapshot_test.dart +++ b/test/snapshot_test.dart @@ -201,6 +201,65 @@ void main() { expect(v.child('firstname').as(), 'John'); }); + + group('Setting with compatible snapshot', () { + var decoder = SnapshotDecoder.from(SnapshotDecoder.defaultDecoder) + ..register((s) => Address(s)) + ..seal(); + var json = { + 'firstname': 'Jane', + 'lastname': 'Doe', + 'address1': {'street': 'Mainstreet', 'number': '1', 'city': 'London'}, + 'address2': {'street': 'Mainstreet', 'number': '1', 'city': 'London'}, + 'address3': {'street': 'Mainstreet', 'number': '1', 'city': 'London'}, + }; + test('Should return this when content unchanged', () { + var person = Snapshot.fromJson(json, decoder: decoder); + var address1Snap = person.child('address1'); + var address1 = address1Snap.as
(); + var address3Snap = person.child('address3'); + + var newValue = Snapshot.fromJson(json, decoder: decoder); + newValue.child('address1').as
(); + var address2Snap = newValue.child('address2'); + var address2 = address2Snap.as
(); + var address3 = newValue.child('address3').as
(); + + var v = person.set(newValue); + + expect(v, same(person)); + expect(v.child('address1'), same(address1Snap)); + expect(v.child('address1').as
(), same(address1)); + expect(v.child('address2'), same(address2Snap)); + expect(v.child('address2').as
(), same(address2)); + expect(v.child('address3'), same(address3Snap)); + expect(v.child('address3').as
(), same(address3)); + }); + + test('Should return other when content changed', () { + var person = Snapshot.fromJson(json, decoder: decoder); + var address1Snap = person.child('address1'); + var address1 = address1Snap.as
(); + var address3Snap = person.child('address3'); + + var newValue = + Snapshot.fromJson(json..['firstname'] = 'John', decoder: decoder); + newValue.child('address1').as
(); + var address2Snap = newValue.child('address2'); + var address2 = address2Snap.as
(); + var address3 = newValue.child('address3').as
(); + + var v = person.set(newValue); + + expect(v, same(newValue)); + expect(v.child('address1'), same(address1Snap)); + expect(v.child('address1').as
(), same(address1)); + expect(v.child('address2'), same(address2Snap)); + expect(v.child('address2').as
(), same(address2)); + expect(v.child('address3'), same(address3Snap)); + expect(v.child('address3').as
(), same(address3)); + }); + }); }); group('Snapshot.setPath()', () {