From dbfff3f70cf5a76880240a4c94652236ab83420b Mon Sep 17 00:00:00 2001 From: Fabio Bozzo Date: Fri, 13 Sep 2024 14:44:26 +0200 Subject: [PATCH] refactoring resolve func --- capability/policy/selector/selector.go | 321 +++++++++++++------------ 1 file changed, 171 insertions(+), 150 deletions(-) diff --git a/capability/policy/selector/selector.go b/capability/policy/selector/selector.go index 2a72554..5cf12be 100644 --- a/capability/policy/selector/selector.go +++ b/capability/policy/selector/selector.go @@ -87,68 +87,77 @@ func resolve(sel Selector, subject ipld.Node, at []string) (ipld.Node, []ipld.No for i, seg := range sel { if seg.Identity() { 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() - 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) - } + // 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 { + if seg.Optional() { + cur = nil + } else { + return nil, nil, newResolutionError(fmt.Sprintf("can not iterate over kind: %s", kindString(cur)), at) } - return nil, many, nil - } else if cur != nil && cur.Kind() == datamodel.Kind_Map { + } else { var many []ipld.Node - it := cur.MapIterator() - for { - if it.Done() { - break - } + 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 + } - 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 + } - 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) + } } + case datamodel.Kind_Map: + it := cur.MapIterator() + for !it.Done() { + k, v, err := it.Next() + if err != nil { + return nil, nil, err + } - if m != nil { - many = append(many, m...) - } else { - many = append(many, o) + 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.Optional() { - cur = nil - } else { - return nil, nil, newResolutionError(fmt.Sprintf("can not iterate over kind: %s", kindString(cur)), at) } - } else if seg.Field() != "" { + case 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()) if err != nil { if isMissing(err) { @@ -160,138 +169,114 @@ func resolve(sel Selector, subject ipld.Node, at []string) (ipld.Node, []ipld.No } else { 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 { - slice := seg.Slice() - start, end := int64(0), cur.Length() - - if len(slice) > 0 { - start = int64(slice[0]) - if start < 0 { - start = cur.Length() + start - if start < 0 { - start = 0 - } - } - } - if len(slice) > 1 { - end = int64(slice[1]) - if end <= 0 { - end = cur.Length() + end - if end < start { - end = start - } - } + 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() + var start, end int64 + switch cur.Kind() { + case datamodel.Kind_List: + start, end = resolveSliceIndices(slice, cur.Length()) + case datamodel.Kind_Bytes: + b, _ := cur.AsBytes() + start, end = resolveSliceIndices(slice, int64(len(b))) + default: + return nil, nil, newResolutionError(fmt.Sprintf("can not slice on kind: %s", kindString(cur)), at) } - if start < 0 || start >= cur.Length() || end < start || end > cur.Length() { + if start < 0 || end < start || end > cur.Length() { if seg.Optional() { cur = nil } else { return nil, nil, newResolutionError(fmt.Sprintf("slice out of bounds: [%d:%d]", start, end), at) } } else { - nb := basicnode.Prototype.List.NewBuilder() - assembler, err := nb.BeginList(int64(end - start)) - if err != nil { - return nil, nil, err - } - for i := start; i < end; i++ { - item, err := cur.LookupByIndex(int64(i)) - if err != nil { - return nil, nil, err + switch cur.Kind() { + case datamodel.Kind_List: + nb := basicnode.Prototype.List.NewBuilder() + assembler, _ := nb.BeginList(end - start) + for i := start; i < end; i++ { + item, _ := cur.LookupByIndex(i) + if err := assembler.AssembleValue().AssignNode(item); err != nil { + return nil, nil, err + } } - if err := assembler.AssembleValue().AssignNode(item); err != nil { + if err := assembler.Finish(); err != nil { 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())) - if cur != nil && cur.Kind() == datamodel.Kind_List { - idx := int64(seg.Index()) - if idx < 0 { - idx = cur.Length() + idx - } - if idx < 0 { - // necessary until https://github.com/ipld/go-ipld-prime/pull/571 - // after, isMissing() below will work - // TODO: remove - return nil, nil, newResolutionError(fmt.Sprintf("index out of bounds: %d", seg.Index()), at) + if cur == nil { + if seg.Optional() { + cur = nil + } else { + return nil, nil, newResolutionError(fmt.Sprintf("can not access index: %d on kind: %s", seg.Index(), kindString(cur)), at) } - n, err := cur.LookupByIndex(idx) - if err != nil { - if isMissing(err) { + } else { + idx := seg.Index() + switch cur.Kind() { + case datamodel.Kind_List: + if idx < 0 { + idx = int(cur.Length()) + idx + } + if idx < 0 || idx >= int(cur.Length()) { if seg.Optional() { cur = nil } else { return nil, nil, newResolutionError(fmt.Sprintf("index out of bounds: %d", seg.Index()), at) } } else { - return nil, nil, err + cur, _ = cur.LookupByIndex(int64(idx)) } - } - cur = n - } else if cur != nil && cur.Kind() == datamodel.Kind_String { - str, err := cur.AsString() - if err != nil { - return nil, nil, err - } - idx := seg.Index() - // handle negative indices by adjusting them to count from the end of the string - if idx < 0 { - idx = len(str) + idx - } - if idx < 0 || idx >= len(str) { - if seg.Optional() { - cur = nil + 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 { - return nil, nil, newResolutionError(fmt.Sprintf("index out of bounds: %d", seg.Index()), at) + cur = basicnode.NewString(string(str[idx])) } - } else { - cur = basicnode.NewString(string(str[idx])) - } - } else if cur != nil && cur.Kind() == datamodel.Kind_Bytes { - b, err := cur.AsBytes() - if err != nil { - return nil, nil, err - } - idx := seg.Index() - if idx < 0 { - idx = len(b) + idx - } - if idx < 0 || idx >= len(b) { - if seg.Optional() { - cur = nil + 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 { - return nil, nil, newResolutionError(fmt.Sprintf("index out of bounds: %d", seg.Index()), at) + cur = basicnode.NewInt(int64(b[idx])) } - } 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) } - } 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 } +// 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 { if n == nil { return "null"