From cfe208e0c3b7199418e5b79e41765a3aca91a3da Mon Sep 17 00:00:00 2001 From: f3ath Date: Fri, 24 Jul 2020 20:54:01 -0700 Subject: [PATCH] Basic design draft --- .cider.yaml | 2 ++ .github/workflows/dart.yml | 24 ++++++++++++++++++++++++ README.md | 17 +++++++++++++++-- analysis_options.yaml | 5 +++++ lib/jessie.dart | 6 ++++++ lib/src/field.dart | 15 +++++++++++++++ lib/src/index.dart | 15 +++++++++++++++ lib/src/json_path.dart | 31 +++++++++++++++++++++++++++++++ pubspec.yaml | 9 +++++++++ test/functional_test.dart | 18 ++++++++++++++++++ test/store.json | 36 ++++++++++++++++++++++++++++++++++++ 11 files changed, 176 insertions(+), 2 deletions(-) create mode 100644 .cider.yaml create mode 100644 .github/workflows/dart.yml create mode 100644 analysis_options.yaml create mode 100644 lib/jessie.dart create mode 100644 lib/src/field.dart create mode 100644 lib/src/index.dart create mode 100644 lib/src/json_path.dart create mode 100644 pubspec.yaml create mode 100644 test/functional_test.dart create mode 100644 test/store.json diff --git a/.cider.yaml b/.cider.yaml new file mode 100644 index 0000000..3bf0b0c --- /dev/null +++ b/.cider.yaml @@ -0,0 +1,2 @@ +changelog: + diff_link_template: 'https://github.com/f3ath/jessie/compare/%from%...%to%' \ No newline at end of file diff --git a/.github/workflows/dart.yml b/.github/workflows/dart.yml new file mode 100644 index 0000000..13980bc --- /dev/null +++ b/.github/workflows/dart.yml @@ -0,0 +1,24 @@ +name: Dart CI + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build: + + runs-on: ubuntu-latest + + container: + image: google/dart:latest + + steps: + - uses: actions/checkout@v2 + - name: Install dependencies + run: pub get + - name: Analyzer + run: dartanalyzer --fatal-infos --fatal-warnings lib test + - name: Tests + run: pub run test \ No newline at end of file diff --git a/README.md b/README.md index 16863cf..5916b6b 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,15 @@ -# jessie -JsonPath for Dart +# [JSONPath] for Dart +Jessie is a work-in-progress. Expect the API to change often. Feel free to join. + +## Roadmap +- [ ] Basic selectors: fields, indices +- [ ] Recursive descent (`..`) +- [ ] Wildcard (`*`) +- [ ] Subscript (`[:2]`) +- [ ] Slice (`[1:10:2]`) +- [ ] Union (`book[0, 1]`, `book[author, title, price]`) +- [ ] Basic filtering (`book[?(@.price - 1)]`) +- [ ] Expressions? + + +[JSONPath]: https://goessner.net/articles/JsonPath/ \ No newline at end of file diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 0000000..7783dc8 --- /dev/null +++ b/analysis_options.yaml @@ -0,0 +1,5 @@ +include: package:pedantic/analysis_options.yaml +linter: + rules: + - sort_constructors_first + - sort_unnamed_constructors_first \ No newline at end of file diff --git a/lib/jessie.dart b/lib/jessie.dart new file mode 100644 index 0000000..543256b --- /dev/null +++ b/lib/jessie.dart @@ -0,0 +1,6 @@ +/// JSONPath for Dart +library jessie; + +export 'package:jessie/src/field.dart'; +export 'package:jessie/src/index.dart'; +export 'package:jessie/src/json_path.dart'; diff --git a/lib/src/field.dart b/lib/src/field.dart new file mode 100644 index 0000000..8c2c791 --- /dev/null +++ b/lib/src/field.dart @@ -0,0 +1,15 @@ +import 'package:jessie/src/json_path.dart'; + +class Field extends JsonPath { + Field(this.name); + + final String name; + + @override + Iterable call(Iterable nodes) => nodes + .where((node) => node is Map && node.containsKey(name)) + .map((node) => node[name]); + + @override + String toString() => "['$name']"; +} diff --git a/lib/src/index.dart b/lib/src/index.dart new file mode 100644 index 0000000..46552ba --- /dev/null +++ b/lib/src/index.dart @@ -0,0 +1,15 @@ +import 'package:jessie/src/json_path.dart'; + +class Index extends JsonPath { + Index(this.index); + + final int index; + + @override + Iterable call(Iterable nodes) => nodes + .where((node) => node is List && node.length > index + 1) + .map((node) => node[index]); + + @override + String toString() => "[$index]"; +} diff --git a/lib/src/json_path.dart b/lib/src/json_path.dart new file mode 100644 index 0000000..b9bdb25 --- /dev/null +++ b/lib/src/json_path.dart @@ -0,0 +1,31 @@ +abstract class JsonPath { + /// Applies this JSONPath to the [nodes] + Iterable call(Iterable nodes); + + /// The string expression without leading `$` + String toString(); + + /// A shortcut for `then()` + JsonPath operator |(JsonPath other) => then(other); + + /// Combines this expression with the [other] + JsonPath then(JsonPath other) => _Chain(this, other); + + /// Filters the given nodes. + /// Returns an Iterable of all elements found + Iterable filter(dynamic node) => call([node]); +} + +class _Chain extends JsonPath { + _Chain(this.first, this.second); + + final JsonPath first; + + final JsonPath second; + + @override + Iterable call(Iterable nodes) => second(first(nodes)); + + @override + String toString() => '$first$second'; +} diff --git a/pubspec.yaml b/pubspec.yaml new file mode 100644 index 0000000..c607d3d --- /dev/null +++ b/pubspec.yaml @@ -0,0 +1,9 @@ +name: jessie +version: 0.0.0+dev.0 +description: JSONPath for Dart +environment: + sdk: ">=2.8.0 <3.0.0" + +dev_dependencies: + test: ^1.9.0 + pedantic: ^1.9.0 diff --git a/test/functional_test.dart b/test/functional_test.dart new file mode 100644 index 0000000..cc9ca3e --- /dev/null +++ b/test/functional_test.dart @@ -0,0 +1,18 @@ +import 'dart:convert'; +import 'dart:io'; + +import 'package:jessie/jessie.dart'; +import 'package:test/test.dart'; + +void main() { + final store = jsonDecode(File('test/store.json').readAsStringSync()); + group('Basic', () { + final path = Field('store') | Field('book') | Index(0) | Field('title'); + test('filtering', () { + expect(path.filter(store).single, 'Sayings of the Century'); + }); + test('toString()', () { + expect(path.toString(), "['store']['book'][0]['title']"); + }); + }); +} diff --git a/test/store.json b/test/store.json new file mode 100644 index 0000000..7353f8f --- /dev/null +++ b/test/store.json @@ -0,0 +1,36 @@ +{ + "store": { + "book": [ + { + "category": "reference", + "author": "Nigel Rees", + "title": "Sayings of the Century", + "price": 8.95 + }, + { + "category": "fiction", + "author": "Evelyn Waugh", + "title": "Sword of Honour", + "price": 12.99 + }, + { + "category": "fiction", + "author": "Herman Melville", + "title": "Moby Dick", + "isbn": "0-553-21311-3", + "price": 8.99 + }, + { + "category": "fiction", + "author": "J. R. R. Tolkien", + "title": "The Lord of the Rings", + "isbn": "0-395-19395-8", + "price": 22.99 + } + ], + "bicycle": { + "color": "red", + "price": 19.95 + } + } +} \ No newline at end of file