Commit 13e9a113 authored by Eric Myhre's avatar Eric Myhre

Add Node.Lookup; and add ErrInvalidKey.

Signed-off-by: default avatarEric Myhre <hash@exultant.us>
parent 40bacb4f
......@@ -52,6 +52,23 @@ func (e ErrNotExists) Error() string {
return fmt.Sprintf("key not found: %q", e.Segment)
}
// ErrInvalidKey may be returned from lookup functions on the Node interface
// when a key is invalid.
//
// Common examples of this are when `Lookup(Node)` is used with a non-string Node;
// typed nodes also introduce other reasons a key may be invalid.
type ErrInvalidKey struct {
Reason string
// Perhaps typed.ErrNoSuchField could be folded into this?
// Perhaps Reason could be replaced by an enum of "NoSuchField"|"NotAString"|"ConstraintRejected"?
// Might be hard to get rid of the freetext field entirely -- constraints may be nontrivial to describe.
}
func (e ErrInvalidKey) Error() string {
return fmt.Sprintf("invalid key: %s", e.Reason)
}
// ErrIteratorOverread is returned when calling 'Next' on a MapIterator or
// ListIterator when it is already done.
type ErrIteratorOverread struct{}
......
......@@ -16,6 +16,7 @@ import (
type Node interface {
ReprKind() ipld.ReprKind
LookupString(path string) Node
Lookup(key Node) Node
LookupIndex(idx int) Node
MapIterator() MapIterator
ListIterator() ListIterator
......@@ -66,6 +67,16 @@ func (n node) LookupString(path string) Node {
}
return node{v, nil}
}
func (n node) Lookup(key Node) Node {
if n.err != nil {
return n
}
v, err := n.n.Lookup(key.(node).n) // hacky. needs fluent.Node needs unbox method.
if err != nil {
return node{nil, err}
}
return node{v, nil}
}
func (n node) LookupIndex(idx int) Node {
if n.err != nil {
return n
......
......@@ -214,6 +214,19 @@ func (n Node) LookupString(pth string) (ipld.Node, error) {
}
}
func (n Node) Lookup(key ipld.Node) (ipld.Node, error) {
switch n.kind {
case ipld.ReprKind_Map:
ks, err := key.AsString()
if err != nil {
return nil, ipld.ErrInvalidKey{"got " + key.ReprKind().String() + ", need string"}
}
return n.LookupString(ks)
default:
return nil, ipld.ErrWrongKind{MethodName: "Lookup", AppropriateKind: ipld.ReprKindSet_JustMap, ActualKind: n.kind}
}
}
func (n Node) LookupIndex(idx int) (ipld.Node, error) {
panic("NYI")
}
......@@ -184,6 +184,23 @@ func (n *Node) LookupString(pth string) (ipld.Node, error) {
}
}
func (n *Node) Lookup(key ipld.Node) (ipld.Node, error) {
switch n.kind {
case ipld.ReprKind_Map:
ks, err := key.AsString()
if err != nil {
return nil, ipld.ErrInvalidKey{fmt.Sprintf("got %s, need string", key.ReprKind())}
}
v, exists := n._map[ks]
if !exists {
return nil, fmt.Errorf("404")
}
return v, nil
default:
return nil, ipld.ErrWrongKind{MethodName: "Lookup", AppropriateKind: ipld.ReprKindSet_JustMap, ActualKind: n.kind}
}
}
func (n *Node) LookupIndex(idx int) (ipld.Node, error) {
switch n.kind {
case ipld.ReprKind_List:
......
......@@ -23,6 +23,9 @@ func (justString) ReprKind() ipld.ReprKind {
func (justString) LookupString(string) (ipld.Node, error) {
return nil, ipld.ErrWrongKind{MethodName: "LookupString", AppropriateKind: ipld.ReprKindSet_JustMap, ActualKind: ipld.ReprKind_String}
}
func (justString) Lookup(key ipld.Node) (ipld.Node, error) {
return nil, ipld.ErrWrongKind{MethodName: "Lookup", AppropriateKind: ipld.ReprKindSet_JustMap, ActualKind: ipld.ReprKind_String}
}
func (justString) LookupIndex(idx int) (ipld.Node, error) {
return nil, ipld.ErrWrongKind{MethodName: "LookupIndex", AppropriateKind: ipld.ReprKindSet_JustList, ActualKind: ipld.ReprKind_String}
}
......
......@@ -22,7 +22,12 @@ type Node interface {
// have constraints, and you already have a reified `typed.Node` value,
// using that value can save parsing and validation costs);
// and may simply be convenient if you already have a Node value in hand.
/// Lookup(key Node) (Node, error)
//
// (When writing generic functions over Node, a good rule of thumb is:
// when handling a map, check for `typed.Node`, and in this case prefer
// the Lookup(Node) method; otherwise, favor LookupString; typically
// implementations will have their fastest paths thusly.)
Lookup(key Node) (Node, error)
// LookupIndex is the equivalent of LookupString but for indexing into a list.
// As with LookupString, the returned Node may be any of the ReprKind:
......
......@@ -39,6 +39,7 @@ type typeGenerator interface {
EmitNodeType(io.Writer)
EmitNodeMethodReprKind(io.Writer)
EmitNodeMethodLookupString(io.Writer)
EmitNodeMethodLookup(io.Writer)
EmitNodeMethodLookupIndex(io.Writer)
EmitNodeMethodMapIterator(io.Writer)
EmitNodeMethodListIterator(io.Writer)
......
......@@ -16,6 +16,14 @@ func (generateKindedRejections) emitNodeMethodLookupString(w io.Writer, t schema
`, w, t)
}
func (generateKindedRejections) emitNodeMethodLookup(w io.Writer, t schema.Type) {
doTemplate(`
func ({{ .Name }}) Lookup(ipld.Node) (ipld.Node, error) {
return nil, ipld.ErrWrongKind{MethodName: "{{ .Name }}.Lookup", AppropriateKind: ipld.ReprKindSet_JustMap, ActualKind: {{ .Kind.ActsLike | ReprKindConst }}}
}
`, w, t)
}
func (generateKindedRejections) emitNodeMethodLookupIndex(w io.Writer, t schema.Type) {
doTemplate(`
func ({{ .Name }}) LookupIndex(idx int) (ipld.Node, error) {
......@@ -120,6 +128,9 @@ type generateKindedRejections_String struct {
func (gk generateKindedRejections_String) EmitNodeMethodLookupString(w io.Writer) {
generateKindedRejections{}.emitNodeMethodLookupString(w, gk.Type)
}
func (gk generateKindedRejections_String) EmitNodeMethodLookup(w io.Writer) {
generateKindedRejections{}.emitNodeMethodLookup(w, gk.Type)
}
func (gk generateKindedRejections_String) EmitNodeMethodLookupIndex(w io.Writer) {
generateKindedRejections{}.emitNodeMethodLookupIndex(w, gk.Type)
}
......
......@@ -80,6 +80,18 @@ func (gk generateKindStruct) EmitNodeMethodLookupString(w io.Writer) {
`, w, gk)
}
func (gk generateKindStruct) EmitNodeMethodLookup(w io.Writer) {
doTemplate(`
func (x {{ .Type.Name }}) Lookup(key ipld.Node) (ipld.Node, error) {
ks, err := key.AsString()
if err != nil {
return nil, ipld.ErrInvalidKey{"got " + key.ReprKind().String() + ", need string"}
}
return x.LookupString(ks)
}
`, w, gk)
}
func (gk generateKindStruct) EmitNodeMethodMapIterator(w io.Writer) {
doTemplate(`
func (x {{ .Type.Name }}) MapIterator() ipld.MapIterator {
......
......@@ -22,6 +22,7 @@ func TestNuevo(t *testing.T) {
tg.EmitNodeType(w)
tg.EmitNodeMethodReprKind(w)
tg.EmitNodeMethodLookupString(w)
tg.EmitNodeMethodLookup(w)
tg.EmitNodeMethodLookupIndex(w)
tg.EmitNodeMethodMapIterator(w)
tg.EmitNodeMethodListIterator(w)
......
......@@ -11,6 +11,9 @@ func (nullNode) ReprKind() ReprKind {
func (nullNode) LookupString(key string) (Node, error) {
return nil, ErrWrongKind{MethodName: "<null>.LookupString", AppropriateKind: ReprKindSet_JustMap, ActualKind: ReprKind_Null}
}
func (nullNode) Lookup(key Node) (Node, error) {
return nil, ErrWrongKind{MethodName: "<null>.Lookup", AppropriateKind: ReprKindSet_JustMap, ActualKind: ReprKind_Null}
}
func (nullNode) LookupIndex(idx int) (Node, error) {
return nil, ErrWrongKind{MethodName: "<null>.LookupIndex", AppropriateKind: ReprKindSet_JustList, ActualKind: ReprKind_Null}
}
......
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