diff --git a/traversal/selector/exploreAll.go b/traversal/selector/exploreAll.go index 9cd8a0676019945f03d289ccb25c2a4d76c953bf..4003d5e0cd3af2c99ce4c583915eefe7637bfd70 100644 --- a/traversal/selector/exploreAll.go +++ b/traversal/selector/exploreAll.go @@ -28,7 +28,7 @@ func (s ExploreAll) Decide(n ipld.Node) bool { } // ParseExploreAll assembles a Selector from a ExploreAll selector node -func ParseExploreAll(n ipld.Node, selectorContexts ...SelectorContext) (Selector, error) { +func (pc ParseContext) ParseExploreAll(n ipld.Node) (Selector, error) { if n.ReprKind() != ipld.ReprKind_Map { return nil, fmt.Errorf("selector spec parse rejected: selector body must be a map") } @@ -36,7 +36,7 @@ func ParseExploreAll(n ipld.Node, selectorContexts ...SelectorContext) (Selector if err != nil { return nil, fmt.Errorf("selector spec parse rejected: next field must be present in ExploreAll selector") } - selector, err := ParseSelector(next, selectorContexts...) + selector, err := pc.ParseSelector(next) if err != nil { return nil, err } diff --git a/traversal/selector/exploreAll_test.go b/traversal/selector/exploreAll_test.go index 409ca511fed598c6fc6a586b83261df3d222dceb..0113886542af356bd6a8f4a674b692ad45c38206 100644 --- a/traversal/selector/exploreAll_test.go +++ b/traversal/selector/exploreAll_test.go @@ -13,12 +13,12 @@ func TestParseExploreAll(t *testing.T) { fnb := fluent.WrapNodeBuilder(ipldfree.NodeBuilder()) // just for the other fixture building t.Run("parsing non map node should error", func(t *testing.T) { sn := fnb.CreateInt(0) - _, err := ParseExploreAll(sn) + _, err := ParseContext{}.ParseExploreAll(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: selector body must be a map")) }) t.Run("parsing map node without next field should error", func(t *testing.T) { sn := fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) {}) - _, err := ParseExploreAll(sn) + _, err := ParseContext{}.ParseExploreAll(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: next field must be present in ExploreAll selector")) }) @@ -26,7 +26,7 @@ func TestParseExploreAll(t *testing.T) { sn := fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { mb.Insert(knb.CreateString(nextSelectorKey), vnb.CreateInt(0)) }) - _, err := ParseExploreAll(sn) + _, err := ParseContext{}.ParseExploreAll(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: selector is a keyed union and thus must be a map")) }) t.Run("parsing map node with next field with valid selector node should parse", func(t *testing.T) { @@ -35,7 +35,7 @@ func TestParseExploreAll(t *testing.T) { mb.Insert(knb.CreateString(matcherKey), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) {})) })) }) - s, err := ParseExploreAll(sn) + s, err := ParseContext{}.ParseExploreAll(sn) Wish(t, err, ShouldEqual, nil) Wish(t, s, ShouldEqual, ExploreAll{Matcher{}}) }) diff --git a/traversal/selector/exploreFields.go b/traversal/selector/exploreFields.go index 106adb11218ff0dde7c8295ed893c992dab9b7e1..50af7e21c9945ba4369a76dc2606cd17f0929090 100644 --- a/traversal/selector/exploreFields.go +++ b/traversal/selector/exploreFields.go @@ -40,7 +40,7 @@ func (s ExploreFields) Decide(n ipld.Node) bool { // ParseExploreFields assembles a Selector // from a ExploreFields selector node -func ParseExploreFields(n ipld.Node, selectorContexts ...SelectorContext) (Selector, error) { +func (pc ParseContext) ParseExploreFields(n ipld.Node) (Selector, error) { if n.ReprKind() != ipld.ReprKind_Map { return nil, fmt.Errorf("selector spec parse rejected: selector body must be a map") } @@ -63,7 +63,7 @@ func ParseExploreFields(n ipld.Node, selectorContexts ...SelectorContext) (Selec kstr, _ := kn.AsString() x.interests = append(x.interests, PathSegmentString{kstr}) - x.selections[kstr], err = ParseSelector(v, selectorContexts...) + x.selections[kstr], err = pc.ParseSelector(v) if err != nil { return nil, err } diff --git a/traversal/selector/exploreFields_test.go b/traversal/selector/exploreFields_test.go index c905c3b913a8217dcb03d5138810d5fbde2fcd19..6017cad7a7723c24a42fed12a3bc63a1c1e06749 100644 --- a/traversal/selector/exploreFields_test.go +++ b/traversal/selector/exploreFields_test.go @@ -13,19 +13,19 @@ func TestParseExploreFields(t *testing.T) { fnb := fluent.WrapNodeBuilder(ipldfree.NodeBuilder()) // just for the other fixture building t.Run("parsing non map node should error", func(t *testing.T) { sn := fnb.CreateInt(0) - _, err := ParseExploreFields(sn) + _, err := ParseContext{}.ParseExploreFields(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: selector body must be a map")) }) t.Run("parsing map node without fields value should error", func(t *testing.T) { sn := fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) {}) - _, err := ParseExploreFields(sn) + _, err := ParseContext{}.ParseExploreFields(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: fields in ExploreFields selector must be present")) }) t.Run("parsing map node with fields value that is not a map should error", func(t *testing.T) { sn := fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { mb.Insert(knb.CreateString(fieldsKey), vnb.CreateString("cheese")) }) - _, err := ParseExploreFields(sn) + _, err := ParseContext{}.ParseExploreFields(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: fields in ExploreFields selector must be a map")) }) t.Run("parsing map node with selector node in fields that is invalid should return child's error", func(t *testing.T) { @@ -34,7 +34,7 @@ func TestParseExploreFields(t *testing.T) { mb.Insert(knb.CreateString("applesauce"), vnb.CreateInt(0)) })) }) - _, err := ParseExploreFields(sn) + _, err := ParseContext{}.ParseExploreFields(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: selector is a keyed union and thus must be a map")) }) t.Run("parsing map node with fields value that is map of only valid selector node should parse", func(t *testing.T) { @@ -45,7 +45,7 @@ func TestParseExploreFields(t *testing.T) { })) })) }) - s, err := ParseExploreFields(sn) + s, err := ParseContext{}.ParseExploreFields(sn) Wish(t, err, ShouldEqual, nil) Wish(t, s, ShouldEqual, ExploreFields{map[string]Selector{"applesauce": Matcher{}}, []PathSegment{PathSegmentString{S: "applesauce"}}}) }) diff --git a/traversal/selector/exploreIndex.go b/traversal/selector/exploreIndex.go index e033d836d92faf9589e4280d76f4f1d06289efe7..4092dabcd831db7e3aa1c0727daf7e6e93b44fa0 100644 --- a/traversal/selector/exploreIndex.go +++ b/traversal/selector/exploreIndex.go @@ -39,7 +39,7 @@ func (s ExploreIndex) Decide(n ipld.Node) bool { // ParseExploreIndex assembles a Selector // from a ExploreIndex selector node -func ParseExploreIndex(n ipld.Node, selectorContexts ...SelectorContext) (Selector, error) { +func (pc ParseContext) ParseExploreIndex(n ipld.Node) (Selector, error) { if n.ReprKind() != ipld.ReprKind_Map { return nil, fmt.Errorf("selector spec parse rejected: selector body must be a map") } @@ -55,7 +55,7 @@ func ParseExploreIndex(n ipld.Node, selectorContexts ...SelectorContext) (Select if err != nil { return nil, fmt.Errorf("selector spec parse rejected: next field must be present in ExploreIndex selector") } - selector, err := ParseSelector(next, selectorContexts...) + selector, err := pc.ParseSelector(next) if err != nil { return nil, err } diff --git a/traversal/selector/exploreIndex_test.go b/traversal/selector/exploreIndex_test.go index ecc698617ea511cb70658c3c0a65bc2988371755..c56d682e96dec671dee429c30907b94083b8957f 100644 --- a/traversal/selector/exploreIndex_test.go +++ b/traversal/selector/exploreIndex_test.go @@ -14,14 +14,14 @@ func TestParseExploreIndex(t *testing.T) { fnb := fluent.WrapNodeBuilder(ipldfree.NodeBuilder()) // just for the other fixture building t.Run("parsing non map node should error", func(t *testing.T) { sn := fnb.CreateInt(0) - _, err := ParseExploreIndex(sn) + _, err := ParseContext{}.ParseExploreIndex(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: selector body must be a map")) }) t.Run("parsing map node without next field should error", func(t *testing.T) { sn := fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { mb.Insert(knb.CreateString(indexKey), vnb.CreateInt(2)) }) - _, err := ParseExploreIndex(sn) + _, err := ParseContext{}.ParseExploreIndex(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: next field must be present in ExploreIndex selector")) }) t.Run("parsing map node without index field should error", func(t *testing.T) { @@ -30,7 +30,7 @@ func TestParseExploreIndex(t *testing.T) { mb.Insert(knb.CreateString(matcherKey), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) {})) })) }) - _, err := ParseExploreIndex(sn) + _, err := ParseContext{}.ParseExploreIndex(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: index field must be present in ExploreIndex selector")) }) t.Run("parsing map node with index field that is not an int should error", func(t *testing.T) { @@ -40,7 +40,7 @@ func TestParseExploreIndex(t *testing.T) { mb.Insert(knb.CreateString(matcherKey), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) {})) })) }) - _, err := ParseExploreIndex(sn) + _, err := ParseContext{}.ParseExploreIndex(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: index field must be a number in ExploreIndex selector")) }) t.Run("parsing map node with next field with invalid selector node should return child's error", func(t *testing.T) { @@ -48,7 +48,7 @@ func TestParseExploreIndex(t *testing.T) { mb.Insert(knb.CreateString(indexKey), vnb.CreateInt(2)) mb.Insert(knb.CreateString(nextSelectorKey), vnb.CreateInt(0)) }) - _, err := ParseExploreIndex(sn) + _, err := ParseContext{}.ParseExploreIndex(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: selector is a keyed union and thus must be a map")) }) t.Run("parsing map node with next field with valid selector node should parse", func(t *testing.T) { @@ -58,7 +58,7 @@ func TestParseExploreIndex(t *testing.T) { mb.Insert(knb.CreateString(matcherKey), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) {})) })) }) - s, err := ParseExploreIndex(sn) + s, err := ParseContext{}.ParseExploreIndex(sn) Wish(t, err, ShouldEqual, nil) Wish(t, s, ShouldEqual, ExploreIndex{Matcher{}, [1]PathSegment{PathSegmentInt{I: 2}}}) }) diff --git a/traversal/selector/exploreRange.go b/traversal/selector/exploreRange.go index 9d365fda201a16bcc0d915b023741ec2bedd7340..00243bfee6381ba310d5a5c60a582281fcc5fbde 100644 --- a/traversal/selector/exploreRange.go +++ b/traversal/selector/exploreRange.go @@ -43,7 +43,7 @@ func (s ExploreRange) Decide(n ipld.Node) bool { // ParseExploreRange assembles a Selector // from a ExploreRange selector node -func ParseExploreRange(n ipld.Node, selectorContexts ...SelectorContext) (Selector, error) { +func (pc ParseContext) ParseExploreRange(n ipld.Node) (Selector, error) { if n.ReprKind() != ipld.ReprKind_Map { return nil, fmt.Errorf("selector spec parse rejected: selector body must be a map") } @@ -70,7 +70,7 @@ func ParseExploreRange(n ipld.Node, selectorContexts ...SelectorContext) (Select if err != nil { return nil, fmt.Errorf("selector spec parse rejected: next field must be present in ExploreRange selector") } - selector, err := ParseSelector(next, selectorContexts...) + selector, err := pc.ParseSelector(next) if err != nil { return nil, err } diff --git a/traversal/selector/exploreRange_test.go b/traversal/selector/exploreRange_test.go index 0d512e837a0cf463c464440fd2697678e32e7ce9..3ed556fea9c274e7d62fbe64ef8ecebb9d6f01a2 100644 --- a/traversal/selector/exploreRange_test.go +++ b/traversal/selector/exploreRange_test.go @@ -14,7 +14,7 @@ func TestParseExploreRange(t *testing.T) { fnb := fluent.WrapNodeBuilder(ipldfree.NodeBuilder()) // just for the other fixture building t.Run("parsing non map node should error", func(t *testing.T) { sn := fnb.CreateInt(0) - _, err := ParseExploreRange(sn) + _, err := ParseContext{}.ParseExploreRange(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: selector body must be a map")) }) t.Run("parsing map node without next field should error", func(t *testing.T) { @@ -22,7 +22,7 @@ func TestParseExploreRange(t *testing.T) { mb.Insert(knb.CreateString(startKey), vnb.CreateInt(2)) mb.Insert(knb.CreateString(endKey), vnb.CreateInt(3)) }) - _, err := ParseExploreRange(sn) + _, err := ParseContext{}.ParseExploreRange(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: next field must be present in ExploreRange selector")) }) t.Run("parsing map node without start field should error", func(t *testing.T) { @@ -32,7 +32,7 @@ func TestParseExploreRange(t *testing.T) { mb.Insert(knb.CreateString(matcherKey), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) {})) })) }) - _, err := ParseExploreRange(sn) + _, err := ParseContext{}.ParseExploreRange(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: start field must be present in ExploreRange selector")) }) t.Run("parsing map node with start field that is not an int should error", func(t *testing.T) { @@ -43,7 +43,7 @@ func TestParseExploreRange(t *testing.T) { mb.Insert(knb.CreateString(matcherKey), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) {})) })) }) - _, err := ParseExploreRange(sn) + _, err := ParseContext{}.ParseExploreRange(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: start field must be a number in ExploreRange selector")) }) t.Run("parsing map node without end field should error", func(t *testing.T) { @@ -53,7 +53,7 @@ func TestParseExploreRange(t *testing.T) { mb.Insert(knb.CreateString(matcherKey), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) {})) })) }) - _, err := ParseExploreRange(sn) + _, err := ParseContext{}.ParseExploreRange(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: end field must be present in ExploreRange selector")) }) t.Run("parsing map node with end field that is not an int should error", func(t *testing.T) { @@ -64,7 +64,7 @@ func TestParseExploreRange(t *testing.T) { mb.Insert(knb.CreateString(matcherKey), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) {})) })) }) - _, err := ParseExploreRange(sn) + _, err := ParseContext{}.ParseExploreRange(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: end field must be a number in ExploreRange selector")) }) t.Run("parsing map node where end is not greater than start should error", func(t *testing.T) { @@ -75,7 +75,7 @@ func TestParseExploreRange(t *testing.T) { mb.Insert(knb.CreateString(matcherKey), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) {})) })) }) - _, err := ParseExploreRange(sn) + _, err := ParseContext{}.ParseExploreRange(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: end field must be greater than start field in ExploreRange selector")) }) t.Run("parsing map node with next field with invalid selector node should return child's error", func(t *testing.T) { @@ -84,7 +84,7 @@ func TestParseExploreRange(t *testing.T) { mb.Insert(knb.CreateString(endKey), vnb.CreateInt(3)) mb.Insert(knb.CreateString(nextSelectorKey), vnb.CreateInt(0)) }) - _, err := ParseExploreRange(sn) + _, err := ParseContext{}.ParseExploreRange(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: selector is a keyed union and thus must be a map")) }) @@ -96,7 +96,7 @@ func TestParseExploreRange(t *testing.T) { mb.Insert(knb.CreateString(matcherKey), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) {})) })) }) - s, err := ParseExploreRange(sn) + s, err := ParseContext{}.ParseExploreRange(sn) Wish(t, err, ShouldEqual, nil) Wish(t, s, ShouldEqual, ExploreRange{Matcher{}, 2, 3, []PathSegment{PathSegmentInt{I: 2}}}) }) diff --git a/traversal/selector/exploreRecursive.go b/traversal/selector/exploreRecursive.go index 89c5fc2e039a026c16643d411161eb8091165c4a..126fc543fa6d23c6ceddb6f6cc9a5014699a31dd 100644 --- a/traversal/selector/exploreRecursive.go +++ b/traversal/selector/exploreRecursive.go @@ -76,7 +76,7 @@ func (erc *exploreRecursiveContext) Link(s Selector) bool { } // ParseExploreRecursive assembles a Selector from a ExploreRecursive selector node -func ParseExploreRecursive(n ipld.Node, selectorContexts ...SelectorContext) (Selector, error) { +func (pc ParseContext) ParseExploreRecursive(n ipld.Node) (Selector, error) { if n.ReprKind() != ipld.ReprKind_Map { return nil, fmt.Errorf("selector spec parse rejected: selector body must be a map") } @@ -94,7 +94,7 @@ func ParseExploreRecursive(n ipld.Node, selectorContexts ...SelectorContext) (Se return nil, fmt.Errorf("selector spec parse rejected: sequence field must be present in ExploreRecursive selector") } erc := &exploreRecursiveContext{} - selector, err := ParseSelector(sequence, append([]SelectorContext{erc}, selectorContexts...)...) + selector, err := pc.PushParent(erc).ParseSelector(sequence) if err != nil { return nil, err } diff --git a/traversal/selector/exploreRecursiveEdge.go b/traversal/selector/exploreRecursiveEdge.go index d519255a88669bdc7561f015d572ffb2af4787ab..2330220805cbbb634da1cefbb7bb1cbce2c08ea1 100644 --- a/traversal/selector/exploreRecursiveEdge.go +++ b/traversal/selector/exploreRecursiveEdge.go @@ -33,13 +33,13 @@ func (s ExploreRecursiveEdge) Decide(n ipld.Node) bool { // ParseExploreRecursiveEdge assembles a Selector // from a exploreRecursiveEdge selector node -func ParseExploreRecursiveEdge(n ipld.Node, selectorContexts ...SelectorContext) (Selector, error) { +func (pc ParseContext) ParseExploreRecursiveEdge(n ipld.Node) (Selector, error) { if n.ReprKind() != ipld.ReprKind_Map { return nil, fmt.Errorf("selector spec parse rejected: selector body must be a map") } s := ExploreRecursiveEdge{} - for _, selectorContext := range selectorContexts { - if selectorContext.Link(s) { + for _, parent := range pc.parentStack { + if parent.Link(s) { return s, nil } } diff --git a/traversal/selector/exploreRecursive_test.go b/traversal/selector/exploreRecursive_test.go index 1513b4cc6b4a90990268c4fef889f0f501b54318..9e86bbf9f48e183172be5ffdd3341eeed59c1e58 100644 --- a/traversal/selector/exploreRecursive_test.go +++ b/traversal/selector/exploreRecursive_test.go @@ -15,14 +15,14 @@ func TestParseExploreRecursive(t *testing.T) { fnb := fluent.WrapNodeBuilder(ipldfree.NodeBuilder()) // just for the other fixture building t.Run("parsing non map node should error", func(t *testing.T) { sn := fnb.CreateInt(0) - _, err := ParseExploreRecursive(sn) + _, err := ParseContext{}.ParseExploreRecursive(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: selector body must be a map")) }) t.Run("parsing map node without sequence field should error", func(t *testing.T) { sn := fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) { mb.Insert(knb.CreateString(maxDepthKey), vnb.CreateInt(2)) }) - _, err := ParseExploreRecursive(sn) + _, err := ParseContext{}.ParseExploreRecursive(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: sequence field must be present in ExploreRecursive selector")) }) t.Run("parsing map node without maxDepth field should error", func(t *testing.T) { @@ -31,7 +31,7 @@ func TestParseExploreRecursive(t *testing.T) { mb.Insert(knb.CreateString(matcherKey), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) {})) })) }) - _, err := ParseExploreRecursive(sn) + _, err := ParseContext{}.ParseExploreRecursive(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: maxDepth field must be present in ExploreRecursive selector")) }) t.Run("parsing map node with maxDepth field that is not an int should error", func(t *testing.T) { @@ -41,7 +41,7 @@ func TestParseExploreRecursive(t *testing.T) { mb.Insert(knb.CreateString(matcherKey), vnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) {})) })) }) - _, err := ParseExploreRecursive(sn) + _, err := ParseContext{}.ParseExploreRecursive(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: maxDepth field must be a number in ExploreRecursive selector")) }) t.Run("parsing map node with sequence field with invalid selector node should return child's error", func(t *testing.T) { @@ -49,7 +49,7 @@ func TestParseExploreRecursive(t *testing.T) { mb.Insert(knb.CreateString(maxDepthKey), vnb.CreateInt(2)) mb.Insert(knb.CreateString(sequenceKey), vnb.CreateInt(0)) }) - _, err := ParseExploreRecursive(sn) + _, err := ParseContext{}.ParseExploreRecursive(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: selector is a keyed union and thus must be a map")) }) t.Run("parsing map node with sequence field with valid selector w/o ExploreRecursiveEdge should not parse", func(t *testing.T) { @@ -63,12 +63,12 @@ func TestParseExploreRecursive(t *testing.T) { })) })) }) - _, err := ParseExploreRecursive(sn) + _, err := ParseContext{}.ParseExploreRecursive(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: ExploreRecursive must have at least one ExploreRecursiveEdge")) }) t.Run("parsing map node that is ExploreRecursiveEdge without ExploreRecursive parent should not parse", func(t *testing.T) { sn := fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) {}) - _, err := ParseExploreRecursiveEdge(sn) + _, err := ParseContext{}.ParseExploreRecursiveEdge(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: ExploreRecursiveEdge must be beneath ExploreRecursive")) }) t.Run("parsing map node with sequence field with valid selector node should parse", func(t *testing.T) { @@ -82,7 +82,7 @@ func TestParseExploreRecursive(t *testing.T) { })) })) }) - s, err := ParseExploreRecursive(sn) + s, err := ParseContext{}.ParseExploreRecursive(sn) Wish(t, err, ShouldEqual, nil) Wish(t, s, ShouldEqual, ExploreRecursive{ExploreAll{ExploreRecursiveEdge{}}, ExploreAll{ExploreRecursiveEdge{}}, 2}) }) diff --git a/traversal/selector/exploreUnion.go b/traversal/selector/exploreUnion.go index 2e8e050269ec8c8f84f646c21c3662f740a50053..c5f7b82b945feba4ddbac34e53035c077247f27c 100644 --- a/traversal/selector/exploreUnion.go +++ b/traversal/selector/exploreUnion.go @@ -73,7 +73,7 @@ func (s ExploreUnion) Decide(n ipld.Node) bool { // ParseExploreUnion assembles a Selector // from an ExploreUnion selector node -func ParseExploreUnion(n ipld.Node, selectorContexts ...SelectorContext) (Selector, error) { +func (pc ParseContext) ParseExploreUnion(n ipld.Node) (Selector, error) { if n.ReprKind() != ipld.ReprKind_List { return nil, fmt.Errorf("selector spec parse rejected: explore union selector must be a list") } @@ -85,7 +85,7 @@ func ParseExploreUnion(n ipld.Node, selectorContexts ...SelectorContext) (Select if err != nil { return nil, fmt.Errorf("error during selector spec parse: %s", err) } - member, err := ParseSelector(v, selectorContexts...) + member, err := pc.ParseSelector(v) if err != nil { return nil, err } diff --git a/traversal/selector/exploreUnion_test.go b/traversal/selector/exploreUnion_test.go index 9fad7e058931abd3367bec0b7a1ed22913743b4b..29a1382531f0f7f188791c831db2a2c966df9440 100644 --- a/traversal/selector/exploreUnion_test.go +++ b/traversal/selector/exploreUnion_test.go @@ -14,7 +14,7 @@ func TestParseExploreUnion(t *testing.T) { fnb := fluent.WrapNodeBuilder(ipldfree.NodeBuilder()) // just for the other fixture building t.Run("parsing non list node should error", func(t *testing.T) { sn := fnb.CreateMap(func(mb fluent.MapBuilder, knb fluent.NodeBuilder, vnb fluent.NodeBuilder) {}) - _, err := ParseExploreUnion(sn) + _, err := ParseContext{}.ParseExploreUnion(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: explore union selector must be a list")) }) t.Run("parsing list node where one node is invalid should return child's error", func(t *testing.T) { @@ -24,7 +24,7 @@ func TestParseExploreUnion(t *testing.T) { })) lb.Append(vnb.CreateInt(2)) }) - _, err := ParseExploreUnion(sn) + _, err := ParseContext{}.ParseExploreUnion(sn) Wish(t, err, ShouldEqual, fmt.Errorf("selector spec parse rejected: selector is a keyed union and thus must be a map")) }) @@ -42,7 +42,7 @@ func TestParseExploreUnion(t *testing.T) { })) })) }) - s, err := ParseExploreUnion(sn) + s, err := ParseContext{}.ParseExploreUnion(sn) Wish(t, err, ShouldEqual, nil) Wish(t, s, ShouldEqual, ExploreUnion{[]Selector{Matcher{}, ExploreIndex{Matcher{}, [1]PathSegment{PathSegmentInt{I: 2}}}}}) }) diff --git a/traversal/selector/matcher.go b/traversal/selector/matcher.go index e210922b1a33fe6b942be66e5f013a9ef40d0ebd..a746779ccd8528fadc61e35ababc06c03ee56f26 100644 --- a/traversal/selector/matcher.go +++ b/traversal/selector/matcher.go @@ -38,7 +38,7 @@ func (s Matcher) Decide(n ipld.Node) bool { // ParseMatcher assembles a Selector // from a matcher selector node // TODO: Parse labels and conditions -func ParseMatcher(n ipld.Node, selectorContexts ...SelectorContext) (Selector, error) { +func (pc ParseContext) ParseMatcher(n ipld.Node) (Selector, error) { if n.ReprKind() != ipld.ReprKind_Map { return nil, fmt.Errorf("selector spec parse rejected: selector body must be a map") } diff --git a/traversal/selector/selector.go b/traversal/selector/selector.go index 9ac880071ede4ebeb9e08392a0241119ea0f7249..4d5bc2c9b4404e149a8b28d1e055db1d3ec00f16 100644 --- a/traversal/selector/selector.go +++ b/traversal/selector/selector.go @@ -15,12 +15,24 @@ type Selector interface { Decide(ipld.Node) bool } -type SelectorContext interface { +// ParsedParent is created whenever you are parsing a selector node that may have +// child selectors nodes that need to know it +type ParsedParent interface { Link(s Selector) bool } +// ParseContext tracks the progress when parsing a selector +type ParseContext struct { + parentStack []ParsedParent +} + // ParseSelector creates a Selector that can be traversed from an IPLD Selector node -func ParseSelector(n ipld.Node, selectorContexts ...SelectorContext) (Selector, error) { +func ParseSelector(n ipld.Node) (Selector, error) { + return ParseContext{}.ParseSelector(n) +} + +// ParseSelector creates a Selector from an IPLD Selector Node with the given context +func (pc ParseContext) ParseSelector(n ipld.Node) (Selector, error) { if n.ReprKind() != ipld.ReprKind_Map { return nil, fmt.Errorf("selector spec parse rejected: selector is a keyed union and thus must be a map") } @@ -33,26 +45,35 @@ func ParseSelector(n ipld.Node, selectorContexts ...SelectorContext) (Selector, // (This switch is where the keyed union discriminators concretely happen.) switch kstr { case exploreFieldsKey: - return ParseExploreFields(v, selectorContexts...) + return pc.ParseExploreFields(v) case exploreAllKey: - return ParseExploreAll(v, selectorContexts...) + return pc.ParseExploreAll(v) case exploreIndexKey: - return ParseExploreIndex(v, selectorContexts...) + return pc.ParseExploreIndex(v) case exploreRangeKey: - return ParseExploreRange(v, selectorContexts...) + return pc.ParseExploreRange(v) case exploreUnionKey: - return ParseExploreUnion(v, selectorContexts...) + return pc.ParseExploreUnion(v) case exploreRecursiveKey: - return ParseExploreRecursive(v, selectorContexts...) + return pc.ParseExploreRecursive(v) case exploreRecursiveEdgeKey: - return ParseExploreRecursiveEdge(v, selectorContexts...) + return pc.ParseExploreRecursiveEdge(v) case matcherKey: - return ParseMatcher(v, selectorContexts...) + return pc.ParseMatcher(v) default: return nil, fmt.Errorf("selector spec parse rejected: %q is not a known member of the selector union", kstr) } } +// PushParent puts a parent onto the stack of parents for a parse context +func (pc ParseContext) PushParent(parent ParsedParent) ParseContext { + l := len(pc.parentStack) + parents := make([]ParsedParent, 0, l+1) + parents = append(parents, parent) + parents = append(parents, pc.parentStack...) + return ParseContext{parents} +} + // PathSegment can describe either an index in a list or a key in a map, as either int or a string type PathSegment interface { String() string