refactoring resolve func

This commit is contained in:
Fabio Bozzo
2024-09-13 14:44:26 +02:00
parent cb45d9019b
commit dbfff3f70c

View File

@@ -87,68 +87,77 @@ 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)
if err != nil { // 2nd level: handle different node kinds (list, map, string, bytes)
return nil, nil, err switch {
} case seg.Iterator():
if cur == nil {
key := fmt.Sprintf("%d", k) if seg.Optional() {
o, m, err := resolve(sel[i+1:], v, append(at[:], key)) cur = nil
if err != nil { } else {
return nil, nil, err return nil, nil, newResolutionError(fmt.Sprintf("can not iterate over kind: %s", kindString(cur)), at)
}
if m != nil {
many = append(many, m...)
} else {
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() {
k, v, err := it.Next()
if err != nil {
return nil, nil, err
}
key := fmt.Sprintf("%d", k)
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)
}
}
case datamodel.Kind_Map:
it := cur.MapIterator()
for !it.Done() {
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)
}
}
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 || cur.Kind() != datamodel.Kind_Map {
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 {
n, err := cur.LookupByString(seg.Field()) n, err := cur.LookupByString(seg.Field())
if err != nil { if err != nil {
if isMissing(err) { if isMissing(err) {
@@ -160,138 +169,114 @@ func resolve(sel Selector, subject ipld.Node, at []string) (ipld.Node, []ipld.No
} else { } else {
return nil, nil, err return nil, nil, err
} }
} else {
cur = n
} }
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:
if cur == nil {
if seg.Optional() {
cur = nil
} else {
return nil, nil, newResolutionError(fmt.Sprintf("can not slice on kind: %s", kindString(cur)), at)
}
} else {
slice := seg.Slice() slice := seg.Slice()
start, end := int64(0), cur.Length() var start, end int64
switch cur.Kind() {
if len(slice) > 0 { case datamodel.Kind_List:
start = int64(slice[0]) start, end = resolveSliceIndices(slice, cur.Length())
if start < 0 { case datamodel.Kind_Bytes:
start = cur.Length() + start b, _ := cur.AsBytes()
if start < 0 { start, end = resolveSliceIndices(slice, int64(len(b)))
start = 0 default:
} return nil, nil, newResolutionError(fmt.Sprintf("can not slice on kind: %s", kindString(cur)), at)
}
} }
if len(slice) > 1 { if start < 0 || end < start || end > cur.Length() {
end = int64(slice[1])
if end <= 0 {
end = cur.Length() + end
if end < start {
end = start
}
}
}
if start < 0 || start >= cur.Length() || end < start || end > cur.Length() {
if seg.Optional() { if seg.Optional() {
cur = nil cur = nil
} else { } else {
return nil, nil, newResolutionError(fmt.Sprintf("slice out of bounds: [%d:%d]", start, end), at) return nil, nil, newResolutionError(fmt.Sprintf("slice out of bounds: [%d:%d]", start, end), at)
} }
} else { } else {
nb := basicnode.Prototype.List.NewBuilder() switch cur.Kind() {
assembler, err := nb.BeginList(int64(end - start)) case datamodel.Kind_List:
if err != nil { nb := basicnode.Prototype.List.NewBuilder()
return nil, nil, err assembler, _ := nb.BeginList(end - start)
} for i := start; i < end; i++ {
for i := start; i < end; i++ { item, _ := cur.LookupByIndex(i)
item, err := cur.LookupByIndex(int64(i)) if err := assembler.AssembleValue().AssignNode(item); err != nil {
if err != nil { return nil, nil, err
return nil, nil, err }
} }
if err := assembler.AssembleValue().AssignNode(item); err != nil { if err := assembler.Finish(); err != nil {
return nil, nil, err return nil, nil, err
} }
cur = nb.Build()
case datamodel.Kind_Bytes:
b, _ := cur.AsBytes()
cur = basicnode.NewBytes(b[start:end])
} }
if err := assembler.Finish(); err != nil {
return nil, nil, err
}
cur = nb.Build()
} }
} else if cur != nil && cur.Kind() == datamodel.Kind_Bytes {
return nil, nil, newResolutionError("bytes slice selection not yet implemented", at)
} else if seg.Optional() {
cur = nil
} else {
return nil, nil, newResolutionError(fmt.Sprintf("can not index: %s on kind: %s", seg.Field(), kindString(cur)), at)
} }
} 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:
cur = n str, _ := cur.AsString()
} else if cur != nil && cur.Kind() == datamodel.Kind_String { if idx < 0 {
str, err := cur.AsString() idx = len(str) + idx
if err != nil { }
return nil, nil, err if idx < 0 || idx >= len(str) {
} if seg.Optional() {
idx := seg.Index() cur = nil
// handle negative indices by adjusting them to count from the end of the string } else {
if idx < 0 { return nil, nil, newResolutionError(fmt.Sprintf("index out of bounds: %d", seg.Index()), at)
idx = len(str) + idx }
}
if idx < 0 || idx >= len(str) {
if seg.Optional() {
cur = nil
} else { } else {
return nil, nil, newResolutionError(fmt.Sprintf("index out of bounds: %d", seg.Index()), at) cur = basicnode.NewString(string(str[idx]))
} }
} else { case datamodel.Kind_Bytes:
cur = basicnode.NewString(string(str[idx])) b, _ := cur.AsBytes()
} if idx < 0 {
} else if cur != nil && cur.Kind() == datamodel.Kind_Bytes { idx = len(b) + idx
b, err := cur.AsBytes() }
if err != nil { if idx < 0 || idx >= len(b) {
return nil, nil, err if seg.Optional() {
} cur = nil
idx := seg.Index() } else {
if idx < 0 { return nil, nil, newResolutionError(fmt.Sprintf("index out of bounds: %d", seg.Index()), at)
idx = len(b) + idx }
}
if idx < 0 || idx >= len(b) {
if seg.Optional() {
cur = nil
} else { } else {
return nil, nil, newResolutionError(fmt.Sprintf("index out of bounds: %d", seg.Index()), at) cur = basicnode.NewInt(int64(b[idx]))
} }
} else { default:
cur = basicnode.NewInt(int64(b[idx])) return nil, nil, newResolutionError(fmt.Sprintf("can not access index: %d on kind: %s", seg.Index(), kindString(cur)), at)
} }
} 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)
} }
} }
} }
@@ -299,6 +284,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"