Skip to content

Commit

Permalink
Union and wildcart
Browse files Browse the repository at this point in the history
  • Loading branch information
f3ath committed Jul 29, 2020
1 parent b9e7eef commit fd745c3
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 18 deletions.
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ Jessie is a work-in-progress. Expect the API to change often. Feel free to join.

## Roadmap
- [x] Basic selectors: fields, indices
- [ ] Recursive descent (`..`)
- [x] Wildcard (`*`)
- [ ] Subscript (`[:2]`)
- [ ] Slice (`[1:10:2]`)
- [x] Recursive descent (`$..`)
- [x] Wildcard (`$.store.*`)
- [ ] Square-bracket field notation (`['foo']`, `$['"some" \'special\' [chars]']`)
- [ ] Subscript (`books[:2]`)
- [ ] Slice (`articles[1:10:2]`)
- [ ] Union (`book[0, 1]`, `book[author, title, price]`)
- [ ] Basic filtering (`book[?(@.price - 1)]`)
- [ ] Expressions?
Expand Down
24 changes: 24 additions & 0 deletions lib/src/selector/all_values.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import 'package:jessie/src/quote.dart';
import 'package:jessie/src/result.dart';
import 'package:jessie/src/selector/selector.dart';

class AllValues extends Selector {
@override
Iterable<Result> call(Iterable<Result> results) => results.map((r) {
final val = r.value;
if (val is Map) {
return val.entries
.map((e) => Result(e.value, r.path + '[${Quote(e.key)}]'));
}
if (val is List) {
return val
.asMap()
.entries
.map((e) => Result(e.value, r.path + '[${e.key}]'));
}
return <Result>[];
}).expand((_) => _);

@override
String get expression => '*';
}
26 changes: 19 additions & 7 deletions lib/src/selector/selector.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import 'package:jessie/src/result.dart';
import 'package:jessie/src/selector/all_values.dart';
import 'package:jessie/src/selector/recursive.dart';

/// Converts a set of results into a set of results
abstract class Selector {
Expand All @@ -14,19 +16,29 @@ abstract class Selector {
String toString() => expression;

/// Combines this expression with the [other]
Selector then(Selector other) => _Chain(this, other);
Selector then(Selector other) => Combine(this, other);
}

class _Chain extends Selector {
_Chain(this.first, this.second);
/// Combines two selectors together
class Combine extends Selector {
Combine(this.left, this.right);

final Selector first;
/// Returns the rightmost selector in a chain
static Selector rightmost(Selector s) =>
(s is Combine) ? rightmost(s.right) : s;

final Selector second;
final Selector left;

final Selector right;

@override
Iterable<Result> call(Iterable<Result> results) => second(first(results));
Iterable<Result> call(Iterable<Result> results) => right(left(results));

@override
String get expression => '$first$second';
String get expression {
/// Special case for the `*` element.
final actualLeft = rightmost(left);
final glue = (right is AllValues && (actualLeft is! Recursive)) ? '.' : '';
return '$left$glue$right';
}
}
12 changes: 12 additions & 0 deletions lib/src/state.dart
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
import 'package:jessie/src/ast.dart';
import 'package:jessie/src/selector/all_in_array.dart';
import 'package:jessie/src/selector/all_values.dart';
import 'package:jessie/src/selector/field.dart';
import 'package:jessie/src/selector/index.dart';
import 'package:jessie/src/selector/recursive.dart';
import 'package:jessie/src/selector/selector.dart';

/// AST parser state
abstract class State {
/// Processes the node. Returns the next state
State process(Node node);

/// Selector made from the tree
Selector get selector;
}

/// Ready to process the next node
class Ready implements State {
Ready(this.selector);

Expand All @@ -28,6 +33,10 @@ class Ready implements State {
if (node.value == '..') {
return Ready(selector.then(Recursive()));
}
if (node.value == '*') {
return Ready(selector.then(AllValues()));
}

throw StateError('Got ${node.value} in $this');
}

Expand All @@ -49,6 +58,9 @@ class AwaitingField implements State {

@override
State process(Node node) {
if (node.value == '*') {
return Ready(selector.then(AllValues()));
}
return Ready(selector.then(Field(node.value)));
}
}
41 changes: 34 additions & 7 deletions test/json_path_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,41 @@ void main() {
expect(allBooksInStore.select(json).last.path, r"$['store']['book'][3]");
});

test('All values', () {
final allInRoot = JsonPath(r'$.*');
expect(allInRoot.toString(), r'$.*');
expect(allInRoot.select(json).single.value, json['store']);
expect(allInRoot.select(json).single.path, r"$['store']");

final allInStore = JsonPath(r'$.store.*');
expect(allInStore.toString(), r"$['store'].*");
expect(allInStore.select(json).length, 2);
expect(allInStore.select(json).first.value, json['store']['book']);
expect(allInStore.select(json).first.path, r"$['store']['book']");
expect(allInStore.select(json).last.value, json['store']['bicycle']);
expect(allInStore.select(json).last.path, r"$['store']['bicycle']");
});

test('Recursive', () {
final everything = JsonPath(r'$..');
expect(everything.toString(), r'$..');
expect(everything.select(json).length, 8);
expect(everything.select(json).first.value, json);
expect(everything.select(json).first.path, r'$');
expect(everything.select(json).last.value, json['store']['bicycle']);
expect(everything.select(json).last.path, r"$['store']['bicycle']");
final allNode = JsonPath(r'$..');
expect(allNode.toString(), r'$..');
expect(allNode.select(json).length, 8);
expect(allNode.select(json).first.value, json);
expect(allNode.select(json).first.path, r'$');
expect(allNode.select(json).last.value, json['store']['bicycle']);
expect(allNode.select(json).last.path, r"$['store']['bicycle']");
});

test('Recursive with all fields', () {
final allValues = JsonPath(r'$..*');
expect(allValues.toString(), r'$..*');
expect(allValues.select(json).length, 27);
expect(allValues.select(json).first.value, json['store']);
expect(allValues.select(json).first.path, r"$['store']");
expect(
allValues.select(json).last.value, json['store']['bicycle']['price']);
expect(
allValues.select(json).last.path, r"$['store']['bicycle']['price']");
});
});
}

0 comments on commit fd745c3

Please sign in to comment.