Commit b66f9261 authored by Eric Myhre's avatar Eric Myhre

Begin schema validation method.

This will be for the active path -- if we also follow through on the
idea of having a just-in-time checked Node wrapper, a lot of these
checks might end up duplicated.  We'll DRY that up when we get there.

Doing accumulation of errors.  Might get loud.  We can come back and
and early-halt parameters or other accumulation strategies later.

Added IsNull predicate to core Node interface.

Going to want enumerated error categories here for sure, but punting
on that until we get more examples of what all the errors *are*; then
I'll come back and tear all this 'fmt.Errorf' nonsense back out.
Signed-off-by: default avatarEric Myhre <hash@exultant.us>
parent 5c32434e
......@@ -44,6 +44,7 @@ func Bind(bindme interface{}, atl atlas.Atlas) ipld.Node {
}
func determineReprKind(rv reflect.Value) ipld.ReprKind {
// TODO also handle nils
switch rv.Type().Kind() {
case reflect.Bool:
return ipld.ReprKind_Bool
......@@ -75,6 +76,9 @@ func (n Node) Kind() ipld.ReprKind {
return n.kind
}
func (n Node) IsNull() bool {
return n.bound.IsNil()
}
func (n Node) AsBool() (v bool, _ error) {
reflect.ValueOf(&v).Elem().Set(n.bound)
return
......
......@@ -44,6 +44,9 @@ func (n *Node) Kind() ipld.ReprKind {
return n.kind
}
func (n *Node) IsNull() bool {
return n.kind == ipld.ReprKind_Null
}
func (n *Node) AsBool() (v bool, _ error) {
return n._bool, expectTyp(ipld.ReprKind_Bool, n.kind)
}
......
......@@ -26,6 +26,7 @@ type Node interface {
// and if it's a primitive type the returned values are nil and zero.
Keys() ([]string, int)
IsNull() bool
AsBool() (bool, error)
AsString() (string, error)
AsInt() (int, error)
......
package typed
package typed
import (
"fmt"
"path"
"github.com/ipld/go-ipld-prime"
)
......@@ -16,6 +19,61 @@ type MutableNode struct {
}
func Validate(ts Universe, t Type, node ipld.Node) []error {
// TODO need more methods to enable traversal, then come back here
return validate(ts, t, node, "/")
}
// review: 'ts' param might not actually be necessary; everything relevant can be reached from t so far.
func validate(ts Universe, t Type, node ipld.Node, pth string) []error {
switch t2 := t.(type) {
case TypeBool:
if node.Kind() != ipld.ReprKind_Bool {
return []error{fmt.Errorf("Schema match failed: expected type %q (which is kind %v) at path %q, but found kind %v", t2.Name, t.ReprKind(), pth, node.Kind())}
}
return nil
case TypeString:
if node.Kind() != ipld.ReprKind_String {
return []error{fmt.Errorf("Schema match failed: expected type %q (which is kind %v) at path %q, but found kind %v", t2.Name, t.ReprKind(), pth, node.Kind())}
}
return nil
case TypeBytes:
if node.Kind() != ipld.ReprKind_Bytes {
return []error{fmt.Errorf("Schema match failed: expected type %q (which is kind %v) at path %q, but found kind %v", t2.Name, t.ReprKind(), pth, node.Kind())}
}
return nil
case TypeInt:
if node.Kind() != ipld.ReprKind_Int {
return []error{fmt.Errorf("Schema match failed: expected type %q (which is kind %v) at path %q, but found kind %v", t2.Name, t.ReprKind(), pth, node.Kind())}
}
return nil
case TypeFloat:
if node.Kind() != ipld.ReprKind_Float {
return []error{fmt.Errorf("Schema match failed: expected type %q (which is kind %v) at path %q, but found kind %v", t2.Name, t.ReprKind(), pth, node.Kind())}
}
return nil
case TypeMap:
if node.Kind() != ipld.ReprKind_Map {
return []error{fmt.Errorf("Schema match failed: expected type %q (which is kind %v) at path %q, but found kind %v", t2.Name, t.ReprKind(), pth, node.Kind())}
}
keys, _ := node.Keys()
errs := []error(nil)
for _, k := range keys {
child, _ := node.TraverseField(k)
if child.IsNull() {
if !t2.ValueNullable {
errs = append(errs, fmt.Errorf("Schema match failed: map at path %q contains unpermitted null in key %q", pth, k))
}
} else {
errs = append(errs, validate(ts, t2.ValueType, child, path.Join(pth, k))...)
}
}
return errs
case TypeList:
case TypeLink:
case TypeUnion:
// TODO *several* interesting errors
case TypeObject:
case TypeEnum:
// TODO another interesting error
}
return nil
}
package typed
import (
"testing"
. "github.com/warpfork/go-wish"
"github.com/ipld/go-ipld-prime/impl/free"
)
func TestSimpleTypes(t *testing.T) {
t.Run("string alone", func(t *testing.T) {
n1 := &ipldfree.Node{}
n1.SetString("asdf")
t1 := TypeString{
Name: "Foo",
}
Wish(t,
Validate(nil, t1, n1),
ShouldEqual, []error(nil))
})
}
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