Compare commits
11 Commits
envelope
...
v1-fix-pol
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7376256672 | ||
|
|
ad03154b6e | ||
|
|
700f130858 | ||
|
|
d57d2a230b | ||
|
|
7060d4bb33 | ||
|
|
a183b627be | ||
|
|
dbfff3f70c | ||
|
|
cb45d9019b | ||
|
|
2e17ff8550 | ||
|
|
e86e45be73 | ||
|
|
97c9990045 |
@@ -16,7 +16,9 @@ func TestIpldRoundTrip(t *testing.T) {
|
|||||||
["any", ".tags",
|
["any", ".tags",
|
||||||
["or", [
|
["or", [
|
||||||
["==", ".", "news"],
|
["==", ".", "news"],
|
||||||
["==", ".", "press"]]]
|
["==", ".", "press"]
|
||||||
|
]]
|
||||||
|
]
|
||||||
]`
|
]`
|
||||||
|
|
||||||
for _, tc := range []struct {
|
for _, tc := range []struct {
|
||||||
|
|||||||
@@ -26,11 +26,22 @@ func matchStatement(statement Statement, node ipld.Node) bool {
|
|||||||
switch statement.Kind() {
|
switch statement.Kind() {
|
||||||
case KindEqual:
|
case KindEqual:
|
||||||
if s, ok := statement.(equality); ok {
|
if s, ok := statement.(equality); ok {
|
||||||
one, _, err := selector.Select(s.selector, node)
|
one, many, err := selector.Select(s.selector, node)
|
||||||
if err != nil || one == nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return datamodel.DeepEqual(s.value, one)
|
if one != nil {
|
||||||
|
return datamodel.DeepEqual(s.value, one)
|
||||||
|
}
|
||||||
|
if many != nil {
|
||||||
|
for _, n := range many {
|
||||||
|
if eq := datamodel.DeepEqual(s.value, n); eq {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
case KindGreaterThan:
|
case KindGreaterThan:
|
||||||
if s, ok := statement.(equality); ok {
|
if s, ok := statement.(equality); ok {
|
||||||
@@ -119,17 +130,25 @@ func matchStatement(statement Statement, node ipld.Node) bool {
|
|||||||
}
|
}
|
||||||
case KindAny:
|
case KindAny:
|
||||||
if s, ok := statement.(quantifier); ok {
|
if s, ok := statement.(quantifier); ok {
|
||||||
// FIXME: line below return a single node, not many
|
one, many, err := selector.Select(s.selector, node)
|
||||||
_, many, err := selector.Select(s.selector, node)
|
if err != nil {
|
||||||
if err != nil || many == nil {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
for _, n := range many {
|
if one != nil {
|
||||||
ok := matchStatement(s.statement, n)
|
ok := matchStatement(s.statement, one)
|
||||||
if ok {
|
if ok {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if many != nil {
|
||||||
|
for _, n := range many {
|
||||||
|
ok := matchStatement(s.statement, n)
|
||||||
|
if ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
|
|
||||||
"github.com/ipld/go-ipld-prime"
|
"github.com/ipld/go-ipld-prime"
|
||||||
"github.com/ipld/go-ipld-prime/datamodel"
|
"github.com/ipld/go-ipld-prime/datamodel"
|
||||||
|
"github.com/ipld/go-ipld-prime/node/basicnode"
|
||||||
"github.com/ipld/go-ipld-prime/schema"
|
"github.com/ipld/go-ipld-prime/schema"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -86,126 +87,292 @@ func resolve(sel Selector, subject ipld.Node, at []string) (ipld.Node, []ipld.No
|
|||||||
for i, seg := range sel {
|
for i, seg := range sel {
|
||||||
if seg.Identity() {
|
if seg.Identity() {
|
||||||
continue
|
continue
|
||||||
} else if seg.Iterator() {
|
}
|
||||||
if cur != nil && cur.Kind() == datamodel.Kind_List {
|
|
||||||
var many []ipld.Node
|
|
||||||
it := cur.ListIterator()
|
|
||||||
for {
|
|
||||||
if it.Done() {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
k, v, err := it.Next()
|
// 1st level: handle the different segment types (iterator, field, slice, index)
|
||||||
|
// 2nd level: handle different node kinds (list, map, string, bytes)
|
||||||
|
switch {
|
||||||
|
case seg.Iterator():
|
||||||
|
if cur == nil || cur.Kind() == datamodel.Kind_Null {
|
||||||
|
if seg.Optional() {
|
||||||
|
// build empty list
|
||||||
|
nb := basicnode.Prototype.List.NewBuilder()
|
||||||
|
assembler, err := nb.BeginList(0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
key := fmt.Sprintf("%d", k)
|
if err = assembler.Finish(); err != nil {
|
||||||
o, m, err := resolve(sel[i+1:], v, append(at[:], key))
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if m != nil {
|
return nb.Build(), nil, nil
|
||||||
many = append(many, m...)
|
} else {
|
||||||
} else {
|
return nil, nil, newResolutionError(fmt.Sprintf("can not iterate over kind: %s", kindString(cur)), at)
|
||||||
many = append(many, o)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return nil, many, nil
|
|
||||||
} else if cur != nil && cur.Kind() == datamodel.Kind_Map {
|
|
||||||
var many []ipld.Node
|
|
||||||
it := cur.MapIterator()
|
|
||||||
for {
|
|
||||||
if it.Done() {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
k, v, err := it.Next()
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
key, _ := k.AsString()
|
|
||||||
o, m, err := resolve(sel[i+1:], v, append(at[:], key))
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if m != nil {
|
|
||||||
many = append(many, m...)
|
|
||||||
} else {
|
|
||||||
many = append(many, o)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil, many, nil
|
|
||||||
} else if seg.Optional() {
|
|
||||||
cur = nil
|
|
||||||
} else {
|
} else {
|
||||||
return nil, nil, newResolutionError(fmt.Sprintf("can not iterate over kind: %s", kindString(cur)), at)
|
var many []ipld.Node
|
||||||
|
switch cur.Kind() {
|
||||||
|
case datamodel.Kind_List:
|
||||||
|
it := cur.ListIterator()
|
||||||
|
for !it.Done() {
|
||||||
|
_, v, err := it.Next()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if there are more iterator segments
|
||||||
|
if len(sel) > i+1 && sel[i+1].Iterator() {
|
||||||
|
if v.Kind() == datamodel.Kind_List {
|
||||||
|
// recursively resolve the remaining selector segments
|
||||||
|
var o ipld.Node
|
||||||
|
var m []ipld.Node
|
||||||
|
o, m, err = resolve(sel[i+1:], v, at)
|
||||||
|
if err != nil {
|
||||||
|
// if the segment is optional and an error occurs, skip the current iteration.
|
||||||
|
if seg.Optional() {
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if m != nil {
|
||||||
|
many = append(many, m...)
|
||||||
|
} else if o != nil {
|
||||||
|
many = append(many, o)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// if the current value is not a list and the next segment is optional, skip the current iteration
|
||||||
|
if sel[i+1].Optional() {
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
return nil, nil, newResolutionError(fmt.Sprintf("can not iterate over kind: %s", kindString(v)), at)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// if there are no more iterator segments, append the current value to the result
|
||||||
|
many = append(many, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case datamodel.Kind_Map:
|
||||||
|
it := cur.MapIterator()
|
||||||
|
for !it.Done() {
|
||||||
|
_, v, err := it.Next()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(sel) > i+1 && sel[i+1].Iterator() {
|
||||||
|
if v.Kind() == datamodel.Kind_List {
|
||||||
|
var o ipld.Node
|
||||||
|
var m []ipld.Node
|
||||||
|
o, m, err = resolve(sel[i+1:], v, at)
|
||||||
|
if err != nil {
|
||||||
|
if seg.Optional() {
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if m != nil {
|
||||||
|
many = append(many, m...)
|
||||||
|
} else if o != nil {
|
||||||
|
many = append(many, o)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if sel[i+1].Optional() {
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
return nil, nil, newResolutionError(fmt.Sprintf("can not iterate over kind: %s", kindString(v)), at)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
many = append(many, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return nil, nil, newResolutionError(fmt.Sprintf("can not iterate over kind: %s", kindString(cur)), at)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, many, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if seg.Field() != "" {
|
case seg.Field() != "":
|
||||||
at = append(at, seg.Field())
|
at = append(at, seg.Field())
|
||||||
if cur != nil && cur.Kind() == datamodel.Kind_Map {
|
if cur == nil {
|
||||||
n, err := cur.LookupByString(seg.Field())
|
if seg.Optional() {
|
||||||
if err != nil {
|
cur = nil
|
||||||
if isMissing(err) {
|
} else {
|
||||||
if seg.Optional() {
|
return nil, nil, newResolutionError(fmt.Sprintf("can not access field: %s on kind: %s", seg.Field(), kindString(cur)), at)
|
||||||
cur = nil
|
}
|
||||||
|
} else {
|
||||||
|
switch cur.Kind() {
|
||||||
|
case datamodel.Kind_Map:
|
||||||
|
n, err := cur.LookupByString(seg.Field())
|
||||||
|
if err != nil {
|
||||||
|
if isMissing(err) {
|
||||||
|
if seg.Optional() {
|
||||||
|
cur = nil
|
||||||
|
} else {
|
||||||
|
return nil, nil, newResolutionError(fmt.Sprintf("object has no field named: %s", seg.Field()), at)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return nil, nil, newResolutionError(fmt.Sprintf("object has no field named: %s", seg.Field()), at)
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return nil, nil, err
|
cur = n
|
||||||
|
}
|
||||||
|
case datamodel.Kind_List:
|
||||||
|
var many []ipld.Node
|
||||||
|
it := cur.ListIterator()
|
||||||
|
for !it.Done() {
|
||||||
|
_, v, err := it.Next()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if v.Kind() == datamodel.Kind_Map {
|
||||||
|
n, err := v.LookupByString(seg.Field())
|
||||||
|
if err == nil {
|
||||||
|
many = append(many, n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(many) > 0 {
|
||||||
|
cur = nil
|
||||||
|
return nil, many, nil
|
||||||
|
} else if seg.Optional() {
|
||||||
|
cur = nil
|
||||||
|
} else {
|
||||||
|
return nil, nil, newResolutionError(fmt.Sprintf("no elements in list have field named: %s", seg.Field()), at)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
if seg.Optional() {
|
||||||
|
cur = nil
|
||||||
|
} else {
|
||||||
|
return nil, nil, newResolutionError(fmt.Sprintf("can not access field: %s on kind: %s", seg.Field(), kindString(cur)), at)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cur = n
|
|
||||||
} else if seg.Optional() {
|
|
||||||
cur = nil
|
|
||||||
} else {
|
|
||||||
return nil, nil, newResolutionError(fmt.Sprintf("can not access field: %s on kind: %s", seg.Field(), kindString(cur)), at)
|
|
||||||
}
|
}
|
||||||
} else if seg.Slice() != nil {
|
|
||||||
if cur != nil && cur.Kind() == datamodel.Kind_List {
|
case seg.Slice() != nil:
|
||||||
return nil, nil, newResolutionError("list slice selection not yet implemented", at)
|
if cur == nil {
|
||||||
} else if cur != nil && cur.Kind() == datamodel.Kind_Bytes {
|
if seg.Optional() {
|
||||||
return nil, nil, newResolutionError("bytes slice selection not yet implemented", at)
|
cur = nil
|
||||||
} else if seg.Optional() {
|
} else {
|
||||||
cur = nil
|
return nil, nil, newResolutionError(fmt.Sprintf("can not slice on kind: %s", kindString(cur)), at)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return nil, nil, newResolutionError(fmt.Sprintf("can not index: %s on kind: %s", seg.Field(), kindString(cur)), at)
|
slice := seg.Slice()
|
||||||
|
var start, end, length int64
|
||||||
|
switch cur.Kind() {
|
||||||
|
case datamodel.Kind_List:
|
||||||
|
length = cur.Length()
|
||||||
|
start, end = resolveSliceIndices(slice, length)
|
||||||
|
case datamodel.Kind_Bytes:
|
||||||
|
b, _ := cur.AsBytes()
|
||||||
|
length = int64(len(b))
|
||||||
|
start, end = resolveSliceIndices(slice, length)
|
||||||
|
case datamodel.Kind_String:
|
||||||
|
str, _ := cur.AsString()
|
||||||
|
length = int64(len(str))
|
||||||
|
start, end = resolveSliceIndices(slice, length)
|
||||||
|
default:
|
||||||
|
return nil, nil, newResolutionError(fmt.Sprintf("can not slice on kind: %s", kindString(cur)), at)
|
||||||
|
}
|
||||||
|
|
||||||
|
if start < 0 || end < start || end > length {
|
||||||
|
if seg.Optional() {
|
||||||
|
cur = nil
|
||||||
|
} else {
|
||||||
|
return nil, nil, newResolutionError(fmt.Sprintf("slice out of bounds: [%d:%d]", start, end), at)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch cur.Kind() {
|
||||||
|
case datamodel.Kind_List:
|
||||||
|
if end > cur.Length() {
|
||||||
|
end = cur.Length()
|
||||||
|
}
|
||||||
|
nb := basicnode.Prototype.List.NewBuilder()
|
||||||
|
assembler, _ := nb.BeginList(int64(end - start))
|
||||||
|
for i := start; i < end; i++ {
|
||||||
|
item, _ := cur.LookupByIndex(int64(i))
|
||||||
|
assembler.AssembleValue().AssignNode(item)
|
||||||
|
}
|
||||||
|
assembler.Finish()
|
||||||
|
cur = nb.Build()
|
||||||
|
case datamodel.Kind_Bytes:
|
||||||
|
b, _ := cur.AsBytes()
|
||||||
|
l := int64(len(b))
|
||||||
|
if end > l {
|
||||||
|
end = l
|
||||||
|
}
|
||||||
|
cur = basicnode.NewBytes(b[start:end])
|
||||||
|
case datamodel.Kind_String:
|
||||||
|
str, _ := cur.AsString()
|
||||||
|
l := int64(len(str))
|
||||||
|
if end > l {
|
||||||
|
end = l
|
||||||
|
}
|
||||||
|
cur = basicnode.NewString(str[start:end])
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
|
default:
|
||||||
at = append(at, fmt.Sprintf("%d", seg.Index()))
|
at = append(at, fmt.Sprintf("%d", seg.Index()))
|
||||||
if cur != nil && cur.Kind() == datamodel.Kind_List {
|
if cur == nil {
|
||||||
idx := int64(seg.Index())
|
if seg.Optional() {
|
||||||
if idx < 0 {
|
cur = nil
|
||||||
idx = cur.Length() + idx
|
} else {
|
||||||
|
return nil, nil, newResolutionError(fmt.Sprintf("can not access index: %d on kind: %s", seg.Index(), kindString(cur)), at)
|
||||||
}
|
}
|
||||||
if idx < 0 {
|
} else {
|
||||||
// necessary until https://github.com/ipld/go-ipld-prime/pull/571
|
idx := seg.Index()
|
||||||
// after, isMissing() below will work
|
switch cur.Kind() {
|
||||||
// TODO: remove
|
case datamodel.Kind_List:
|
||||||
return nil, nil, newResolutionError(fmt.Sprintf("index out of bounds: %d", seg.Index()), at)
|
if idx < 0 {
|
||||||
}
|
idx = int(cur.Length()) + idx
|
||||||
n, err := cur.LookupByIndex(idx)
|
}
|
||||||
if err != nil {
|
if idx < 0 || idx >= int(cur.Length()) {
|
||||||
if isMissing(err) {
|
|
||||||
if seg.Optional() {
|
if seg.Optional() {
|
||||||
cur = nil
|
cur = nil
|
||||||
} else {
|
} else {
|
||||||
return nil, nil, newResolutionError(fmt.Sprintf("index out of bounds: %d", seg.Index()), at)
|
return nil, nil, newResolutionError(fmt.Sprintf("index out of bounds: %d", seg.Index()), at)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return nil, nil, err
|
cur, _ = cur.LookupByIndex(int64(idx))
|
||||||
}
|
}
|
||||||
|
case datamodel.Kind_String:
|
||||||
|
str, _ := cur.AsString()
|
||||||
|
if idx < 0 {
|
||||||
|
idx = len(str) + idx
|
||||||
|
}
|
||||||
|
if idx < 0 || idx >= len(str) {
|
||||||
|
if seg.Optional() {
|
||||||
|
cur = nil
|
||||||
|
} else {
|
||||||
|
return nil, nil, newResolutionError(fmt.Sprintf("index out of bounds: %d", seg.Index()), at)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cur = basicnode.NewString(string(str[idx]))
|
||||||
|
}
|
||||||
|
case datamodel.Kind_Bytes:
|
||||||
|
b, _ := cur.AsBytes()
|
||||||
|
if idx < 0 {
|
||||||
|
idx = len(b) + idx
|
||||||
|
}
|
||||||
|
if idx < 0 || idx >= len(b) {
|
||||||
|
if seg.Optional() {
|
||||||
|
cur = nil
|
||||||
|
} else {
|
||||||
|
return nil, nil, newResolutionError(fmt.Sprintf("index out of bounds: %d", seg.Index()), at)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cur = basicnode.NewInt(int64(b[idx]))
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return nil, nil, newResolutionError(fmt.Sprintf("can not access index: %d on kind: %s", seg.Index(), kindString(cur)), at)
|
||||||
}
|
}
|
||||||
cur = n
|
|
||||||
} else if seg.Optional() {
|
|
||||||
cur = nil
|
|
||||||
} else {
|
|
||||||
return nil, nil, newResolutionError(fmt.Sprintf("can not access field: %s on kind: %s", seg.Field(), kindString(cur)), at)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -213,6 +380,42 @@ func resolve(sel Selector, subject ipld.Node, at []string) (ipld.Node, []ipld.No
|
|||||||
return cur, nil, nil
|
return cur, nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// resolveSliceIndices resolves the start and end indices for slicing a list or byte array.
|
||||||
|
//
|
||||||
|
// It takes the slice indices from the selector segment and the length of the list or byte array,
|
||||||
|
// and returns the resolved start and end indices. Negative indices are supported.
|
||||||
|
//
|
||||||
|
// Parameters:
|
||||||
|
// - slice: The slice indices from the selector segment.
|
||||||
|
// - length: The length of the list or byte array being sliced.
|
||||||
|
//
|
||||||
|
// Returns:
|
||||||
|
// - start: The resolved start index for slicing.
|
||||||
|
// - end: The resolved end index for slicing.
|
||||||
|
func resolveSliceIndices(slice []int, length int64) (int64, int64) {
|
||||||
|
start, end := int64(0), length
|
||||||
|
if len(slice) > 0 {
|
||||||
|
start = int64(slice[0])
|
||||||
|
if start < 0 {
|
||||||
|
start = length + start
|
||||||
|
if start < 0 {
|
||||||
|
start = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(slice) > 1 {
|
||||||
|
end = int64(slice[1])
|
||||||
|
if end <= 0 {
|
||||||
|
end = length + end
|
||||||
|
if end < start {
|
||||||
|
end = start
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return start, end
|
||||||
|
}
|
||||||
|
|
||||||
func kindString(n datamodel.Node) string {
|
func kindString(n datamodel.Node) string {
|
||||||
if n == nil {
|
if n == nil {
|
||||||
return "null"
|
return "null"
|
||||||
|
|||||||
@@ -30,14 +30,14 @@ func TestSupportedForms(t *testing.T) {
|
|||||||
for _, testcase := range []Testcase{
|
for _, testcase := range []Testcase{
|
||||||
{Name: "Identity", Selector: `.`, Input: `{"x":1}`, Output: `{"x":1}`},
|
{Name: "Identity", Selector: `.`, Input: `{"x":1}`, Output: `{"x":1}`},
|
||||||
{Name: "Iterator", Selector: `.[]`, Input: `[1, 2]`, Output: `[1, 2]`},
|
{Name: "Iterator", Selector: `.[]`, Input: `[1, 2]`, Output: `[1, 2]`},
|
||||||
{Name: "Optional Null Iterator", Selector: `.[]?`, Input: `null`, Output: `()`},
|
{Name: "Optional Null Iterator", Selector: `.[]?`, Input: `null`, Output: `[]`},
|
||||||
{Name: "Optional Iterator", Selector: `.[][]?`, Input: `[[1], 2, [3]]`, Output: `[1, 3]`},
|
{Name: "Optional Iterator", Selector: `.[][]?`, Input: `[[1], 2, [3]]`, Output: `[1, 3]`},
|
||||||
{Name: "Object Key", Selector: `.x`, Input: `{"x": 1 }`, Output: `1`},
|
{Name: "Object Key", Selector: `.x`, Input: `{"x": 1 }`, Output: `1`},
|
||||||
{Name: "Quoted Key", Selector: `.["x"]`, Input: `{"x": 1}`, Output: `1`},
|
{Name: "Quoted Key", Selector: `.["x"]`, Input: `{"x": 1}`, Output: `1`},
|
||||||
{Name: "Index", Selector: `.[0]`, Input: `[1, 2]`, Output: `1`},
|
{Name: "Index", Selector: `.[0]`, Input: `[1, 2]`, Output: `1`},
|
||||||
{Name: "Negative Index", Selector: `.[-1]`, Input: `[1, 2]`, Output: `2`},
|
{Name: "Negative Index", Selector: `.[-1]`, Input: `[1, 2]`, Output: `2`},
|
||||||
{Name: "String Index", Selector: `.[0]`, Input: `"Hi"`, Output: `"H"`},
|
{Name: "String Index", Selector: `.[0]`, Input: `"Hi"`, Output: `"H"`},
|
||||||
{Name: "Bytes Index", Selector: `.[0]`, Input: `{"/":{"bytes":"AAE"}`, Output: `0`},
|
{Name: "Bytes Index", Selector: `.[0]`, Input: `{"/":{"bytes":"AAE"}}`, Output: `0`},
|
||||||
{Name: "Array Slice", Selector: `.[0:2]`, Input: `[0, 1, 2]`, Output: `[0, 1]`},
|
{Name: "Array Slice", Selector: `.[0:2]`, Input: `[0, 1, 2]`, Output: `[0, 1]`},
|
||||||
{Name: "Array Slice", Selector: `.[1:]`, Input: `[0, 1, 2]`, Output: `[1, 2]`},
|
{Name: "Array Slice", Selector: `.[1:]`, Input: `[0, 1, 2]`, Output: `[1, 2]`},
|
||||||
{Name: "Array Slice", Selector: `.[:2]`, Input: `[0, 1, 2]`, Output: `[0, 1]`},
|
{Name: "Array Slice", Selector: `.[:2]`, Input: `[0, 1, 2]`, Output: `[0, 1]`},
|
||||||
|
|||||||
Reference in New Issue
Block a user