Commit c96c25d3 authored by Eric Myhre's avatar Eric Myhre

Add LookupSegment to Node interface.

Fix traversal internals to use it (!) rather than converting segments
to strings, which was both wasteful, and in some cases, *wrong* (!)
(although by coincidence happened to mostly work at present because of
another thing from early-days code that was also technically wrong).

Fix ipldfree.Node to reject LookupString if used on a list node!
(This is the other "wrong" thing that made the traversal coincidentally
work.)

LookupSegment method generation also added to codegen.
Signed-off-by: default avatarEric Myhre <hash@exultant.us>
parent 0b91e330
......@@ -87,6 +87,16 @@ func (n node) LookupIndex(idx int) Node {
}
return node{v, nil}
}
func (n node) LookupSegment(seg ipld.PathSegment) Node {
if n.err != nil {
return n
}
v, err := n.n.LookupSegment(seg)
if err != nil {
return node{nil, err}
}
return node{v, nil}
}
func (n node) MapIterator() MapIterator {
if n.err != nil {
panic(Error{n.err})
......
......@@ -230,3 +230,27 @@ func (n Node) Lookup(key ipld.Node) (ipld.Node, error) {
func (n Node) LookupIndex(idx int) (ipld.Node, error) {
panic("NYI")
}
func (n Node) LookupSegment(seg ipld.PathSegment) (ipld.Node, error) {
switch n.kind {
case ipld.ReprKind_Map:
return n.LookupString(seg.String())
case ipld.ReprKind_List:
idx, err := seg.Index()
if err != nil {
return nil, err
}
return n.LookupIndex(idx)
case ipld.ReprKind_Invalid,
ipld.ReprKind_Null,
ipld.ReprKind_Bool,
ipld.ReprKind_String,
ipld.ReprKind_Bytes,
ipld.ReprKind_Int,
ipld.ReprKind_Float,
ipld.ReprKind_Link:
return nil, ipld.ErrWrongKind{MethodName: "LookupSegment", AppropriateKind: ipld.ReprKindSet_Recursive, ActualKind: n.kind}
default:
panic("unreachable")
}
}
......@@ -2,9 +2,8 @@ package ipldfree
import (
"fmt"
"strconv"
"github.com/ipld/go-ipld-prime"
ipld "github.com/ipld/go-ipld-prime"
)
var (
......@@ -162,17 +161,9 @@ func (n *Node) LookupString(pth string) (ipld.Node, error) {
return nil, fmt.Errorf("404")
}
return v, nil
case ipld.ReprKind_List:
i, err := strconv.Atoi(pth)
if err != nil {
return nil, fmt.Errorf("404")
}
if i >= len(n._arr) {
return nil, fmt.Errorf("404")
}
return n._arr[i], nil
case ipld.ReprKind_Invalid,
ipld.ReprKind_Null,
ipld.ReprKind_List,
ipld.ReprKind_String,
ipld.ReprKind_Bytes,
ipld.ReprKind_Int,
......@@ -225,3 +216,27 @@ func (n *Node) LookupIndex(idx int) (ipld.Node, error) {
panic("unreachable")
}
}
func (n *Node) LookupSegment(seg ipld.PathSegment) (ipld.Node, error) {
switch n.kind {
case ipld.ReprKind_Map:
return n.LookupString(seg.String())
case ipld.ReprKind_List:
idx, err := seg.Index()
if err != nil {
return nil, err
}
return n.LookupIndex(idx)
case ipld.ReprKind_Invalid,
ipld.ReprKind_Null,
ipld.ReprKind_Bool,
ipld.ReprKind_String,
ipld.ReprKind_Bytes,
ipld.ReprKind_Int,
ipld.ReprKind_Float,
ipld.ReprKind_Link:
return nil, ipld.ErrWrongKind{MethodName: "LookupSegment", AppropriateKind: ipld.ReprKindSet_Recursive, ActualKind: n.kind}
default:
panic("unreachable")
}
}
......@@ -29,6 +29,9 @@ func (justString) Lookup(key ipld.Node) (ipld.Node, error) {
func (justString) LookupIndex(idx int) (ipld.Node, error) {
return nil, ipld.ErrWrongKind{MethodName: "LookupIndex", AppropriateKind: ipld.ReprKindSet_JustList, ActualKind: ipld.ReprKind_String}
}
func (justString) LookupSegment(seg ipld.PathSegment) (ipld.Node, error) {
return nil, ipld.ErrWrongKind{MethodName: "LookupSegment", AppropriateKind: ipld.ReprKindSet_Recursive, ActualKind: ipld.ReprKind_String}
}
func (justString) MapIterator() ipld.MapIterator {
return nodeutil.MapIteratorErrorThunk(ipld.ErrWrongKind{MethodName: "MapIterator", AppropriateKind: ipld.ReprKindSet_JustMap, ActualKind: ipld.ReprKind_String})
}
......
......@@ -39,8 +39,13 @@ type Node interface {
// If idx is out of range, a nil node and an error will be returned.
LookupIndex(idx int) (Node, error)
// LookupSegment is a convenience method that should be added sometime soon.
/// LookupSegment(seg PathSegment) (Node, error)
// LookupSegment is will act as either LookupString or LookupIndex,
// whichever is contextually appropriate.
//
// Using LookupSegment may imply an "atoi" conversion if used on a list node,
// or an "itoa" conversion if used on a map node. If an "itoa" conversion
// takes place, it may error, and this method may return that error.
LookupSegment(seg PathSegment) (Node, error)
// Note that when using codegenerated types, there may be a fifth variant
// of lookup method on maps: `Get($GeneratedTypeKey) $GeneratedTypeValue`!
......
......@@ -52,6 +52,7 @@ type nodeGenerator interface {
EmitNodeMethodLookupString(io.Writer)
EmitNodeMethodLookup(io.Writer)
EmitNodeMethodLookupIndex(io.Writer)
EmitNodeMethodLookupSegment(io.Writer)
EmitNodeMethodMapIterator(io.Writer) // also iterator itself
EmitNodeMethodListIterator(io.Writer) // also iterator itself
EmitNodeMethodLength(io.Writer)
......
......@@ -36,6 +36,14 @@ func (d generateKindedRejections) emitNodeMethodLookupIndex(w io.Writer) {
`, w, d)
}
func (d generateKindedRejections) emitNodeMethodLookupSegment(w io.Writer) {
doTemplate(`
func ({{ .TypeIdent }}) LookupSegment(seg ipld.PathSegment) (ipld.Node, error) {
return nil, ipld.ErrWrongKind{TypeName: "{{ .TypeProse }}", MethodName: "LookupSegment", AppropriateKind: ipld.ReprKindSet_Recursive, ActualKind: {{ .Kind | ReprKindConst }}}
}
`, w, d)
}
func (d generateKindedRejections) emitNodeMethodMapIterator(w io.Writer) {
doTemplate(`
func ({{ .TypeIdent }}) MapIterator() ipld.MapIterator {
......@@ -139,6 +147,9 @@ func (gk generateKindedRejections_String) EmitNodeMethodLookup(w io.Writer) {
func (gk generateKindedRejections_String) EmitNodeMethodLookupIndex(w io.Writer) {
generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_String}.emitNodeMethodLookupIndex(w)
}
func (gk generateKindedRejections_String) EmitNodeMethodLookupSegment(w io.Writer) {
generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_String}.emitNodeMethodLookupSegment(w)
}
func (gk generateKindedRejections_String) EmitNodeMethodMapIterator(w io.Writer) {
generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_String}.emitNodeMethodMapIterator(w)
}
......@@ -181,6 +192,13 @@ type generateKindedRejections_Map struct {
func (gk generateKindedRejections_Map) EmitNodeMethodLookupIndex(w io.Writer) {
generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Map}.emitNodeMethodLookupIndex(w)
}
func (gk generateKindedRejections_Map) EmitNodeMethodLookupSegment(w io.Writer) {
doTemplate(`
func (n {{ .TypeIdent }}) LookupSegment(seg ipld.PathSegment) (ipld.Node, error) {
return n.LookupString(seg.String())
}
`, w, gk)
}
func (gk generateKindedRejections_Map) EmitNodeMethodListIterator(w io.Writer) {
generateKindedRejections{gk.TypeIdent, gk.TypeProse, ipld.ReprKind_Map}.emitNodeMethodListIterator(w)
}
......
......@@ -25,6 +25,7 @@ func TestNuevo(t *testing.T) {
tg.EmitNodeMethodLookupString(w)
tg.EmitNodeMethodLookup(w)
tg.EmitNodeMethodLookupIndex(w)
tg.EmitNodeMethodLookupSegment(w)
tg.EmitNodeMethodMapIterator(w)
tg.EmitNodeMethodListIterator(w)
tg.EmitNodeMethodLength(w)
......@@ -62,6 +63,7 @@ func TestNuevo(t *testing.T) {
rng.EmitNodeMethodLookupString(w)
rng.EmitNodeMethodLookup(w)
rng.EmitNodeMethodLookupIndex(w)
rng.EmitNodeMethodLookupSegment(w)
rng.EmitNodeMethodMapIterator(w)
rng.EmitNodeMethodListIterator(w)
rng.EmitNodeMethodLength(w)
......
......@@ -17,6 +17,9 @@ func (nullNode) Lookup(key Node) (Node, error) {
func (nullNode) LookupIndex(idx int) (Node, error) {
return nil, ErrWrongKind{MethodName: "LookupIndex", AppropriateKind: ReprKindSet_JustList, ActualKind: ReprKind_Null}
}
func (nullNode) LookupSegment(seg PathSegment) (Node, error) {
return nil, ErrWrongKind{MethodName: "LookupSegment", AppropriateKind: ReprKindSet_Recursive, ActualKind: ReprKind_Null}
}
func (nullNode) MapIterator() MapIterator {
return mapIteratorReject{ErrWrongKind{MethodName: "MapIterator", AppropriateKind: ReprKindSet_JustMap, ActualKind: ReprKind_Null}}
}
......
......@@ -89,8 +89,7 @@ func (prog Progress) walkAdv_iterateAll(n ipld.Node, s selector.Selector, fn Adv
func (prog Progress) walkAdv_iterateSelective(n ipld.Node, attn []ipld.PathSegment, s selector.Selector, fn AdvVisitFn) error {
for _, ps := range attn {
// TODO: Probably not the most efficient way to be doing this...
v, err := n.LookupString(ps.String())
v, err := n.LookupSegment(ps)
if err != nil {
continue
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment