From 5bd25c8dc904074fdc7130353614cba336e5dc86 Mon Sep 17 00:00:00 2001 From: AnhQuan Nguyen Date: Sun, 28 Feb 2021 22:04:09 -0800 Subject: [PATCH 1/5] nullsafety --- lib/cast.dart | 62 ++++++------- lib/src/coding.dart | 7 +- lib/src/keyed_archive.dart | 83 +++++++++-------- lib/src/list.dart | 20 ++-- lib/src/resolver.dart | 8 +- pubspec.yaml | 5 +- test/decode_test.dart | 182 +++++++++++++++++++++---------------- test/encode_test.dart | 88 ++++++++++++------ 8 files changed, 259 insertions(+), 196 deletions(-) diff --git a/lib/cast.dart b/lib/cast.dart index f47c028..b54cbbe 100644 --- a/lib/cast.dart +++ b/lib/cast.dart @@ -58,41 +58,41 @@ class AnyCast extends Cast { class IntCast extends Cast { const IntCast(); core.int _cast(dynamic from, core.String context, dynamic key) => - from is core.int - ? from - : throw new FailedCast(context, key, "$from is not an int"); + from is core.int + ? from + : throw new FailedCast(context, key, "$from is not an int"); } class DoubleCast extends Cast { const DoubleCast(); core.double _cast(dynamic from, core.String context, dynamic key) => - from is core.double - ? from - : throw new FailedCast(context, key, "$from is not an double"); + from is core.double + ? from + : throw new FailedCast(context, key, "$from is not an double"); } class StringCast extends Cast { const StringCast(); core.String _cast(dynamic from, core.String context, dynamic key) => - from is core.String - ? from - : throw new FailedCast(context, key, "$from is not a String"); + from is core.String + ? from + : throw new FailedCast(context, key, "$from is not a String"); } class BoolCast extends Cast { const BoolCast(); core.bool _cast(dynamic from, core.String context, dynamic key) => - from is core.bool - ? from - : throw new FailedCast(context, key, "$from is not a bool"); + from is core.bool + ? from + : throw new FailedCast(context, key, "$from is not a bool"); } class Map extends Cast> { final Cast _key; final Cast _value; const Map(Cast key, Cast value) - : _key = key, - _value = value; + : _key = key, + _value = value; core.Map _cast(dynamic from, core.String context, dynamic key) { if (from is core.Map) { var result = {}; @@ -110,7 +110,7 @@ class StringMap extends Cast> { final Cast _value; const StringMap(Cast value) : _value = value; core.Map _cast( - dynamic from, core.String context, dynamic key) { + dynamic from, core.String context, dynamic key) { if (from is core.Map) { var result = {}; for (core.String key in from.keys) { @@ -122,20 +122,17 @@ class StringMap extends Cast> { } } -class List extends Cast> { +class List extends Cast> { final Cast _entry; const List(Cast entry) : _entry = entry; - core.List _cast(dynamic from, core.String context, dynamic key) { + core.List _cast(dynamic from, core.String context, dynamic key) { if (from is core.List) { var length = from.length; - var result = core.List(length); - for (core.int i = 0; i < length; ++i) { - if (from[i] != null) { - result[i] = _entry._cast(from[i], "list entry", i); - } else { - result[i] = null; - } - } + var result = core.List.generate( + length, + (i) => + from[i] != null ? _entry._cast(from[i], "list entry", i) : null); + return result; } return throw new FailedCast(context, key, "not a list"); @@ -145,14 +142,13 @@ class List extends Cast> { class Keyed extends Cast> { Iterable get keys => _map.keys; final core.Map> _map; - const Keyed(core.Map> map) - : _map = map; + const Keyed(core.Map> map) : _map = map; core.Map _cast(dynamic from, core.String context, dynamic key) { core.Map result = {}; if (from is core.Map) { for (K key in from.keys) { if (_map.containsKey(key)) { - result[key] = _map[key]._cast(from[key], "map entry", key); + result[key] = _map[key]!._cast(from[key], "map entry", key); } else { result[key] = from[key]; } @@ -167,8 +163,8 @@ class OneOf extends Cast { final Cast _left; final Cast _right; const OneOf(Cast left, Cast right) - : _left = left, - _right = right; + : _left = left, + _right = right; dynamic _cast(dynamic from, core.String context, dynamic key) { try { return _left._cast(from, context, key); @@ -182,10 +178,10 @@ class Apply extends Cast { final Cast _first; final T Function(S) _transform; const Apply(T Function(S) transform, Cast first) - : _transform = transform, - _first = first; + : _transform = transform, + _first = first; T _cast(dynamic from, core.String context, dynamic key) => - _transform(_first._cast(from, context, key)); + _transform(_first._cast(from, context, key)); } class Future extends Cast> { diff --git a/lib/src/coding.dart b/lib/src/coding.dart index 98c5bde..e1db004 100644 --- a/lib/src/coding.dart +++ b/lib/src/coding.dart @@ -8,8 +8,8 @@ import 'package:codable/cast.dart' as cast; /// By overriding [decode] and [encode], an instance of this type will read or write its values /// into a data container that can be transferred into formats like JSON or YAML. abstract class Coding { - Uri referenceURI; - Map> get castMap => null; + Uri? referenceURI; + Map>? get castMap => null; @mustCallSuper void decode(KeyedArchive object) { @@ -19,5 +19,4 @@ abstract class Coding { // would prefer to write referenceURI to object here, but see note in KeyedArchive._encodedObject void encode(KeyedArchive object); - -} \ No newline at end of file +} diff --git a/lib/src/keyed_archive.dart b/lib/src/keyed_archive.dart index 71d07d7..ec2c9c2 100644 --- a/lib/src/keyed_archive.dart +++ b/lib/src/keyed_archive.dart @@ -24,7 +24,9 @@ import 'package:codable/src/resolver.dart'; /// final archive = KeyedArchive.archive(person); /// final json = json.encode(archive); /// -class KeyedArchive extends Object with MapMixin implements Referencable { +class KeyedArchive extends Object + with MapMixin + implements Referencable { /// Unarchives [data] into a [KeyedArchive] that can be used by [Coding.decode] to deserialize objects. /// /// Each [Map] in [data] (including [data] itself) is converted to a [KeyedArchive]. @@ -33,7 +35,8 @@ class KeyedArchive extends Object with MapMixin implements Refe /// /// If [allowReferences] is true, JSON Schema references will be traversed and decoded objects /// will contain values from the referenced object. This flag defaults to false. - static KeyedArchive unarchive(Map data, {bool allowReferences: false}) { + static KeyedArchive unarchive(Map data, + {bool allowReferences: false}) { final archive = new KeyedArchive(data); if (allowReferences) { archive.resolveOrThrow(new ReferenceResolver(archive)); @@ -49,7 +52,8 @@ class KeyedArchive extends Object with MapMixin implements Refe /// /// If [allowReferences] is true, JSON Schema references in the emitted document will be validated. /// Defaults to false. - static Map archive(Coding root, {bool allowReferences: false}) { + static Map archive(Coding root, + {bool allowReferences: false}) { final archive = new KeyedArchive({}); root.encode(archive); if (allowReferences) { @@ -80,11 +84,11 @@ class KeyedArchive extends Object with MapMixin implements Refe /// ..referenceURI = Uri(path: "/other/object"); /// archive.encodeObject("object", object); /// - Uri referenceURI; + Uri? referenceURI; - Map _map; - Coding _inflated; - KeyedArchive _objectReference; + late Map _map; + Coding? _inflated; + KeyedArchive? _objectReference; /// Typecast the values in this archive. /// @@ -109,7 +113,7 @@ class KeyedArchive extends Object with MapMixin implements Refe /// // This now becomes a valid assignment /// List key = archive.decode("key"); /// - void castValues(Map schema) { + void castValues(Map? schema) { if (schema == null) { return; } @@ -119,7 +123,7 @@ class KeyedArchive extends Object with MapMixin implements Refe if (_objectReference != null) { // todo: can optimize this by only running it once - _objectReference._map = caster.cast(_objectReference._map); + _objectReference!._map = caster.cast(_objectReference!._map); } } @@ -127,13 +131,13 @@ class KeyedArchive extends Object with MapMixin implements Refe _map[key] = value; } - dynamic operator [](Object key) => _getValue(key); + dynamic operator [](Object? key) => _getValue(key as String); Iterable get keys => _map.keys; void clear() => _map.clear(); - dynamic remove(Object key) => _map.remove(key); + dynamic remove(Object? key) => _map.remove(key); Map toPrimitive() { final out = {}; @@ -168,7 +172,7 @@ class KeyedArchive extends Object with MapMixin implements Refe _map[key] = new ListArchive.from(val); } else if (key == r"$ref") { if (val is Map) { - _objectReference = val; + _objectReference = val as KeyedArchive; } else { referenceURI = Uri.parse(Uri.parse(val).fragment); } @@ -182,9 +186,10 @@ class KeyedArchive extends Object with MapMixin implements Refe @override void resolveOrThrow(ReferenceResolver coder) { if (referenceURI != null) { - _objectReference = coder.resolve(referenceURI); + _objectReference = coder.resolve(referenceURI!); if (_objectReference == null) { - throw new ArgumentError("Invalid document. Reference '#${referenceURI.path}' does not exist in document."); + throw new ArgumentError( + "Invalid document. Reference '#${referenceURI!.path}' does not exist in document."); } } @@ -199,17 +204,19 @@ class KeyedArchive extends Object with MapMixin implements Refe /* decode */ - T _decodedObject(KeyedArchive raw, T inflate()) { + T? _decodedObject(KeyedArchive? raw, T inflate()) { if (raw == null) { return null; } if (raw._inflated == null) { raw._inflated = inflate(); - raw._inflated.decode(raw); + if (raw._inflated != null) { + raw._inflated!.decode(raw); + } } - return raw._inflated; + return raw._inflated as T; } /// Returns the object associated by [key]. @@ -221,15 +228,15 @@ class KeyedArchive extends Object with MapMixin implements Refe /// If this object is a reference to another object (via [referenceURI]), this object's key-value /// pairs will be searched first. If [key] is not found, the referenced object's key-values pairs are searched. /// If no match is found, null is returned. - T decode(String key) { + T? decode(String key) { var v = _getValue(key); if (v == null) { return null; } - if (T == Uri) { + if (T is Uri) { return Uri.parse(v) as T; - } else if (T == DateTime) { + } else if (T is DateTime) { return DateTime.parse(v) as T; } @@ -241,7 +248,7 @@ class KeyedArchive extends Object with MapMixin implements Refe /// [inflate] must create an empty instance of [T]. The value associated with [key] /// must be a [KeyedArchive] (a [Map]). The values of the associated object are read into /// the empty instance of [T]. - T decodeObject(String key, T inflate()) { + T? decodeObject(String key, T inflate()) { final val = _getValue(key); if (val == null) { return null; @@ -249,7 +256,7 @@ class KeyedArchive extends Object with MapMixin implements Refe if (val is! KeyedArchive) { throw new ArgumentError( - "Cannot decode key '$key' into '$T', because the value is not a Map. Actual value: '$val'."); + "Cannot decode key '$key' into '$T', because the value is not a Map. Actual value: '$val'."); } return _decodedObject(val, inflate); @@ -261,17 +268,17 @@ class KeyedArchive extends Object with MapMixin implements Refe /// must be a [ListArchive] (a [List] of [Map]). For each element of the archived list, /// [inflate] is invoked and each object in the archived list is decoded into /// the instance of [T]. - List decodeObjects(String key, T inflate()) { + List? decodeObjects(String key, T inflate()) { var val = _getValue(key); if (val == null) { return null; } if (val is! List) { throw new ArgumentError( - "Cannot decode key '$key' as 'List<$T>', because value is not a List. Actual value: '$val'."); + "Cannot decode key '$key' as 'List<$T>', because value is not a List. Actual value: '$val'."); } - return (val as List).map((v) => _decodedObject(v, inflate)).toList().cast(); + return val.map((v) => _decodedObject(v, inflate)).toList(); } /// Returns a map of [T]s associated with [key]. @@ -280,22 +287,24 @@ class KeyedArchive extends Object with MapMixin implements Refe /// must be a [KeyedArchive] (a [Map]), where each value is a [T]. /// For each key-value pair of the archived map, [inflate] is invoked and /// each value is decoded into the instance of [T]. - Map decodeObjectMap(String key, T inflate()) { + Map? decodeObjectMap(String key, T inflate()) { var v = _getValue(key); if (v == null) { return null; } if (v is! Map) { - throw new ArgumentError("Cannot decode key '$key' as 'Map', because value is not a Map. Actual value: '$v'."); + throw new ArgumentError( + "Cannot decode key '$key' as 'Map', because value is not a Map. Actual value: '$v'."); } - return new Map.fromIterable(v.keys, key: (k) => k, value: (k) => _decodedObject(v[k], inflate)); + return Map.fromIterable(v.keys, + key: (k) => k, value: (k) => _decodedObject(v[k], inflate)); } /* encode */ - Map _encodedObject(Coding object) { + Map? _encodedObject(Coding? object) { if (object == null) { return null; } @@ -305,9 +314,11 @@ class KeyedArchive extends Object with MapMixin implements Refe // they are currently not being emitted. the solution is probably tricky. // letting encode run as normal would stack overflow when there is a cyclic // reference between this object and another. - var json = new KeyedArchive._empty().._map = {}..referenceURI = object.referenceURI; + var json = new KeyedArchive._empty() + .._map = {} + ..referenceURI = object.referenceURI; if (json.referenceURI != null) { - json._map[r"$ref"] = Uri(fragment: json.referenceURI.path).toString(); + json._map[r"$ref"] = Uri(fragment: json.referenceURI!.path).toString(); } else { object.encode(json); } @@ -339,7 +350,7 @@ class KeyedArchive extends Object with MapMixin implements Refe /// /// This invokes [Coding.encode] on [value] and adds the object /// to this archive for the key [key]. - void encodeObject(String key, Coding value) { + void encodeObject(String key, Coding? value) { if (value == null) { return; } @@ -351,19 +362,20 @@ class KeyedArchive extends Object with MapMixin implements Refe /// /// This invokes [Coding.encode] on each object in [value] and adds the list of objects /// to this archive for the key [key]. - void encodeObjects(String key, List value) { + void encodeObjects(String key, List? value) { if (value == null) { return; } - _map[key] = new ListArchive.from(value.map((v) => _encodedObject(v)).toList()); + _map[key] = + new ListArchive.from(value.map((v) => _encodedObject(v)).toList()); } /// Encodes map of [Coding] objects into this object for [key]. /// /// This invokes [Coding.encode] on each value in [value] and adds the map of objects /// to this archive for the key [key]. - void encodeObjectMap(String key, Map value) { + void encodeObjectMap(String key, Map? value) { if (value == null) { return; } @@ -376,4 +388,3 @@ class KeyedArchive extends Object with MapMixin implements Refe _map[key] = object; } } - diff --git a/lib/src/list.dart b/lib/src/list.dart index 2726ae8..8ec0f14 100644 --- a/lib/src/list.dart +++ b/lib/src/list.dart @@ -8,21 +8,23 @@ import 'package:codable/src/resolver.dart'; /// A list of values in a [KeyedArchive]. /// /// This object is a [List] that has additional behavior for encoding and decoding [Coding] objects. -class ListArchive extends Object with ListMixin implements Referencable { +class ListArchive extends Object + with ListMixin + implements Referencable { final List _inner; ListArchive() : _inner = []; /// Replaces all instances of [Map] and [List] in this object with [KeyedArchive] and [ListArchive]s. ListArchive.from(List raw) - : _inner = raw.map((e) { - if (e is Map) { - return KeyedArchive(e); - } else if (e is List) { - return ListArchive.from(e); - } - return e; - }).toList(); + : _inner = raw.map((e) { + if (e is Map) { + return KeyedArchive(e); + } else if (e is List) { + return ListArchive.from(e); + } + return e; + }).toList(); @override operator [](int index) => _inner[index]; diff --git a/lib/src/resolver.dart b/lib/src/resolver.dart index 79d8a50..f5f0dc1 100644 --- a/lib/src/resolver.dart +++ b/lib/src/resolver.dart @@ -5,9 +5,9 @@ class ReferenceResolver { final KeyedArchive document; - KeyedArchive resolve(Uri ref) { + KeyedArchive? resolve(Uri ref) { return ref.pathSegments.fold(document, (objectPtr, pathSegment) { - return objectPtr[pathSegment] as Map; - }) as KeyedArchive; + return (objectPtr as Map)[pathSegment]; + }); } -} \ No newline at end of file +} diff --git a/pubspec.yaml b/pubspec.yaml index 71d6909..6f5bac7 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -5,10 +5,9 @@ homepage: https://github.com/stablekernel/dart-codable author: stable|kernel environment: - sdk: '>=2.0.0 <3.0.0' + sdk: '>=2.12.0-0 <3.0.0' dependencies: - meta: ^1.1.5 dev_dependencies: - test: ^1.3.0 + test: ^1.16.4 diff --git a/test/decode_test.dart b/test/decode_test.dart index 5862563..d47a75b 100644 --- a/test/decode_test.dart +++ b/test/decode_test.dart @@ -8,7 +8,7 @@ void main() { group("Primitive decode", () { test("Can decode primitive type", () { final archive = getJSONArchive({"key": 2}); - int val = archive.decode("key"); + int val = archive!.decode("key"); expect(val, 2); }); @@ -16,7 +16,7 @@ void main() { final archive = getJSONArchive({ "key": [1, "2"] }); - List l = archive.decode("key"); + List l = archive!.decode("key"); expect(l, [1, "2"]); }); @@ -24,32 +24,32 @@ void main() { final archive = getJSONArchive({ "key": {"key": "val"} }); - Map d = archive.decode("key"); + Map d = archive!.decode("key"); expect(d, {"key": "val"}); }); test("Can decode URI", () { final archive = getJSONArchive({"key": "https://host.com"}); - Uri d = archive.decode("key"); + Uri d = Uri.parse(archive!.decode("key")); expect(d.host, "host.com"); }); test("Can decode DateTime", () { final date = new DateTime.now(); final archive = getJSONArchive({"key": date.toIso8601String()}); - DateTime d = archive.decode("key"); + DateTime d = DateTime.parse(archive!.decode("key")); expect(d.isAtSameMomentAs(date), true); }); test("If value is null, return null from decode", () { final archive = getJSONArchive({"key": null}); - int val = archive.decode("key"); + int? val = archive!.decode("key"); expect(val, isNull); }); test("If archive does not contain key, return null from decode", () { final archive = getJSONArchive({}); - int val = archive.decode("key"); + int? val = archive!.decode("key"); expect(val, isNull); }); }); @@ -59,7 +59,7 @@ void main() { final archive = getJSONArchive({ "key": {"key": "val"} }); - archive.castValues({"key": cast.Map(cast.String, cast.String)}); + archive!.castValues({"key": cast.Map(cast.String, cast.String)}); Map d = archive.decode("key"); expect(d, {"key": "val"}); }); @@ -70,27 +70,29 @@ void main() { "key": ["val"] } }); - archive.castValues({"key": cast.Map(cast.String, cast.List(cast.String))}); - Map> d = archive.decode("key"); + archive! + .castValues({"key": cast.Map(cast.String, cast.List(cast.String))}); + Map>? d = archive.decode("key"); expect(d, { "key": ["val"] }); }); - test("Can decode Map> where elements are null", () { + test("Can decode Map> where elements are null", () { final archive = getJSONArchive({ "key": { "key": [null, null] } }); - archive.castValues({"key": cast.Map(cast.String, cast.List(cast.String))}); - Map> d = archive.decode("key"); + archive! + .castValues({"key": cast.Map(cast.String, cast.List(cast.String))}); + Map>? d = archive.decode("key"); expect(d, { "key": [null, null] }); }); - test("Can decode Map>>", () { + test("Can decode Map>>", () { final archive = getJSONArchive({ "key": { "key": { @@ -98,8 +100,11 @@ void main() { } } }); - archive.castValues({"key": cast.Map(cast.String, cast.Map(cast.String, cast.List(cast.String)))}); - Map>> d = archive.decode("key"); + archive!.castValues({ + "key": + cast.Map(cast.String, cast.Map(cast.String, cast.List(cast.String))) + }); + Map>> d = archive.decode("key"); expect(d, { "key": { "key": ["val", null] @@ -113,8 +118,8 @@ void main() { final archive = getJSONArchive({ "key": ["val", null] }); - archive.castValues({"key": cast.List(cast.String)}); - List d = archive.decode("key"); + archive!.castValues({"key": cast.List(cast.String)}); + List d = archive.decode("key"); expect(d, ["val", null]); }); @@ -127,8 +132,9 @@ void main() { null ] }); - archive.castValues({"key": cast.List(cast.Map(cast.String, cast.List(cast.String)))}); - List>> d = archive.decode("key"); + archive!.castValues( + {"key": cast.List(cast.Map(cast.String, cast.List(cast.String)))}); + List>?> d = archive.decode("key"); expect(d, [ { "key": ["val", null] @@ -143,7 +149,7 @@ void main() { final archive = getJSONArchive({ "key": {"name": "Bob"} }); - Parent p = archive.decodeObject("key", () => Parent()); + Parent p = archive!.decodeObject("key", () => Parent())!; expect(p.name, "Bob"); expect(p.child, isNull); expect(p.children, isNull); @@ -157,7 +163,7 @@ void main() { ] }); try { - archive.decodeObject("key", () => Parent()); + archive!.decodeObject("key", () => Parent()); fail('unreachable'); } on ArgumentError {} }); @@ -170,23 +176,27 @@ void main() { {"name": "Sally"} ] }); - List p = archive.decodeObjects("key", () => Parent()); - expect(p[0].name, "Bob"); + List p = archive!.decodeObjects("key", () => Parent())!; + expect(p[0]!.name, "Bob"); expect(p[1], isNull); - expect(p[2].name, "Sally"); + expect(p[2]!.name, "Sally"); }); - test("If coding object list is paired with non-List, an exception is thrown", () { + test( + "If coding object list is paired with non-List, an exception is thrown", + () { final archive = getJSONArchive({ "key": {"name": "Bob"} }); try { - archive.decodeObjects("key", () => Parent()); + archive!.decodeObjects("key", () => Parent()); fail('unreachable'); } on ArgumentError {} }); - test("If any element of coding list is not a coding object, an exception is thrown", () { + test( + "If any element of coding list is not a coding object, an exception is thrown", + () { final archive = getJSONArchive({ "key": [ {"name": "Bob"}, @@ -194,7 +204,7 @@ void main() { ] }); try { - archive.decodeObjects("key", () => Parent()); + archive!.decodeObjects("key", () => Parent()); fail('unreachable'); } on TypeError {} }); @@ -207,26 +217,29 @@ void main() { } }); - final map = archive.decodeObjectMap("key", () => Parent()); - expect(map.length, 2); - expect(map["1"].name, "Bob"); + final map = archive!.decodeObjectMap("key", () => Parent()); + expect(map!.length, 2); + expect(map["1"]!.name, "Bob"); expect(map["2"], isNull); }); - test("If coding object map is paired with non-Map, an exception is thrown", () { + test("If coding object map is paired with non-Map, an exception is thrown", + () { final archive = getJSONArchive({"key": []}); try { - archive.decodeObjectMap("key", () => Parent()); + archive!.decodeObjectMap("key", () => Parent()); fail('unreachable'); } on ArgumentError {} }); - test("If any element of coding map is not a coding object, an exception is thrown", () { + test( + "If any element of coding map is not a coding object, an exception is thrown", + () { final archive = getJSONArchive({ "key": {"1": "2"} }); try { - archive.decodeObjectMap("key", () => Parent()); + archive!.decodeObjectMap("key", () => Parent()); fail('unreachable'); } on TypeError {} }); @@ -241,9 +254,9 @@ void main() { } }); - final o = archive.decodeObject("key", () => Parent()); - expect(o.name, "Bob"); - expect(o.child.name, "Sally"); + final o = archive!.decodeObject("key", () => Parent()); + expect(o!.name, "Bob"); + expect(o.child!.name, "Sally"); expect(o.childMap, isNull); expect(o.children, isNull); }); @@ -258,12 +271,12 @@ void main() { } }); - final o = archive.decodeObject("key", () => Parent()); - expect(o.name, "Bob"); + final o = archive!.decodeObject("key", () => Parent()); + expect(o!.name, "Bob"); expect(o.child, isNull); expect(o.childMap, isNull); - expect(o.children.length, 1); - expect(o.children.first.name, "Sally"); + expect(o.children!.length, 1); + expect(o.children!.first!.name, "Sally"); }); test("Can decode map of nested objects", () { @@ -276,12 +289,12 @@ void main() { } }); - final o = archive.decodeObject("key", () => Parent()); - expect(o.name, "Bob"); + final o = archive!.decodeObject("key", () => Parent()); + expect(o!.name, "Bob"); expect(o.children, isNull); expect(o.child, isNull); - expect(o.childMap.length, 1); - expect(o.childMap["sally"].name, "Sally"); + expect(o.childMap!.length, 1); + expect(o.childMap!["sally"]!.name, "Sally"); }); }); @@ -295,13 +308,15 @@ void main() { } }, allowReferences: true); - final p = archive.decodeObject("parent", () => Parent()); - expect(p.name, "Bob"); - expect(p.child.name, "Sally"); - expect(p.child.parent, isNull); + final p = archive!.decodeObject("parent", () => Parent()); + expect(p!.name, "Bob"); + expect(p.child!.name, "Sally"); + expect(p.child!.parent, isNull); }); - test("If reference doesn't exist, an error is thrown when creating document", () { + test( + "If reference doesn't exist, an error is thrown when creating document", + () { try { getJSONArchive({ "parent": { @@ -320,32 +335,41 @@ void main() { "child": {"name": "Sally"}, "parent": { "name": "Bob", - "children": [{"\$ref": "#/child"}, {"name": "fred"}] + "children": [ + {"\$ref": "#/child"}, + {"name": "fred"} + ] } }, allowReferences: true); - final p = archive.decodeObject("parent", () => Parent()); - expect(p.name, "Bob"); - expect(p.children.first.name, "Sally"); - expect(p.children.last.name, "fred"); + final p = archive!.decodeObject("parent", () => Parent()); + expect(p!.name, "Bob"); + expect(p.children!.first!.name, "Sally"); + expect(p.children!.last!.name, "fred"); }); test("Cyclical references are resolved", () { final archive = getJSONArchive({ - "child": {"name": "Sally", "parent": {"\$ref": "#/parent"}}, + "child": { + "name": "Sally", + "parent": {"\$ref": "#/parent"} + }, "parent": { "name": "Bob", - "children": [{"\$ref": "#/child"}, {"name": "fred"}] + "children": [ + {"\$ref": "#/child"}, + {"name": "fred"} + ] } }, allowReferences: true); - final p = archive.decodeObject("parent", () => Parent()); - expect(p.name, "Bob"); - expect(p.children.first.name, "Sally"); - expect(p.children.first.parent.name, "Bob"); - expect(p.children.last.name, "fred"); + final p = archive!.decodeObject("parent", () => Parent()); + expect(p!.name, "Bob"); + expect(p.children!.first!.name, "Sally"); + expect(p.children!.first!.parent!.name, "Bob"); + expect(p.children!.last!.name, "fred"); - expect(p.hashCode, isNot(p.children.first.parent.hashCode)); + expect(p.hashCode, isNot(p.children!.first!.parent.hashCode)); }); test("Can override castMap to coerce values", () { @@ -355,30 +379,28 @@ void main() { "things": ["value"] } }); - final p = archive.decodeObject("key", () => Parent()); - expect(p.things, ["value"]); + final p = archive!.decodeObject("key", () => Parent()); + expect(p!.things, ["value"]); }); }); - } /// Strips type info from data -KeyedArchive getJSONArchive(dynamic data, {bool allowReferences: false}) { - return KeyedArchive.unarchive(json.decode(json.encode(data)), allowReferences: allowReferences); +KeyedArchive? getJSONArchive(dynamic data, {bool allowReferences: false}) { + return KeyedArchive.unarchive(json.decode(json.encode(data)), + allowReferences: allowReferences); } class Parent extends Coding { - String name; - Child child; - List children; - Map childMap; - List things; + String? name; + Child? child; + List? children; + Map? childMap; + List? things; @override Map> get castMap { - return { - "things": cast.List(cast.String) - }; + return {"things": cast.List(cast.String)}; } @override @@ -397,9 +419,9 @@ class Parent extends Coding { } class Child extends Coding { - String name; + String? name; - Parent parent; + Parent? parent; @override void decode(KeyedArchive object) { diff --git a/test/encode_test.dart b/test/encode_test.dart index c4f313b..62e1790 100644 --- a/test/encode_test.dart +++ b/test/encode_test.dart @@ -72,7 +72,8 @@ void main() { test("Can encode list of Coding objects", () { final out = encode((object) { - object.encodeObject("key", Parent("Bob", children: [Child("Fred"), null, Child("Sally")])); + object.encodeObject("key", + Parent("Bob", children: [Child("Fred"), null, Child("Sally")])); }); expect(out, { @@ -90,7 +91,12 @@ void main() { test("Can encode map of Coding objects", () { final out = encode((object) { object.encodeObject( - "key", Parent("Bob", childMap: {"fred": Child("Fred"), "null": null, "sally": Child("Sally")})); + "key", + Parent("Bob", childMap: { + "fred": Child("Fred"), + "null": null, + "sally": Child("Sally") + })); }); expect(out, { @@ -109,7 +115,9 @@ void main() { group("Coding object references", () { test("Parent can contain reference to child in single object encode", () { final container = Container( - Parent("Bob", child: Child._()..referenceURI = Uri(path: "/definitions/child")), {"child": Child("Sally")}); + Parent("Bob", + child: Child._()..referenceURI = Uri(path: "/definitions/child")), + {"child": Child("Sally")}); final out = KeyedArchive.archive(container, allowReferences: true); expect(out, { @@ -123,8 +131,13 @@ void main() { }); }); - test("If reference doesn't exist, an error is thrown when creating document", () { - final container = Container(Parent("Bob", child: Child._()..referenceURI = Uri(path: "/definitions/child")), {}); + test( + "If reference doesn't exist, an error is thrown when creating document", + () { + final container = Container( + Parent("Bob", + child: Child._()..referenceURI = Uri(path: "/definitions/child")), + {}); try { KeyedArchive.archive(container, allowReferences: true); @@ -134,9 +147,14 @@ void main() { } }); - test("If reference doesn't exist in objectMap, an error is thrown when creating document", () { - final container = - Container(Parent("Bob", childMap: {"c": Child._()..referenceURI = Uri(path: "/definitions/child")}), {}); + test( + "If reference doesn't exist in objectMap, an error is thrown when creating document", + () { + final container = Container( + Parent("Bob", childMap: { + "c": Child._()..referenceURI = Uri(path: "/definitions/child") + }), + {}); try { KeyedArchive.archive(container, allowReferences: true); @@ -146,9 +164,14 @@ void main() { } }); - test("If reference doesn't exist in objectList, an error is thrown when creating document", () { - final container = - Container(Parent("Bob", children: [Child._()..referenceURI = Uri(path: "/definitions/child")]), {}); + test( + "If reference doesn't exist in objectList, an error is thrown when creating document", + () { + final container = Container( + Parent("Bob", children: [ + Child._()..referenceURI = Uri(path: "/definitions/child") + ]), + {}); try { KeyedArchive.archive(container, allowReferences: true); @@ -160,7 +183,10 @@ void main() { test("Parent can contain reference to child in a list of objects", () { final container = Container( - Parent("Bob", children: [Child("Sally"), Child._()..referenceURI = Uri(path: "/definitions/child")]), + Parent("Bob", children: [ + Child("Sally"), + Child._()..referenceURI = Uri(path: "/definitions/child") + ]), {"child": Child("Fred")}); final out = KeyedArchive.archive(container, allowReferences: true); @@ -180,8 +206,10 @@ void main() { test("Parent can contain reference to child in a map of objects", () { final container = Container( - Parent("Bob", - childMap: {"sally": Child("Sally"), "ref": Child._()..referenceURI = Uri(path: "/definitions/child")}), + Parent("Bob", childMap: { + "sally": Child("Sally"), + "ref": Child._()..referenceURI = Uri(path: "/definitions/child") + }), {"child": Child("Fred")}); final out = KeyedArchive.archive(container, allowReferences: true); @@ -201,8 +229,14 @@ void main() { test("Cyclical references are resolved", () { final container = Container( - Parent("Bob", children: [Child("Sally"), Child._()..referenceURI = Uri(path: "/definitions/child")]), - {"child": Child("Fred", parent: Parent._()..referenceURI = Uri(path: "/root"))}); + Parent("Bob", children: [ + Child("Sally"), + Child._()..referenceURI = Uri(path: "/definitions/child") + ]), + { + "child": Child("Fred", + parent: Parent._()..referenceURI = Uri(path: "/root")) + }); final out = KeyedArchive.archive(container, allowReferences: true); final expected = { @@ -228,7 +262,7 @@ void main() { // and ensure that our data is valid json final washedData = json.decode(json.encode(out)); final doc = KeyedArchive.unarchive(washedData); - final decodedContainer = new Container._()..decode(doc); + final decodedContainer = Container._()..decode(doc); final reencodedArchive = KeyedArchive.archive(decodedContainer); expect(reencodedArchive, expected); }); @@ -270,14 +304,14 @@ class Container extends Coding { Container(this.root, this.definitions); - Parent root; - Map definitions; + Parent? root; + Map? definitions; @override void decode(KeyedArchive object) { super.decode(object); - root = object.decodeObject("root", () => Parent._()); + root = object.decodeObject("root", () => Parent._())!; definitions = object.decodeObjectMap("definitions", () => Child._()); } @@ -293,11 +327,11 @@ class Parent extends Coding { Parent(this.name, {this.child, this.children, this.childMap, this.things}); - String name; - Child child; - List children; - Map childMap; - List things; + String? name; + Child? child; + List? children; + Map? childMap; + List? things; @override void decode(KeyedArchive object) { @@ -324,8 +358,8 @@ class Child extends Coding { Child(this.name, {this.parent}); - String name; - Parent parent; + String? name; + Parent? parent; @override void decode(KeyedArchive object) { From 95906159607505c2f61c1b7a37449b320dd690a1 Mon Sep 17 00:00:00 2001 From: AnhQuan Nguyen Date: Sun, 28 Feb 2021 22:07:17 -0800 Subject: [PATCH 2/5] update travis --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 1517deb..36a3453 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: dart dart: - - stable + - beta jobs: include: From ede7b25847e6d3b1657214d4bbcf461903af3e6e Mon Sep 17 00:00:00 2001 From: AnhQuan Nguyen Date: Mon, 1 Mar 2021 01:27:09 -0800 Subject: [PATCH 3/5] nullable key for _getValue --- lib/src/keyed_archive.dart | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/src/keyed_archive.dart b/lib/src/keyed_archive.dart index ec2c9c2..fc79904 100644 --- a/lib/src/keyed_archive.dart +++ b/lib/src/keyed_archive.dart @@ -131,7 +131,7 @@ class KeyedArchive extends Object _map[key] = value; } - dynamic operator [](Object? key) => _getValue(key as String); + dynamic operator [](Object? key) => _getValue(key as String?); Iterable get keys => _map.keys; @@ -153,7 +153,7 @@ class KeyedArchive extends Object return out; } - dynamic _getValue(String key) { + dynamic _getValue(String? key) { if (_map.containsKey(key)) { return _map[key]; } @@ -228,7 +228,7 @@ class KeyedArchive extends Object /// If this object is a reference to another object (via [referenceURI]), this object's key-value /// pairs will be searched first. If [key] is not found, the referenced object's key-values pairs are searched. /// If no match is found, null is returned. - T? decode(String key) { + T? decode(String? key) { var v = _getValue(key); if (v == null) { return null; @@ -248,7 +248,7 @@ class KeyedArchive extends Object /// [inflate] must create an empty instance of [T]. The value associated with [key] /// must be a [KeyedArchive] (a [Map]). The values of the associated object are read into /// the empty instance of [T]. - T? decodeObject(String key, T inflate()) { + T? decodeObject(String? key, T inflate()) { final val = _getValue(key); if (val == null) { return null; @@ -287,7 +287,7 @@ class KeyedArchive extends Object /// must be a [KeyedArchive] (a [Map]), where each value is a [T]. /// For each key-value pair of the archived map, [inflate] is invoked and /// each value is decoded into the instance of [T]. - Map? decodeObjectMap(String key, T inflate()) { + Map? decodeObjectMap(String? key, T inflate()) { var v = _getValue(key); if (v == null) { return null; From fb9b98a145c714be4bd6314bef0c1ebb9854b08c Mon Sep 17 00:00:00 2001 From: Brett Sutton Date: Wed, 31 Mar 2021 14:54:59 +1100 Subject: [PATCH 4/5] Nnbd migration (#1) * formatted code and replaced deprecated list method. * null safety migration. All unit tests are passing. Updated verson no. to 2.0.0 to reflect breaking change (nnbd) and changed name to conduit-codable as codable can no longer be used. * renamed to conduit_codable as we can't publish as codable * Removed unused ! --- lib/cast.dart | 41 +++++++------- lib/codable.dart | 2 +- lib/src/codable.dart | 4 +- lib/src/coding.dart | 4 +- lib/src/keyed_archive.dart | 79 +++++++++++++------------- lib/src/list.dart | 8 +-- lib/src/resolver.dart | 12 +++- pubspec.yaml | 6 +- test/decode_test.dart | 110 ++++++++++++++++++------------------- test/encode_test.dart | 12 ++-- 10 files changed, 142 insertions(+), 136 deletions(-) diff --git a/lib/cast.dart b/lib/cast.dart index b54cbbe..204f21b 100644 --- a/lib/cast.dart +++ b/lib/cast.dart @@ -60,7 +60,7 @@ class IntCast extends Cast { core.int _cast(dynamic from, core.String context, dynamic key) => from is core.int ? from - : throw new FailedCast(context, key, "$from is not an int"); + : throw FailedCast(context, key, "$from is not an int"); } class DoubleCast extends Cast { @@ -68,7 +68,7 @@ class DoubleCast extends Cast { core.double _cast(dynamic from, core.String context, dynamic key) => from is core.double ? from - : throw new FailedCast(context, key, "$from is not an double"); + : throw FailedCast(context, key, "$from is not an double"); } class StringCast extends Cast { @@ -76,7 +76,7 @@ class StringCast extends Cast { core.String _cast(dynamic from, core.String context, dynamic key) => from is core.String ? from - : throw new FailedCast(context, key, "$from is not a String"); + : throw FailedCast(context, key, "$from is not a String"); } class BoolCast extends Cast { @@ -84,7 +84,7 @@ class BoolCast extends Cast { core.bool _cast(dynamic from, core.String context, dynamic key) => from is core.bool ? from - : throw new FailedCast(context, key, "$from is not a bool"); + : throw FailedCast(context, key, "$from is not a bool"); } class Map extends Cast> { @@ -102,7 +102,7 @@ class Map extends Cast> { } return result; } - return throw new FailedCast(context, key, "not a map"); + return throw FailedCast(context, key, "not a map"); } } @@ -113,12 +113,12 @@ class StringMap extends Cast> { dynamic from, core.String context, dynamic key) { if (from is core.Map) { var result = {}; - for (core.String key in from.keys) { + for (core.String key in from.keys as core.Iterable) { result[key] = _value._cast(from[key], "map entry", key); } return result; } - return throw new FailedCast(context, key, "not a map"); + return throw FailedCast(context, key, "not a map"); } } @@ -128,25 +128,28 @@ class List extends Cast> { core.List _cast(dynamic from, core.String context, dynamic key) { if (from is core.List) { var length = from.length; - var result = core.List.generate( - length, - (i) => - from[i] != null ? _entry._cast(from[i], "list entry", i) : null); - + var result = core.List.filled(length, null); + for (core.int i = 0; i < length; ++i) { + if (from[i] != null) { + result[i] = _entry._cast(from[i], "list entry", i); + } else { + result[i] = null; + } + } return result; } - return throw new FailedCast(context, key, "not a list"); + return throw FailedCast(context, key, "not a list"); } } -class Keyed extends Cast> { +class Keyed extends Cast> { Iterable get keys => _map.keys; final core.Map> _map; const Keyed(core.Map> map) : _map = map; - core.Map _cast(dynamic from, core.String context, dynamic key) { - core.Map result = {}; + core.Map _cast(dynamic from, core.String context, dynamic key) { + core.Map result = {}; if (from is core.Map) { - for (K key in from.keys) { + for (K key in from.keys as core.Iterable) { if (_map.containsKey(key)) { result[key] = _map[key]!._cast(from[key], "map entry", key); } else { @@ -155,7 +158,7 @@ class Keyed extends Cast> { } return result; } - return throw new FailedCast(context, key, "not a map"); + return throw FailedCast(context, key, "not a map"); } } @@ -191,7 +194,7 @@ class Future extends Cast> { if (from is async.Future) { return from.then(_value.cast); } - return throw new FailedCast(context, key, "not a Future"); + return throw FailedCast(context, key, "not a Future"); } } diff --git a/lib/codable.dart b/lib/codable.dart index 6865e7e..aa086d9 100644 --- a/lib/codable.dart +++ b/lib/codable.dart @@ -5,4 +5,4 @@ library codable; export 'src/coding.dart'; export 'src/list.dart'; -export 'src/keyed_archive.dart'; \ No newline at end of file +export 'src/keyed_archive.dart'; diff --git a/lib/src/codable.dart b/lib/src/codable.dart index f341daa..34a1f1a 100644 --- a/lib/src/codable.dart +++ b/lib/src/codable.dart @@ -1,5 +1,5 @@ -import 'package:codable/src/resolver.dart'; +import 'package:conduit_codable/src/resolver.dart'; abstract class Referencable { void resolveOrThrow(ReferenceResolver resolver); -} \ No newline at end of file +} diff --git a/lib/src/coding.dart b/lib/src/coding.dart index e1db004..f781027 100644 --- a/lib/src/coding.dart +++ b/lib/src/coding.dart @@ -1,6 +1,6 @@ -import 'package:codable/src/keyed_archive.dart'; +import 'package:conduit_codable/src/keyed_archive.dart'; import 'package:meta/meta.dart'; -import 'package:codable/cast.dart' as cast; +import 'package:conduit_codable/cast.dart' as cast; /// A base class for encodable and decodable objects. /// diff --git a/lib/src/keyed_archive.dart b/lib/src/keyed_archive.dart index fc79904..258a74f 100644 --- a/lib/src/keyed_archive.dart +++ b/lib/src/keyed_archive.dart @@ -1,9 +1,9 @@ import 'dart:collection'; -import 'package:codable/src/codable.dart'; -import 'package:codable/src/coding.dart'; -import 'package:codable/cast.dart' as cast; -import 'package:codable/src/list.dart'; -import 'package:codable/src/resolver.dart'; +import 'package:conduit_codable/src/codable.dart'; +import 'package:conduit_codable/src/coding.dart'; +import 'package:conduit_codable/cast.dart' as cast; +import 'package:conduit_codable/src/list.dart'; +import 'package:conduit_codable/src/resolver.dart'; /// A container for a dynamic data object that can be decoded into [Coding] objects. /// @@ -36,10 +36,10 @@ class KeyedArchive extends Object /// If [allowReferences] is true, JSON Schema references will be traversed and decoded objects /// will contain values from the referenced object. This flag defaults to false. static KeyedArchive unarchive(Map data, - {bool allowReferences: false}) { - final archive = new KeyedArchive(data); + {bool allowReferences = false}) { + final archive = KeyedArchive(data); if (allowReferences) { - archive.resolveOrThrow(new ReferenceResolver(archive)); + archive.resolveOrThrow(ReferenceResolver(archive)); } return archive; } @@ -52,17 +52,17 @@ class KeyedArchive extends Object /// /// If [allowReferences] is true, JSON Schema references in the emitted document will be validated. /// Defaults to false. - static Map archive(Coding root, - {bool allowReferences: false}) { - final archive = new KeyedArchive({}); + static Map archive(Coding root, + {bool allowReferences = false}) { + final archive = KeyedArchive({}); root.encode(archive); if (allowReferences) { - archive.resolveOrThrow(new ReferenceResolver(archive)); + archive.resolveOrThrow(ReferenceResolver(archive)); } return archive.toPrimitive(); } - KeyedArchive._empty(); + KeyedArchive._empty() : _map = Map(); /// Use [unarchive] instead. KeyedArchive(this._map) { @@ -86,7 +86,7 @@ class KeyedArchive extends Object /// Uri? referenceURI; - late Map _map; + Map _map; Coding? _inflated; KeyedArchive? _objectReference; @@ -118,7 +118,7 @@ class KeyedArchive extends Object return; } - final caster = new cast.Keyed(schema); + final caster = cast.Keyed(schema); _map = caster.cast(_map); if (_objectReference != null) { @@ -127,11 +127,11 @@ class KeyedArchive extends Object } } - operator []=(String key, dynamic value) { + operator []=(String key, dynamic? value) { _map[key] = value; } - dynamic operator [](Object? key) => _getValue(key as String?); + dynamic? operator [](Object? key) => _getValue(key as String); Iterable get keys => _map.keys; @@ -139,8 +139,8 @@ class KeyedArchive extends Object dynamic remove(Object? key) => _map.remove(key); - Map toPrimitive() { - final out = {}; + Map toPrimitive() { + final out = {}; _map.forEach((key, val) { if (val is KeyedArchive) { out[key] = val.toPrimitive(); @@ -167,12 +167,12 @@ class KeyedArchive extends Object keys.forEach((key) { final val = _map[key]; if (val is Map) { - _map[key] = new KeyedArchive(caster.cast(val)); + _map[key] = KeyedArchive(caster.cast(val)); } else if (val is List) { - _map[key] = new ListArchive.from(val); + _map[key] = ListArchive.from(val); } else if (key == r"$ref") { if (val is Map) { - _objectReference = val as KeyedArchive; + _objectReference = val as KeyedArchive?; } else { referenceURI = Uri.parse(Uri.parse(val).fragment); } @@ -188,7 +188,7 @@ class KeyedArchive extends Object if (referenceURI != null) { _objectReference = coder.resolve(referenceURI!); if (_objectReference == null) { - throw new ArgumentError( + throw ArgumentError( "Invalid document. Reference '#${referenceURI!.path}' does not exist in document."); } } @@ -204,19 +204,17 @@ class KeyedArchive extends Object /* decode */ - T? _decodedObject(KeyedArchive? raw, T inflate()) { + T? _decodedObject(KeyedArchive? raw, T inflate()) { if (raw == null) { return null; } if (raw._inflated == null) { raw._inflated = inflate(); - if (raw._inflated != null) { - raw._inflated!.decode(raw); - } + raw._inflated!.decode(raw); } - return raw._inflated as T; + return raw._inflated as T?; } /// Returns the object associated by [key]. @@ -228,7 +226,7 @@ class KeyedArchive extends Object /// If this object is a reference to another object (via [referenceURI]), this object's key-value /// pairs will be searched first. If [key] is not found, the referenced object's key-values pairs are searched. /// If no match is found, null is returned. - T? decode(String? key) { + T? decode(String key) { var v = _getValue(key); if (v == null) { return null; @@ -248,14 +246,14 @@ class KeyedArchive extends Object /// [inflate] must create an empty instance of [T]. The value associated with [key] /// must be a [KeyedArchive] (a [Map]). The values of the associated object are read into /// the empty instance of [T]. - T? decodeObject(String? key, T inflate()) { + T? decodeObject(String key, T inflate()) { final val = _getValue(key); if (val == null) { return null; } if (val is! KeyedArchive) { - throw new ArgumentError( + throw ArgumentError( "Cannot decode key '$key' into '$T', because the value is not a Map. Actual value: '$val'."); } @@ -268,17 +266,17 @@ class KeyedArchive extends Object /// must be a [ListArchive] (a [List] of [Map]). For each element of the archived list, /// [inflate] is invoked and each object in the archived list is decoded into /// the instance of [T]. - List? decodeObjects(String key, T inflate()) { + List? decodeObjects(String key, T? inflate()) { var val = _getValue(key); if (val == null) { return null; } if (val is! List) { - throw new ArgumentError( + throw ArgumentError( "Cannot decode key '$key' as 'List<$T>', because value is not a List. Actual value: '$val'."); } - return val.map((v) => _decodedObject(v, inflate)).toList(); + return val.map((v) => _decodedObject(v, inflate)).toList().cast(); } /// Returns a map of [T]s associated with [key]. @@ -287,14 +285,14 @@ class KeyedArchive extends Object /// must be a [KeyedArchive] (a [Map]), where each value is a [T]. /// For each key-value pair of the archived map, [inflate] is invoked and /// each value is decoded into the instance of [T]. - Map? decodeObjectMap(String? key, T inflate()) { + Map? decodeObjectMap(String key, T inflate()) { var v = _getValue(key); if (v == null) { return null; } if (v is! Map) { - throw new ArgumentError( + throw ArgumentError( "Cannot decode key '$key' as 'Map', because value is not a Map. Actual value: '$v'."); } @@ -304,7 +302,7 @@ class KeyedArchive extends Object /* encode */ - Map? _encodedObject(Coding? object) { + Map? _encodedObject(Coding? object) { if (object == null) { return null; } @@ -314,7 +312,7 @@ class KeyedArchive extends Object // they are currently not being emitted. the solution is probably tricky. // letting encode run as normal would stack overflow when there is a cyclic // reference between this object and another. - var json = new KeyedArchive._empty() + var json = KeyedArchive._empty() .._map = {} ..referenceURI = object.referenceURI; if (json.referenceURI != null) { @@ -367,15 +365,14 @@ class KeyedArchive extends Object return; } - _map[key] = - new ListArchive.from(value.map((v) => _encodedObject(v)).toList()); + _map[key] = ListArchive.from(value.map((v) => _encodedObject(v)).toList()); } /// Encodes map of [Coding] objects into this object for [key]. /// /// This invokes [Coding.encode] on each value in [value] and adds the map of objects /// to this archive for the key [key]. - void encodeObjectMap(String key, Map? value) { + void encodeObjectMap(String key, Map? value) { if (value == null) { return; } diff --git a/lib/src/list.dart b/lib/src/list.dart index 8ec0f14..21b5f92 100644 --- a/lib/src/list.dart +++ b/lib/src/list.dart @@ -1,9 +1,9 @@ import 'dart:collection'; -import 'package:codable/src/codable.dart'; -import 'package:codable/src/coding.dart'; -import 'package:codable/src/keyed_archive.dart'; -import 'package:codable/src/resolver.dart'; +import 'package:conduit_codable/src/codable.dart'; +import 'package:conduit_codable/src/coding.dart'; +import 'package:conduit_codable/src/keyed_archive.dart'; +import 'package:conduit_codable/src/resolver.dart'; /// A list of values in a [KeyedArchive]. /// diff --git a/lib/src/resolver.dart b/lib/src/resolver.dart index f5f0dc1..4ab2194 100644 --- a/lib/src/resolver.dart +++ b/lib/src/resolver.dart @@ -1,4 +1,4 @@ -import 'package:codable/src/keyed_archive.dart'; +import 'package:conduit_codable/src/keyed_archive.dart'; class ReferenceResolver { ReferenceResolver(this.document); @@ -6,8 +6,14 @@ class ReferenceResolver { final KeyedArchive document; KeyedArchive? resolve(Uri ref) { - return ref.pathSegments.fold(document, (objectPtr, pathSegment) { - return (objectPtr as Map)[pathSegment]; + var folded = ref.pathSegments.fold(document, + (KeyedArchive? objectPtr, pathSegment) { + if (objectPtr != null) + return objectPtr[pathSegment]; // as Map; + else + return null; }); + + return folded; } } diff --git a/pubspec.yaml b/pubspec.yaml index 6f5bac7..91a4fe5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,11 +1,11 @@ -name: codable +name: conduit_codable description: A serialization library for converting dynamic, structured data (JSON, YAML) into Dart types. -version: 1.0.0 +version: 1.0.0-b1 homepage: https://github.com/stablekernel/dart-codable author: stable|kernel environment: - sdk: '>=2.12.0-0 <3.0.0' + sdk: '>=2.12.0 <3.0.0' dependencies: diff --git a/test/decode_test.dart b/test/decode_test.dart index d47a75b..9d012bd 100644 --- a/test/decode_test.dart +++ b/test/decode_test.dart @@ -1,14 +1,14 @@ import 'dart:convert'; -import 'package:codable/codable.dart'; +import 'package:conduit_codable/codable.dart'; import 'package:test/test.dart'; -import 'package:codable/cast.dart' as cast; +import 'package:conduit_codable/cast.dart' as cast; void main() { group("Primitive decode", () { test("Can decode primitive type", () { final archive = getJSONArchive({"key": 2}); - int val = archive!.decode("key"); + int? val = archive.decode("key"); expect(val, 2); }); @@ -16,7 +16,7 @@ void main() { final archive = getJSONArchive({ "key": [1, "2"] }); - List l = archive!.decode("key"); + List? l = archive.decode("key"); expect(l, [1, "2"]); }); @@ -24,32 +24,32 @@ void main() { final archive = getJSONArchive({ "key": {"key": "val"} }); - Map d = archive!.decode("key"); + KeyedArchive? d = archive.decode("key"); expect(d, {"key": "val"}); }); test("Can decode URI", () { final archive = getJSONArchive({"key": "https://host.com"}); - Uri d = Uri.parse(archive!.decode("key")); - expect(d.host, "host.com"); + Uri? d = archive.decode("key"); + expect(d!.host, "host.com"); }); test("Can decode DateTime", () { - final date = new DateTime.now(); + final date = DateTime.now(); final archive = getJSONArchive({"key": date.toIso8601String()}); - DateTime d = DateTime.parse(archive!.decode("key")); - expect(d.isAtSameMomentAs(date), true); + DateTime? d = archive.decode("key"); + expect(d!.isAtSameMomentAs(date), true); }); test("If value is null, return null from decode", () { final archive = getJSONArchive({"key": null}); - int? val = archive!.decode("key"); + int? val = archive.decode("key"); expect(val, isNull); }); test("If archive does not contain key, return null from decode", () { final archive = getJSONArchive({}); - int? val = archive!.decode("key"); + int? val = archive.decode("key"); expect(val, isNull); }); }); @@ -59,8 +59,8 @@ void main() { final archive = getJSONArchive({ "key": {"key": "val"} }); - archive!.castValues({"key": cast.Map(cast.String, cast.String)}); - Map d = archive.decode("key"); + archive.castValues({"key": cast.Map(cast.String, cast.String)}); + Map? d = archive.decode("key"); expect(d, {"key": "val"}); }); @@ -70,7 +70,7 @@ void main() { "key": ["val"] } }); - archive! + archive .castValues({"key": cast.Map(cast.String, cast.List(cast.String))}); Map>? d = archive.decode("key"); expect(d, { @@ -84,7 +84,7 @@ void main() { "key": [null, null] } }); - archive! + archive .castValues({"key": cast.Map(cast.String, cast.List(cast.String))}); Map>? d = archive.decode("key"); expect(d, { @@ -100,11 +100,11 @@ void main() { } } }); - archive!.castValues({ + archive.castValues({ "key": cast.Map(cast.String, cast.Map(cast.String, cast.List(cast.String))) }); - Map>> d = archive.decode("key"); + Map>>? d = archive.decode("key"); expect(d, { "key": { "key": ["val", null] @@ -118,8 +118,8 @@ void main() { final archive = getJSONArchive({ "key": ["val", null] }); - archive!.castValues({"key": cast.List(cast.String)}); - List d = archive.decode("key"); + archive.castValues({"key": cast.List(cast.String)}); + List? d = archive.decode("key"); expect(d, ["val", null]); }); @@ -132,9 +132,9 @@ void main() { null ] }); - archive!.castValues( + archive.castValues( {"key": cast.List(cast.Map(cast.String, cast.List(cast.String)))}); - List>?> d = archive.decode("key"); + List>?>? d = archive.decode("key"); expect(d, [ { "key": ["val", null] @@ -149,7 +149,7 @@ void main() { final archive = getJSONArchive({ "key": {"name": "Bob"} }); - Parent p = archive!.decodeObject("key", () => Parent())!; + Parent p = archive.decodeObject("key", () => Parent())!; expect(p.name, "Bob"); expect(p.child, isNull); expect(p.children, isNull); @@ -163,7 +163,7 @@ void main() { ] }); try { - archive!.decodeObject("key", () => Parent()); + archive.decodeObject("key", () => Parent()); fail('unreachable'); } on ArgumentError {} }); @@ -176,8 +176,8 @@ void main() { {"name": "Sally"} ] }); - List p = archive!.decodeObjects("key", () => Parent())!; - expect(p[0]!.name, "Bob"); + List? p = archive.decodeObjects("key", () => Parent()); + expect(p![0]!.name, "Bob"); expect(p[1], isNull); expect(p[2]!.name, "Sally"); }); @@ -189,7 +189,7 @@ void main() { "key": {"name": "Bob"} }); try { - archive!.decodeObjects("key", () => Parent()); + archive.decodeObjects("key", () => Parent()); fail('unreachable'); } on ArgumentError {} }); @@ -204,7 +204,7 @@ void main() { ] }); try { - archive!.decodeObjects("key", () => Parent()); + archive.decodeObjects("key", () => Parent()); fail('unreachable'); } on TypeError {} }); @@ -217,8 +217,8 @@ void main() { } }); - final map = archive!.decodeObjectMap("key", () => Parent()); - expect(map!.length, 2); + final map = archive.decodeObjectMap("key", () => Parent())!; + expect(map.length, 2); expect(map["1"]!.name, "Bob"); expect(map["2"], isNull); }); @@ -227,7 +227,7 @@ void main() { () { final archive = getJSONArchive({"key": []}); try { - archive!.decodeObjectMap("key", () => Parent()); + archive.decodeObjectMap("key", () => Parent()); fail('unreachable'); } on ArgumentError {} }); @@ -239,7 +239,7 @@ void main() { "key": {"1": "2"} }); try { - archive!.decodeObjectMap("key", () => Parent()); + archive.decodeObjectMap("key", () => Parent()); fail('unreachable'); } on TypeError {} }); @@ -254,8 +254,8 @@ void main() { } }); - final o = archive!.decodeObject("key", () => Parent()); - expect(o!.name, "Bob"); + final o = archive.decodeObject("key", () => Parent())!; + expect(o.name, "Bob"); expect(o.child!.name, "Sally"); expect(o.childMap, isNull); expect(o.children, isNull); @@ -271,12 +271,12 @@ void main() { } }); - final o = archive!.decodeObject("key", () => Parent()); - expect(o!.name, "Bob"); + final o = archive.decodeObject("key", () => Parent())!; + expect(o.name, "Bob"); expect(o.child, isNull); expect(o.childMap, isNull); expect(o.children!.length, 1); - expect(o.children!.first!.name, "Sally"); + expect(o.children?.first?.name, "Sally"); }); test("Can decode map of nested objects", () { @@ -289,8 +289,8 @@ void main() { } }); - final o = archive!.decodeObject("key", () => Parent()); - expect(o!.name, "Bob"); + final o = archive.decodeObject("key", () => Parent())!; + expect(o.name, "Bob"); expect(o.children, isNull); expect(o.child, isNull); expect(o.childMap!.length, 1); @@ -308,8 +308,8 @@ void main() { } }, allowReferences: true); - final p = archive!.decodeObject("parent", () => Parent()); - expect(p!.name, "Bob"); + final p = archive.decodeObject("parent", () => Parent())!; + expect(p.name, "Bob"); expect(p.child!.name, "Sally"); expect(p.child!.parent, isNull); }); @@ -342,10 +342,10 @@ void main() { } }, allowReferences: true); - final p = archive!.decodeObject("parent", () => Parent()); - expect(p!.name, "Bob"); - expect(p.children!.first!.name, "Sally"); - expect(p.children!.last!.name, "fred"); + final p = archive.decodeObject("parent", () => Parent())!; + expect(p.name, "Bob"); + expect(p.children?.first?.name, "Sally"); + expect(p.children?.last?.name, "fred"); }); test("Cyclical references are resolved", () { @@ -363,13 +363,13 @@ void main() { } }, allowReferences: true); - final p = archive!.decodeObject("parent", () => Parent()); - expect(p!.name, "Bob"); - expect(p.children!.first!.name, "Sally"); - expect(p.children!.first!.parent!.name, "Bob"); - expect(p.children!.last!.name, "fred"); + final p = archive.decodeObject("parent", () => Parent())!; + expect(p.name, "Bob"); + expect(p.children?.first?.name, "Sally"); + expect(p.children?.first?.parent!.name, "Bob"); + expect(p.children?.last?.name, "fred"); - expect(p.hashCode, isNot(p.children!.first!.parent.hashCode)); + expect(p.hashCode, isNot(p.children?.first?.parent.hashCode)); }); test("Can override castMap to coerce values", () { @@ -379,14 +379,14 @@ void main() { "things": ["value"] } }); - final p = archive!.decodeObject("key", () => Parent()); - expect(p!.things, ["value"]); + final p = archive.decodeObject("key", () => Parent())!; + expect(p.things, ["value"]); }); }); } /// Strips type info from data -KeyedArchive? getJSONArchive(dynamic data, {bool allowReferences: false}) { +KeyedArchive getJSONArchive(dynamic data, {bool allowReferences = false}) { return KeyedArchive.unarchive(json.decode(json.encode(data)), allowReferences: allowReferences); } @@ -395,7 +395,7 @@ class Parent extends Coding { String? name; Child? child; List? children; - Map? childMap; + Map? childMap; List? things; @override diff --git a/test/encode_test.dart b/test/encode_test.dart index 62e1790..8a07e11 100644 --- a/test/encode_test.dart +++ b/test/encode_test.dart @@ -1,6 +1,6 @@ import 'dart:convert'; -import 'package:codable/codable.dart'; +import 'package:conduit_codable/codable.dart'; import 'package:test/test.dart'; void main() { @@ -44,10 +44,10 @@ void main() { test("Can encode DateTime", () { final out = encode((obj) { - obj.encode("key", new DateTime(2000)); + obj.encode("key", DateTime(2000)); }); - expect(out, {"key": new DateTime(2000).toIso8601String()}); + expect(out, {"key": DateTime(2000).toIso8601String()}); }); test("If value is null, do not include key", () { @@ -293,8 +293,8 @@ void main() { }); } -Map encode(void encoder(KeyedArchive object)) { - final archive = new KeyedArchive({}); +Map? encode(void encoder(KeyedArchive object)) { + final archive = KeyedArchive({}); encoder(archive); return json.decode(json.encode(archive)); } @@ -331,7 +331,7 @@ class Parent extends Coding { Child? child; List? children; Map? childMap; - List? things; + List? things; @override void decode(KeyedArchive object) { From 73b3e74c52525e0f369270cb314c1f6e53c17d05 Mon Sep 17 00:00:00 2001 From: AnhQuan Nguyen Date: Tue, 30 Mar 2021 20:56:39 -0700 Subject: [PATCH 5/5] change hompage stuff --- pubspec.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pubspec.yaml b/pubspec.yaml index 91a4fe5..60d755f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,8 +1,7 @@ name: conduit_codable description: A serialization library for converting dynamic, structured data (JSON, YAML) into Dart types. version: 1.0.0-b1 -homepage: https://github.com/stablekernel/dart-codable -author: stable|kernel +homepage: https://github.com/conduit-dart/dart-codable environment: sdk: '>=2.12.0 <3.0.0'