Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
What's new
10
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Open sidebar
ld
go-ld-prime
Commits
30cf80e2
Unverified
Commit
30cf80e2
authored
Aug 08, 2019
by
Eric Myhre
Committed by
GitHub
Aug 08, 2019
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #26 from ipld/feat/recursive-selectors
Feat/recursive selectors
parents
631c9dad
cbedbe9a
Changes
15
Hide whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
505 additions
and
44 deletions
+505
-44
traversal/selector/exploreAll.go
traversal/selector/exploreAll.go
+2
-2
traversal/selector/exploreAll_test.go
traversal/selector/exploreAll_test.go
+4
-4
traversal/selector/exploreFields.go
traversal/selector/exploreFields.go
+2
-2
traversal/selector/exploreFields_test.go
traversal/selector/exploreFields_test.go
+5
-5
traversal/selector/exploreIndex.go
traversal/selector/exploreIndex.go
+2
-2
traversal/selector/exploreIndex_test.go
traversal/selector/exploreIndex_test.go
+6
-6
traversal/selector/exploreRange.go
traversal/selector/exploreRange.go
+2
-2
traversal/selector/exploreRange_test.go
traversal/selector/exploreRange_test.go
+9
-9
traversal/selector/exploreRecursive.go
traversal/selector/exploreRecursive.go
+105
-0
traversal/selector/exploreRecursiveEdge.go
traversal/selector/exploreRecursiveEdge.go
+47
-0
traversal/selector/exploreRecursive_test.go
traversal/selector/exploreRecursive_test.go
+280
-0
traversal/selector/exploreUnion.go
traversal/selector/exploreUnion.go
+2
-2
traversal/selector/exploreUnion_test.go
traversal/selector/exploreUnion_test.go
+3
-3
traversal/selector/matcher.go
traversal/selector/matcher.go
+1
-1
traversal/selector/selector.go
traversal/selector/selector.go
+35
-6
No files found.
traversal/selector/exploreAll.go
View file @
30cf80e2
...
...
@@ -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
)
(
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) (Selector, error) {
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"selector spec parse rejected: next field must be present in ExploreAll selector"
)
}
selector
,
err
:=
ParseSelector
(
next
)
selector
,
err
:=
pc
.
ParseSelector
(
next
)
if
err
!=
nil
{
return
nil
,
err
}
...
...
traversal/selector/exploreAll_test.go
View file @
30cf80e2
...
...
@@ -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
{}})
})
...
...
traversal/selector/exploreFields.go
View file @
30cf80e2
...
...
@@ -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
)
(
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) (Selector, error) {
kstr
,
_
:=
kn
.
AsString
()
x
.
interests
=
append
(
x
.
interests
,
PathSegmentString
{
kstr
})
x
.
selections
[
kstr
],
err
=
ParseSelector
(
v
)
x
.
selections
[
kstr
],
err
=
pc
.
ParseSelector
(
v
)
if
err
!=
nil
{
return
nil
,
err
}
...
...
traversal/selector/exploreFields_test.go
View file @
30cf80e2
...
...
@@ -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"
}}})
})
...
...
traversal/selector/exploreIndex.go
View file @
30cf80e2
...
...
@@ -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
)
(
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) (Selector, error) {
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"selector spec parse rejected: next field must be present in ExploreIndex selector"
)
}
selector
,
err
:=
ParseSelector
(
next
)
selector
,
err
:=
pc
.
ParseSelector
(
next
)
if
err
!=
nil
{
return
nil
,
err
}
...
...
traversal/selector/exploreIndex_test.go
View file @
30cf80e2
...
...
@@ -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
}}})
})
...
...
traversal/selector/exploreRange.go
View file @
30cf80e2
...
...
@@ -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
)
(
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) (Selector, error) {
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"selector spec parse rejected: next field must be present in ExploreRange selector"
)
}
selector
,
err
:=
ParseSelector
(
next
)
selector
,
err
:=
pc
.
ParseSelector
(
next
)
if
err
!=
nil
{
return
nil
,
err
}
...
...
traversal/selector/exploreRange_test.go
View file @
30cf80e2
...
...
@@ -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
}}})
})
...
...
traversal/selector/exploreRecursive.go
0 → 100644
View file @
30cf80e2
package
selector
import
(
"fmt"
ipld
"github.com/ipld/go-ipld-prime"
)
// ExploreRecursive traverses some structure recursively.
// To guide this exploration, it uses a "sequence", which is another Selector
// tree; some leaf node in this sequence should contain an ExploreRecursiveEdge
// selector, which denotes the place recursion should occur.
//
// In implementation, whenever evaluation reaches an ExploreRecursiveEdge marker
// in the recursion sequence's Selector tree, the implementation logically
// produces another new Selector which is a copy of the original
// ExploreRecursive selector, but with a decremented maxDepth parameter, and
// continues evaluation thusly.
//
// It is not valid for an ExploreRecursive selector's sequence to contain
// no instances of ExploreRecursiveEdge; it *is* valid for it to contain
// more than one ExploreRecursiveEdge.
//
// ExploreRecursive can contain a nested ExploreRecursive!
// This is comparable to a nested for-loop.
// In these cases, any ExploreRecursiveEdge instance always refers to the
// nearest parent ExploreRecursive (in other words, ExploreRecursiveEdge can
// be thought of like the 'continue' statement, or end of a for-loop body;
// it is *not* a 'goto' statement).
//
// Be careful when using ExploreRecursive with a large maxDepth parameter;
// it can easily cause very large traversals (especially if used in combination
// with selectors like ExploreAll inside the sequence).
type
ExploreRecursive
struct
{
sequence
Selector
// selector for element we're interested in
current
Selector
// selector to apply to the current node
maxDepth
int
}
// Interests for ExploreRecursive is empty (meaning traverse everything)
func
(
s
ExploreRecursive
)
Interests
()
[]
PathSegment
{
return
s
.
current
.
Interests
()
}
// Explore returns the node's selector for all fields
func
(
s
ExploreRecursive
)
Explore
(
n
ipld
.
Node
,
p
PathSegment
)
Selector
{
nextSelector
:=
s
.
current
.
Explore
(
n
,
p
)
if
nextSelector
==
nil
{
return
nil
}
_
,
ok
:=
nextSelector
.
(
ExploreRecursiveEdge
)
if
!
ok
{
return
ExploreRecursive
{
s
.
sequence
,
nextSelector
,
s
.
maxDepth
}
}
if
s
.
maxDepth
<
2
{
return
nil
}
return
ExploreRecursive
{
s
.
sequence
,
s
.
sequence
,
s
.
maxDepth
-
1
}
}
// Decide always returns false because this is not a matcher
func
(
s
ExploreRecursive
)
Decide
(
n
ipld
.
Node
)
bool
{
return
s
.
current
.
Decide
(
n
)
}
type
exploreRecursiveContext
struct
{
edgesFound
int
}
func
(
erc
*
exploreRecursiveContext
)
Link
(
s
Selector
)
bool
{
_
,
ok
:=
s
.
(
ExploreRecursiveEdge
)
if
ok
{
erc
.
edgesFound
++
}
return
ok
}
// ParseExploreRecursive assembles a Selector from a ExploreRecursive selector node
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"
)
}
maxDepthNode
,
err
:=
n
.
TraverseField
(
maxDepthKey
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"selector spec parse rejected: maxDepth field must be present in ExploreRecursive selector"
)
}
maxDepthValue
,
err
:=
maxDepthNode
.
AsInt
()
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"selector spec parse rejected: maxDepth field must be a number in ExploreRecursive selector"
)
}
sequence
,
err
:=
n
.
TraverseField
(
sequenceKey
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"selector spec parse rejected: sequence field must be present in ExploreRecursive selector"
)
}
erc
:=
&
exploreRecursiveContext
{}
selector
,
err
:=
pc
.
PushParent
(
erc
)
.
ParseSelector
(
sequence
)
if
err
!=
nil
{
return
nil
,
err
}
if
erc
.
edgesFound
==
0
{
return
nil
,
fmt
.
Errorf
(
"selector spec parse rejected: ExploreRecursive must have at least one ExploreRecursiveEdge"
)
}
return
ExploreRecursive
{
selector
,
selector
,
maxDepthValue
},
nil
}
traversal/selector/exploreRecursiveEdge.go
0 → 100644
View file @
30cf80e2
package
selector
import
(
"fmt"
ipld
"github.com/ipld/go-ipld-prime"
)
// ExploreRecursiveEdge is a special sentinel value which is used to mark
// the end of a sequence started by an ExploreRecursive selector: the recursion
// goes back to the initial state of the earlier ExploreRecursive selector,
// and proceeds again (with a decremented maxDepth value).
//
// An ExploreRecursive selector that doesn't contain an ExploreRecursiveEdge
// is nonsensical. Containing more than one ExploreRecursiveEdge is valid.
// An ExploreRecursiveEdge without an enclosing ExploreRecursive is an error.
type
ExploreRecursiveEdge
struct
{}
// Interests should ultimately never get called for an ExploreRecursiveEdge selector
func
(
s
ExploreRecursiveEdge
)
Interests
()
[]
PathSegment
{
panic
(
"Traversed Explore Recursive Edge Node With No Parent"
)
}
// Explore should ultimately never get called for an ExploreRecursiveEdge selector
func
(
s
ExploreRecursiveEdge
)
Explore
(
n
ipld
.
Node
,
p
PathSegment
)
Selector
{
panic
(
"Traversed Explore Recursive Edge Node With No Parent"
)
}
// Decide should ultimately never get called for an ExploreRecursiveEdge selector
func
(
s
ExploreRecursiveEdge
)
Decide
(
n
ipld
.
Node
)
bool
{
panic
(
"Traversed Explore Recursive Edge Node With No Parent"
)
}
// ParseExploreRecursiveEdge assembles a Selector
// from a exploreRecursiveEdge selector node
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
_
,
parent
:=
range
pc
.
parentStack
{
if
parent
.
Link
(
s
)
{
return
s
,
nil
}
}
return
nil
,
fmt
.
Errorf
(
"selector spec parse rejected: ExploreRecursiveEdge must be beneath ExploreRecursive"
)
}
traversal/selector/exploreRecursive_test.go
0 → 100644
View file @
30cf80e2
package
selector
import
(
"bytes"
"fmt"
"testing"
"github.com/ipld/go-ipld-prime/encoding/dagjson"
"github.com/ipld/go-ipld-prime/fluent"
ipldfree
"github.com/ipld/go-ipld-prime/impl/free"
.
"github.com/warpfork/go-wish"
)
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
:=
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
:=
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
)
{
sn
:=
fnb
.
CreateMap
(
func
(
mb
fluent
.
MapBuilder
,
knb
fluent
.
NodeBuilder
,
vnb
fluent
.
NodeBuilder
)
{
mb
.
Insert
(
knb
.
CreateString
(
sequenceKey
),
vnb
.
CreateMap
(
func
(
mb
fluent
.
MapBuilder
,
knb
fluent
.
NodeBuilder
,
vnb
fluent
.
NodeBuilder
)
{
mb
.
Insert
(
knb
.
CreateString
(
matcherKey
),
vnb
.
CreateMap
(
func
(
mb
fluent
.
MapBuilder
,
knb
fluent
.
NodeBuilder
,
vnb
fluent
.
NodeBuilder
)
{}))
}))
})
_
,
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
)
{
sn
:=
fnb
.
CreateMap
(
func
(
mb
fluent
.
MapBuilder
,
knb
fluent
.
NodeBuilder
,
vnb
fluent
.
NodeBuilder
)
{
mb
.
Insert
(
knb
.
CreateString
(
maxDepthKey
),
vnb
.
CreateString
(
"cheese"
))
mb
.
Insert
(
knb
.
CreateString
(
sequenceKey
),
vnb
.
CreateMap
(
func
(
mb
fluent
.
MapBuilder
,
knb
fluent
.
NodeBuilder
,
vnb
fluent
.
NodeBuilder
)
{
mb
.
Insert
(
knb
.
CreateString
(
matcherKey
),
vnb
.
CreateMap
(
func
(
mb
fluent
.
MapBuilder
,
knb
fluent
.
NodeBuilder
,
vnb
fluent
.
NodeBuilder
)
{}))
}))
})
_
,
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
)
{
sn
:=
fnb
.
CreateMap
(
func
(
mb
fluent
.
MapBuilder
,
knb
fluent
.
NodeBuilder
,
vnb
fluent
.
NodeBuilder
)
{
mb
.
Insert
(
knb
.
CreateString
(
maxDepthKey
),
vnb
.
CreateInt
(
2
))
mb
.
Insert
(
knb
.
CreateString
(
sequenceKey
),
vnb
.
CreateInt
(
0
))
})
_
,
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
)
{
sn
:=
fnb
.
CreateMap
(
func
(
mb
fluent
.
MapBuilder
,
knb
fluent
.
NodeBuilder
,
vnb
fluent
.
NodeBuilder
)
{
mb
.
Insert
(
knb
.
CreateString
(
maxDepthKey
),
vnb
.
CreateInt
(
2
))
mb
.
Insert
(
knb
.
CreateString
(
sequenceKey
),
vnb
.
CreateMap
(
func
(
mb
fluent
.
MapBuilder
,
knb
fluent
.
NodeBuilder
,
vnb
fluent
.
NodeBuilder
)
{
mb
.
Insert
(
knb
.
CreateString
(
exploreAllKey
),
vnb
.
CreateMap
(
func
(
mb
fluent
.
MapBuilder
,
knb
fluent
.
NodeBuilder
,
vnb
fluent
.
NodeBuilder
)
{
mb
.
Insert
(
knb
.
CreateString
(
nextSelectorKey
),
vnb
.
CreateMap
(
func
(
mb
fluent
.
MapBuilder
,
knb
fluent
.
NodeBuilder
,
vnb
fluent
.
NodeBuilder
)
{
mb
.
Insert
(
knb
.
CreateString
(
matcherKey
),
vnb
.
CreateMap
(
func
(
mb
fluent
.
MapBuilder
,
knb
fluent
.
NodeBuilder
,
vnb
fluent
.
NodeBuilder
)
{}))
}))
}))
}))
})
_
,
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
:=
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
)
{
sn
:=
fnb
.
CreateMap
(
func
(
mb
fluent
.
MapBuilder
,
knb
fluent
.
NodeBuilder
,
vnb
fluent
.
NodeBuilder
)
{
mb
.
Insert
(
knb
.
CreateString
(
maxDepthKey
),
vnb
.
CreateInt
(
2
))
mb
.
Insert
(
knb
.
CreateString
(
sequenceKey
),
vnb
.
CreateMap
(
func
(
mb
fluent
.
MapBuilder
,
knb
fluent
.
NodeBuilder
,
vnb
fluent
.
NodeBuilder
)
{
mb
.
Insert
(
knb
.
CreateString
(
exploreAllKey
),
vnb
.
CreateMap
(
func
(
mb
fluent
.
MapBuilder
,
knb
fluent
.
NodeBuilder
,
vnb
fluent
.
NodeBuilder
)
{
mb
.
Insert
(
knb
.
CreateString
(
nextSelectorKey
),
vnb
.
CreateMap
(
func
(
mb
fluent
.
MapBuilder
,
knb
fluent
.
NodeBuilder
,
vnb
fluent
.
NodeBuilder
)
{
mb
.
Insert
(
knb
.
CreateString
(
exploreRecursiveEdgeKey
),
vnb
.
CreateMap
(
func
(
mb
fluent
.
MapBuilder
,
knb
fluent
.
NodeBuilder
,
vnb
fluent
.
NodeBuilder
)
{}))
}))
}))
}))
})
s
,
err
:=
ParseContext
{}
.
ParseExploreRecursive
(
sn
)
Wish
(
t
,
err
,
ShouldEqual
,
nil
)
Wish
(
t
,
s
,
ShouldEqual
,
ExploreRecursive
{
ExploreAll
{
ExploreRecursiveEdge
{}},
ExploreAll
{
ExploreRecursiveEdge
{}},
2
})
})
}
/*
{
exploreRecursive: {
maxDepth: 3
sequence: {
exploreFields: {
fields: {
Parents: {
exploreAll: {
exploreRecursiveEdge: {}
}
}
}
}
}
}
}
*/
func
TestExploreRecursiveExplore
(
t
*
testing
.
T
)
{
recursiveEdge
:=
ExploreRecursiveEdge
{}
maxDepth
:=
3
var
rs
Selector
t
.
Run
(
"exploring should traverse until we get to maxDepth"
,
func
(
t
*
testing
.
T
)
{
parentsSelector
:=
ExploreAll
{
recursiveEdge
}
subTree
:=
ExploreFields
{
map
[
string
]
Selector
{
"Parents"
:
parentsSelector
},
[]
PathSegment
{
PathSegmentString
{
S
:
"Parents"
}}}
rs
=
ExploreRecursive
{
subTree
,
subTree
,
maxDepth
}
nodeString
:=
`{
"Parents": [
{
"Parents": [
{
"Parents": [
{
"Parents": []
}
]
}
]
}
]
}
`
rn
,
err
:=
dagjson
.
Decoder
(
ipldfree
.
NodeBuilder
(),
bytes
.
NewBufferString
(
nodeString
))
Wish
(
t
,
err
,
ShouldEqual
,
nil
)
rs
=
rs
.
Explore
(
rn
,
PathSegmentString
{
S
:
"Parents"
})
rn
,
err
=
rn
.
TraverseField
(
"Parents"
)
Wish
(
t
,
rs
,
ShouldEqual
,
ExploreRecursive
{
subTree
,
parentsSelector
,
maxDepth
})
Wish
(
t
,
err
,
ShouldEqual
,
nil
)
rs
=
rs
.
Explore
(
rn
,
PathSegmentInt
{
I
:
0
})
rn
,
err
=
rn
.
TraverseIndex
(
0
)
Wish
(
t
,
rs
,
ShouldEqual
,
ExploreRecursive
{
subTree
,
subTree
,
maxDepth
-
1
})
Wish
(
t
,
err
,
ShouldEqual
,
nil
)
rs
=
rs
.
Explore
(
rn
,
PathSegmentString
{
S
:
"Parents"
})
rn
,
err
=
rn
.
TraverseField
(
"Parents"
)
Wish
(
t
,
rs
,
ShouldEqual
,
ExploreRecursive
{
subTree
,
parentsSelector
,
maxDepth
-
1
})
Wish
(
t
,
err
,
ShouldEqual
,
nil
)
rs
=
rs
.
Explore
(
rn
,
PathSegmentInt
{
I
:
0
})
rn
,
err
=
rn
.
TraverseIndex
(
0
)
Wish
(
t
,
rs
,
ShouldEqual
,
ExploreRecursive
{
subTree
,
subTree
,
maxDepth
-
2
})
Wish
(
t
,
err
,
ShouldEqual
,
nil
)
rs
=
rs
.
Explore
(
rn
,
PathSegmentString
{
S
:
"Parents"
})
rn
,
err
=
rn
.
TraverseField
(
"Parents"
)
Wish
(
t
,
rs
,
ShouldEqual
,
ExploreRecursive
{
subTree
,
parentsSelector
,
maxDepth
-
2
})
Wish
(
t
,
err
,
ShouldEqual
,
nil
)
rs
=
rs
.
Explore
(
rn
,
PathSegmentInt
{
I
:
0
})
rn
,
err
=
rn
.
TraverseIndex
(
0
)
Wish
(
t
,
rs
,
ShouldEqual
,
nil
)
Wish
(
t
,
err
,
ShouldEqual
,
nil
)
})
t
.
Run
(
"exploring should continue till we get to selector that returns nil on explore"
,
func
(
t
*
testing
.
T
)
{
parentsSelector
:=
ExploreIndex
{
recursiveEdge
,
[
1
]
PathSegment
{
PathSegmentInt
{
I
:
1
}}}
subTree
:=
ExploreFields
{
map
[
string
]
Selector
{
"Parents"
:
parentsSelector
},
[]
PathSegment
{
PathSegmentString
{
S
:
"Parents"
}}}
rs
=
ExploreRecursive
{
subTree
,
subTree
,
maxDepth
}
nodeString
:=
`{
"Parents": {
}
}
`
rn
,
err
:=
dagjson
.
Decoder
(
ipldfree
.
NodeBuilder
(),
bytes
.
NewBufferString
(
nodeString
))
Wish
(
t
,
err
,
ShouldEqual
,
nil
)
rs
=
rs
.
Explore
(
rn
,
PathSegmentString
{
S
:
"Parents"
})
rn
,
err
=
rn
.
TraverseField
(
"Parents"
)
Wish
(
t
,
rs
,
ShouldEqual
,
ExploreRecursive
{
subTree
,
parentsSelector
,
maxDepth
})
Wish
(
t
,
err
,
ShouldEqual
,
nil
)
rs
=
rs
.
Explore
(
rn
,
PathSegmentInt
{
I
:
0
})
Wish
(
t
,
rs
,
ShouldEqual
,
nil
)
})
t
.
Run
(
"exploring should work when there is nested recursion"
,
func
(
t
*
testing
.
T
)
{
parentsSelector
:=
ExploreAll
{
recursiveEdge
}
sideSelector
:=
ExploreAll
{
recursiveEdge
}
subTree
:=
ExploreFields
{
map
[
string
]
Selector
{
"Parents"
:
parentsSelector
,
"Side"
:
ExploreRecursive
{
sideSelector
,
sideSelector
,
maxDepth
},
},
[]
PathSegment
{
PathSegmentString
{
S
:
"Parents"
},
PathSegmentString
{
S
:
"Side"
},
},
}
s
:=
ExploreRecursive
{
subTree
,
subTree
,
maxDepth
}
nodeString
:=
`{
"Parents": [
{
"Parents": [],
"Side": {
"cheese": {
"whiz": {
}
}
}
}
],
"Side": {
"real": {
"apple": {
"sauce": {
}
}
}
}
}
`
n
,
err
:=
dagjson
.
Decoder
(
ipldfree
.
NodeBuilder
(),
bytes
.
NewBufferString
(
nodeString
))
Wish
(
t
,
err
,
ShouldEqual
,
nil
)
// traverse down Parent nodes
rn
:=
n
rs
=
s
rs
=
rs
.
Explore
(
rn
,
PathSegmentString
{
S
:
"Parents"
})
rn
,
err
=
rn
.
TraverseField
(
"Parents"
)
Wish
(
t
,
rs
,
ShouldEqual
,
ExploreRecursive
{
subTree
,
parentsSelector
,
maxDepth
})
Wish
(
t
,
err
,
ShouldEqual
,
nil
)
rs
=
rs
.
Explore
(
rn
,
PathSegmentInt
{
I
:
0
})
rn
,
err
=
rn
.
TraverseIndex
(
0
)
Wish
(
t
,
rs
,
ShouldEqual
,
ExploreRecursive
{
subTree
,
subTree
,
maxDepth
-
1
})
Wish
(
t
,
err
,
ShouldEqual
,
nil
)
rs
=
rs
.
Explore
(
rn
,
PathSegmentString
{
S
:
"Parents"
})
rn
,
err
=
rn
.
TraverseField
(
"Parents"
)
Wish
(
t
,
rs
,
ShouldEqual
,
ExploreRecursive
{
subTree
,
parentsSelector
,
maxDepth
-
1
})
Wish
(
t
,
err
,
ShouldEqual
,
nil
)
// traverse down top level Side tree (nested recursion)
rn
=
n
rs
=
s
rs
=
rs
.
Explore
(
rn
,
PathSegmentString
{
S
:
"Side"
})
rn
,
err
=
rn
.
TraverseField
(
"Side"
)
Wish
(
t
,
rs
,
ShouldEqual
,
ExploreRecursive
{
subTree
,
ExploreRecursive
{
sideSelector
,
sideSelector
,
maxDepth
},
maxDepth
})
Wish
(
t
,
err
,
ShouldEqual
,
nil
)
rs
=
rs
.
Explore
(
rn
,
PathSegmentString
{
S
:
"real"
})
rn
,
err
=
rn
.
TraverseField
(
"real"
)
Wish
(
t
,
rs
,
ShouldEqual
,
ExploreRecursive
{
subTree
,
ExploreRecursive
{
sideSelector
,
sideSelector
,
maxDepth
-
1
},
maxDepth
})
Wish
(
t
,
err
,
ShouldEqual
,
nil
)
rs
=
rs
.
Explore
(
rn
,
PathSegmentString
{
S
:
"apple"
})
rn
,
err
=
rn
.
TraverseField
(
"apple"
)
Wish
(
t
,
rs
,
ShouldEqual
,
ExploreRecursive
{
subTree
,
ExploreRecursive
{
sideSelector
,
sideSelector
,
maxDepth
-
2
},
maxDepth
})
Wish
(
t
,
err
,
ShouldEqual
,
nil
)
rs
=
rs
.
Explore
(
rn
,
PathSegmentString
{
S
:
"sauce"
})
rn
,
err
=
rn
.
TraverseField
(
"sauce"
)
Wish
(
t
,
rs
,
ShouldEqual
,
nil
)
Wish
(
t
,
err
,
ShouldEqual
,
nil
)
// traverse once down Parent (top level recursion) then down Side tree (nested recursion)
rn
=
n
rs
=
s
rs
=
rs
.
Explore
(
rn
,
PathSegmentString
{
S
:
"Parents"
})
rn
,
err
=
rn
.
TraverseField
(
"Parents"
)
Wish
(
t
,
rs
,
ShouldEqual
,
ExploreRecursive
{
subTree
,
parentsSelector
,
maxDepth
})
Wish
(
t
,
err
,
ShouldEqual
,
nil
)
rs
=
rs
.
Explore
(
rn
,
PathSegmentInt
{
I
:
0
})
rn
,
err
=
rn
.
TraverseIndex
(
0
)
Wish
(
t
,
rs
,
ShouldEqual
,
ExploreRecursive
{
subTree
,
subTree
,
maxDepth
-
1
})
Wish
(
t
,
err
,
ShouldEqual
,
nil
)
rs
=
rs
.
Explore
(
rn
,
PathSegmentString
{
S
:
"Side"
})
rn
,
err
=
rn
.
TraverseField
(
"Side"
)
Wish
(
t
,
rs
,
ShouldEqual
,
ExploreRecursive
{
subTree
,
ExploreRecursive
{
sideSelector
,
sideSelector
,
maxDepth
},
maxDepth
-
1
})
Wish
(
t
,
err
,
ShouldEqual
,
nil
)
rs
=
rs
.
Explore
(
rn
,
PathSegmentString
{
S
:
"cheese"
})
rn
,
err
=
rn
.
TraverseField
(
"cheese"
)
Wish
(
t
,
rs
,
ShouldEqual
,
ExploreRecursive
{
subTree
,
ExploreRecursive
{
sideSelector
,
sideSelector
,
maxDepth
-
1
},
maxDepth
-
1
})
Wish
(
t
,
err
,
ShouldEqual
,
nil
)
rs
=
rs
.
Explore
(
rn
,
PathSegmentString
{
S
:
"whiz"
})
rn
,
err
=
rn
.
TraverseField
(
"whiz"
)
Wish
(
t
,
rs
,
ShouldEqual
,
ExploreRecursive
{
subTree
,
ExploreRecursive
{
sideSelector
,
sideSelector
,
maxDepth
-
2
},
maxDepth
-
1
})
Wish
(
t
,
err
,
ShouldEqual
,
nil
)
})
}
traversal/selector/exploreUnion.go
View file @
30cf80e2
...
...
@@ -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
)
(
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) (Selector, error) {
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"error during selector spec parse: %s"
,
err
)
}
member
,
err
:=
ParseSelector
(
v
)
member
,
err
:=
pc
.
ParseSelector
(
v
)
if
err
!=
nil
{
return
nil
,
err
}
...
...
traversal/selector/exploreUnion_test.go
View file @
30cf80e2
...
...
@@ -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
}}}}})
})
...
...
traversal/selector/matcher.go
View file @
30cf80e2
...
...
@@ -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
)
(
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"
)
}
...
...
traversal/selector/selector.go
View file @
30cf80e2
...
...
@@ -15,8 +15,24 @@ type Selector interface {
Decide
(
ipld
.
Node
)
bool
}
// 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
)
(
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"
)
}
...
...
@@ -29,22 +45,35 @@ func ParseSelector(n ipld.Node) (Selector, error) {
// (This switch is where the keyed union discriminators concretely happen.)
switch
kstr
{
case
exploreFieldsKey
:
return
ParseExploreFields
(
v
)
return
pc
.
ParseExploreFields
(
v
)
case
exploreAllKey
:
return
ParseExploreAll
(
v
)
return
pc
.
ParseExploreAll
(
v
)
case
exploreIndexKey
:
return
ParseExploreIndex
(
v
)
return
pc
.
ParseExploreIndex
(
v
)
case
exploreRangeKey
:
return
ParseExploreRange
(
v
)
return
pc
.
ParseExploreRange
(
v
)
case
exploreUnionKey
:
return
ParseExploreUnion
(
v
)
return
pc
.
ParseExploreUnion
(
v
)
case
exploreRecursiveKey
:
return
pc
.
ParseExploreRecursive
(
v
)
case
exploreRecursiveEdgeKey
:
return
pc
.
ParseExploreRecursiveEdge
(
v
)
case
matcherKey
:
return
ParseMatcher
(
v
)
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
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment