From 4f4603a04bee20d93a7f9739e07ae1019f3d9371 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?The=20=D0=9A=D0=BE=D0=BD=D1=8C?= Date: Fri, 11 Dec 2020 18:20:57 -0800 Subject: [PATCH] v3: null safety (#8) --- .github/workflows/dart.yml | 8 ++++---- CHANGELOG.md | 5 ++++- analysis_options.yaml | 2 +- lib/src/ast/tokenize.dart | 2 +- lib/src/json_path.dart | 7 ++++--- lib/src/parsing_state.dart | 15 ++++++++------- lib/src/{selector => }/quote.dart | 0 lib/src/selector/filter.dart | 2 +- lib/src/selector/joint.dart | 2 +- lib/src/selector/list_union.dart | 4 ++-- lib/src/selector/list_wildcard.dart | 4 ++-- lib/src/selector/object_union.dart | 4 ++-- lib/src/selector/object_wildcard.dart | 4 ++-- lib/src/selector/recursive.dart | 4 ++-- lib/src/selector/root.dart | 2 +- lib/src/selector/selector.dart | 4 ++-- lib/src/selector/slice.dart | 10 +++++----- pubspec.yaml | 13 +++++++------ 18 files changed, 49 insertions(+), 43 deletions(-) rename lib/src/{selector => }/quote.dart (100%) diff --git a/.github/workflows/dart.yml b/.github/workflows/dart.yml index 8b975e0..65727ed 100644 --- a/.github/workflows/dart.yml +++ b/.github/workflows/dart.yml @@ -12,15 +12,15 @@ jobs: runs-on: ubuntu-latest container: - image: google/dart:latest + image: google/dart:beta steps: - uses: actions/checkout@v2 - name: Install dependencies - run: pub get + run: dart pub get - name: Format run: dartfmt --dry-run --set-exit-if-changed lib test example - name: Analyzer - run: dartanalyzer --fatal-infos --fatal-warnings lib test example + run: dart analyze --fatal-infos --fatal-warnings - name: Tests - run: pub run test_coverage --no-badge --print-test-output --min-coverage 100 + run: dart run test_coverage --no-badge --print-test-output --min-coverage 100 diff --git a/CHANGELOG.md b/CHANGELOG.md index 3bc1a9f..80c2221 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,7 @@ ## [Unreleased] +### Changed +- Prepared for null safety + ## [0.2.0] - 2020-09-07 ### Added - Ability to create arrays and set adjacent indices @@ -83,4 +86,4 @@ Previously, no modification would be made and no errors/exceptions thrown. [0.0.0+dev.4]: https://github.com/f3ath/jessie/compare/0.0.0+dev.3...0.0.0+dev.4 [0.0.0+dev.3]: https://github.com/f3ath/jessie/compare/0.0.0+dev.2...0.0.0+dev.3 [0.0.0+dev.2]: https://github.com/f3ath/jessie/compare/0.0.0+dev.1...0.0.0+dev.2 -[0.0.0+dev.1]: https://github.com/f3ath/jessie/compare/0.0.0+dev.0...0.0.0+dev.1 +[0.0.0+dev.1]: https://github.com/f3ath/jessie/compare/0.0.0+dev.0...0.0.0+dev.1 \ No newline at end of file diff --git a/analysis_options.yaml b/analysis_options.yaml index 7783dc8..d9406f3 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -2,4 +2,4 @@ include: package:pedantic/analysis_options.yaml linter: rules: - sort_constructors_first - - sort_unnamed_constructors_first \ No newline at end of file + - sort_unnamed_constructors_first diff --git a/lib/src/ast/tokenize.dart b/lib/src/ast/tokenize.dart index d819535..25e10f2 100644 --- a/lib/src/ast/tokenize.dart +++ b/lib/src/ast/tokenize.dart @@ -1,5 +1,5 @@ Iterable tokenize(String expr) => - _tokens.allMatches(expr).map((match) => match.group(0)); + _tokens.allMatches(expr).map((match) => match.group(0).toString()); final _tokens = RegExp([ r'\$', // root diff --git a/lib/src/json_path.dart b/lib/src/json_path.dart index 03bbbd6..9175d81 100644 --- a/lib/src/json_path.dart +++ b/lib/src/json_path.dart @@ -15,11 +15,12 @@ class JsonPath { /// in the [expression]. /// /// Throws [FormatException] if the [expression] can not be parsed. - factory JsonPath(String expression, {Map filter}) { + factory JsonPath(String expression, + {Map filter = const {}}) { if (expression.isEmpty) throw FormatException('Empty expression'); ParsingState state = Ready(RootSelector()); AST(tokenize(expression)).nodes.forEach((node) { - state = state.process(node, filter ?? {}); + state = state.process(node, filter); }); return JsonPath._(state.selector); } @@ -33,7 +34,7 @@ class JsonPath { _selector.read([JsonPathMatch(json, '')]); /// Returns a copy of [json] with all matching values replaced with [value]. - dynamic set(dynamic json, dynamic value) => _selector.set(json, (_) => value); + dynamic set(json, value) => _selector.set(json, (_) => value); @override String toString() => _selector.expression(); diff --git a/lib/src/parsing_state.dart b/lib/src/parsing_state.dart index a37a2ab..af794bf 100644 --- a/lib/src/parsing_state.dart +++ b/lib/src/parsing_state.dart @@ -20,7 +20,7 @@ abstract class ParsingState { /// Ready to process the next node class Ready implements ParsingState { - Ready(this.selector); + const Ready(this.selector); @override final Selector selector; @@ -63,10 +63,11 @@ class Ready implements ParsingState { Filter _filter(List nodes, Map filters) { final name = nodes[1].value; - if (!filters.containsKey(name)) { + final filter = filters[name]; + if (filter == null) { throw FormatException('Filter not found: "${name}"'); } - return Filter(name, filters[name]); + return Filter(name, filter); } bool _isFilter(List nodes) => nodes.first.value == '?'; @@ -83,9 +84,9 @@ class Ready implements ParsingState { } Slice _slice(List nodes) { - int first; - int last; - int step; + int? first; + int? last; + int? step; var colons = 0; nodes.forEach((node) { if (node.value == ':') { @@ -107,7 +108,7 @@ class Ready implements ParsingState { } class AwaitingField implements ParsingState { - AwaitingField(this.selector); + const AwaitingField(this.selector); @override final Selector selector; diff --git a/lib/src/selector/quote.dart b/lib/src/quote.dart similarity index 100% rename from lib/src/selector/quote.dart rename to lib/src/quote.dart diff --git a/lib/src/selector/filter.dart b/lib/src/selector/filter.dart index 238371a..b6671b6 100644 --- a/lib/src/selector/filter.dart +++ b/lib/src/selector/filter.dart @@ -19,6 +19,6 @@ class Filter with SelectorMixin implements Selector { String expression() => '[?$name]'; @override - dynamic set(dynamic json, Replacement replacement) => + dynamic set(json, Replacement replacement) => isApplicable(json) ? replacement(json) : json; } diff --git a/lib/src/selector/joint.dart b/lib/src/selector/joint.dart index 9102b63..427c400 100644 --- a/lib/src/selector/joint.dart +++ b/lib/src/selector/joint.dart @@ -34,6 +34,6 @@ class Joint with SelectorMixin implements Selector { right.expression(); @override - dynamic set(dynamic json, Replacement replacement) => + dynamic set(json, Replacement replacement) => left.set(json, (_) => right.set(_, replacement)); } diff --git a/lib/src/selector/list_union.dart b/lib/src/selector/list_union.dart index 7ca6c0a..af3664c 100644 --- a/lib/src/selector/list_union.dart +++ b/lib/src/selector/list_union.dart @@ -9,14 +9,14 @@ class ListUnion with SelectorMixin implements Selector { @override Iterable read(Iterable matches) => matches - .map((r) => (r.value is List) ? _map(r.value, r.path) : []) + .map((r) => (r.value is List) ? _map(r.value, r.path) : []) .expand((_) => _); @override String expression() => '[${_indices.join(',')}]'; @override - dynamic set(dynamic json, Replacement replacement) { + dynamic set(json, Replacement replacement) { json ??= []; if (json is List) { json = [...json]; diff --git a/lib/src/selector/list_wildcard.dart b/lib/src/selector/list_wildcard.dart index 4d72282..9abc2c4 100644 --- a/lib/src/selector/list_wildcard.dart +++ b/lib/src/selector/list_wildcard.dart @@ -13,10 +13,10 @@ class ListWildcard with SelectorMixin implements Selector { String expression() => '[*]'; @override - dynamic set(dynamic json, Replacement replacement) => + dynamic set(json, Replacement replacement) => (json is List) ? json.map(replacement).toList() : json; - Iterable _wrap(List val, String path) sync* { + static Iterable _wrap(List val, String path) sync* { for (var i = 0; i < val.length; i++) { yield JsonPathMatch(val[i], path + '[$i]'); } diff --git a/lib/src/selector/object_union.dart b/lib/src/selector/object_union.dart index 774f20f..32f91a1 100644 --- a/lib/src/selector/object_union.dart +++ b/lib/src/selector/object_union.dart @@ -1,5 +1,5 @@ import 'package:json_path/src/json_path_match.dart'; -import 'package:json_path/src/selector/quote.dart'; +import 'package:json_path/src/quote.dart'; import 'package:json_path/src/selector/selector.dart'; import 'package:json_path/src/selector/selector_mixin.dart'; @@ -18,7 +18,7 @@ class ObjectUnion with SelectorMixin implements Selector { String expression() => '[${_keys.map((k) => Quote(k)).join(',')}]'; @override - dynamic set(dynamic json, Replacement replacement) { + dynamic set(json, Replacement replacement) { if (json == null) return _patch({}, replacement); if (json is Map) return {...json, ..._patch(json, replacement)}; return json; diff --git a/lib/src/selector/object_wildcard.dart b/lib/src/selector/object_wildcard.dart index f665cee..08eb1a8 100644 --- a/lib/src/selector/object_wildcard.dart +++ b/lib/src/selector/object_wildcard.dart @@ -1,5 +1,5 @@ import 'package:json_path/src/json_path_match.dart'; -import 'package:json_path/src/selector/quote.dart'; +import 'package:json_path/src/quote.dart'; import 'package:json_path/src/selector/selector.dart'; import 'package:json_path/src/selector/selector_mixin.dart'; @@ -24,7 +24,7 @@ class ObjectWildcard with SelectorMixin implements Selector { .map((e) => JsonPathMatch(e.value, path + '[${e.key}]')); @override - dynamic set(dynamic json, Replacement replacement) => (json is Map) + dynamic set(json, Replacement replacement) => (json is Map) ? json.map((key, value) => MapEntry(key, replacement(value))) : json; } diff --git a/lib/src/selector/recursive.dart b/lib/src/selector/recursive.dart index 029a9e2..d221576 100644 --- a/lib/src/selector/recursive.dart +++ b/lib/src/selector/recursive.dart @@ -1,5 +1,5 @@ import 'package:json_path/src/json_path_match.dart'; -import 'package:json_path/src/selector/quote.dart'; +import 'package:json_path/src/quote.dart'; import 'package:json_path/src/selector/selector.dart'; import 'package:json_path/src/selector/selector_mixin.dart'; @@ -12,7 +12,7 @@ class Recursive with SelectorMixin implements Selector { String expression() => '..'; @override - dynamic set(dynamic json, Replacement replacement) { + dynamic set(json, Replacement replacement) { if (json is Map) { return _replaceInMap(json, replacement); } diff --git a/lib/src/selector/root.dart b/lib/src/selector/root.dart index 0e9f32a..683db03 100644 --- a/lib/src/selector/root.dart +++ b/lib/src/selector/root.dart @@ -13,5 +13,5 @@ class RootSelector with SelectorMixin implements Selector { String expression() => r'$'; @override - dynamic set(dynamic json, Replacement replacement) => replacement(json); + dynamic set(json, Replacement replacement) => replacement(json); } diff --git a/lib/src/selector/selector.dart b/lib/src/selector/selector.dart index 30fa731..ec74a15 100644 --- a/lib/src/selector/selector.dart +++ b/lib/src/selector/selector.dart @@ -12,7 +12,7 @@ abstract class Selector { Selector then(Selector other); /// Returns a copy of [json] with all selected values modified using [replacement] function. - dynamic set(dynamic json, Replacement replacement); + dynamic set(json, Replacement replacement); } -typedef Replacement = dynamic Function(dynamic value); +typedef Replacement = V Function(T value); diff --git a/lib/src/selector/slice.dart b/lib/src/selector/slice.dart index 984da9a..ed47c4f 100644 --- a/lib/src/selector/slice.dart +++ b/lib/src/selector/slice.dart @@ -5,7 +5,7 @@ import 'package:json_path/src/selector/selector.dart'; import 'package:json_path/src/selector/selector_mixin.dart'; class Slice with SelectorMixin implements Selector { - Slice({int first, this.last, int step}) + Slice({int? first, this.last, int? step}) : first = first ?? 0, step = step ?? 1; @@ -18,7 +18,7 @@ class Slice with SelectorMixin implements Selector { final int first; - final int last; + final int? last; final int step; @@ -33,7 +33,7 @@ class Slice with SelectorMixin implements Selector { '[${first == 0 ? '' : first}:${last ?? ''}${step != 1 ? ':$step' : ''}]'; @override - dynamic set(dynamic json, Replacement replacement) { + dynamic set(json, Replacement replacement) { if (json is List) { final indices = _indices(json); if (indices.isNotEmpty) { @@ -57,7 +57,7 @@ class Slice with SelectorMixin implements Selector { int _actualLast(int len) { if (last == null) return len; - if (last < 0) return min(len, len + last); - return min(len, last); + if (last! < 0) return min(len, len + last!); + return min(len, last!); } } diff --git a/pubspec.yaml b/pubspec.yaml index 7435dcb..d60ee92 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,12 +1,13 @@ name: json_path -version: 0.2.0 +version: 0.3.0-nullsafety description: Implementation of JSONPath expressions like "$.store.book[2].price". Can read and set values in parsed JSON objects. homepage: "https://github.com/f3ath/jessie" +environment: + sdk: '>=2.12.0-29 <3.0.0' + dev_dependencies: - pedantic: ^1.9.0 - test: ^1.9.0 - test_coverage: ^0.4.3 + pedantic: ^1.10.0-nullsafety + test: ^1.16.0-nullsafety + test_coverage: ^0.5.0 -environment: - sdk: ">=2.8.0 <3.0.0"