diff --git a/node/bindnode/node.go b/node/bindnode/node.go index 54d495c57feddba9611bf2547d2dab6337f4b35e..f82459651e6bd457ed6c3462683a81d1d5d5f33c 100644 --- a/node/bindnode/node.go +++ b/node/bindnode/node.go @@ -82,10 +82,16 @@ func fieldNameFromSchema(name string) string { func inferGoType(typ schema.Type) reflect.Type { switch typ := typ.(type) { + case *schema.TypeBool: + return goTypeBool case *schema.TypeInt: return goTypeInt + case *schema.TypeFloat: + return goTypeFloat case *schema.TypeString: return goTypeString + case *schema.TypeBytes: + return goTypeBytes case *schema.TypeStruct: fields := typ.Fields() goFields := make([]reflect.StructField, len(fields)) @@ -216,8 +222,10 @@ func (w *_prototype) Representation() ipld.NodePrototype { } var ( - goTypeString = reflect.TypeOf("") + goTypeBool = reflect.TypeOf(false) goTypeInt = reflect.TypeOf(int(0)) + goTypeFloat = reflect.TypeOf(0.0) + goTypeString = reflect.TypeOf("") goTypeBytes = reflect.TypeOf([]byte{}) goTypeLink = reflect.TypeOf((*ipld.Link)(nil)).Elem() @@ -296,9 +304,7 @@ func (w *_node) LookupByString(key string) (ipld.Node, error) { } fval := valuesVal.MapIndex(kval) if !fval.IsValid() { // not found - return nil, ipld.ErrNotExists{ - // TODO - } + return nil, ipld.ErrNotExists{Segment: ipld.PathSegmentOfString(key)} } // TODO: Error/panic if fval.IsNil() && !typ.ValueIsNullable()? // Otherwise we could have two non-equal Go values (nil map, @@ -326,15 +332,11 @@ func (w *_node) LookupByString(key string) (ipld.Node, error) { } } if mtyp == nil { // not found - return nil, ipld.ErrNotExists{ - // TODO - } + return nil, ipld.ErrNotExists{Segment: ipld.PathSegmentOfString(key)} } haveIdx := int(w.val.FieldByName("Index").Int()) if haveIdx != idx { // mismatching type - return nil, ipld.ErrNotExists{ - // TODO - } + return nil, ipld.ErrNotExists{Segment: ipld.PathSegmentOfString(key)} } mval := w.val.FieldByName("Value").Elem() node := &_node{ @@ -358,11 +360,8 @@ func (w *_node) LookupByIndex(idx int64) (ipld.Node, error) { switch typ := w.schemaType.(type) { case *schema.TypeList: if idx < 0 || int(idx) >= w.val.Len() { - return nil, ipld.ErrNotExists{ - // TODO - } + return nil, ipld.ErrNotExists{Segment: ipld.PathSegmentOfInt(idx)} } - // TODO: bounds check val := w.val.Index(int(idx)) if typ.ValueIsNullable() { if val.IsNil() { @@ -448,7 +447,14 @@ func (w *_node) IsNull() bool { } func (w *_node) AsBool() (bool, error) { - panic("TODO: ") + if w.Kind() != ipld.Kind_Bool { + return false, ipld.ErrWrongKind{ + TypeName: w.schemaType.Name().String(), + MethodName: "AsBool", + // TODO + } + } + return w.val.Bool(), nil } func (w *_node) AsInt() (int64, error) { @@ -463,7 +469,14 @@ func (w *_node) AsInt() (int64, error) { } func (w *_node) AsFloat() (float64, error) { - panic("TODO: ") + if w.Kind() != ipld.Kind_Float { + return 0, ipld.ErrWrongKind{ + TypeName: w.schemaType.Name().String(), + MethodName: "AsFloat", + // TODO + } + } + return w.val.Float(), nil } func (w *_node) AsString() (string, error) { @@ -509,6 +522,7 @@ type _builder struct { } func (w *_builder) Build() ipld.Node { + // TODO: should we panic if no Assign call was made, just like codegen? return &_node{schemaType: w.schemaType, val: w.val} } @@ -615,8 +629,22 @@ func (w *_assembler) AssignNull() error { return nil } -func (w *_assembler) AssignBool(bool) error { - panic("TODO: AssignBool") +func (w *_assembler) AssignBool(b bool) error { + if w.kind() != ipld.Kind_Bool { + return ipld.ErrWrongKind{ + TypeName: w.schemaType.Name().String(), + MethodName: "AssignBool", + AppropriateKind: ipld.KindSet{ipld.Kind_Bool}, + ActualKind: w.kind(), + } + } + w.nonPtrVal().SetBool(b) + if w.finish != nil { + if err := w.finish(); err != nil { + return err + } + } + return nil } func (w *_assembler) AssignInt(i int64) error { @@ -637,8 +665,22 @@ func (w *_assembler) AssignInt(i int64) error { return nil } -func (w *_assembler) AssignFloat(float64) error { - panic("TODO: AssignFloat") +func (w *_assembler) AssignFloat(f float64) error { + if w.kind() != ipld.Kind_Float { + return ipld.ErrWrongKind{ + TypeName: w.schemaType.Name().String(), + MethodName: "AssignFloat", + AppropriateKind: ipld.KindSet{ipld.Kind_Float}, + ActualKind: w.kind(), + } + } + w.nonPtrVal().SetFloat(f) + if w.finish != nil { + if err := w.finish(); err != nil { + return err + } + } + return nil } func (w *_assembler) AssignString(s string) error { @@ -736,12 +778,24 @@ func (w *_assembler) AssignNode(node ipld.Node) error { } return am.Finish() + case ipld.Kind_Bool: + b, err := node.AsBool() + if err != nil { + return err + } + return w.AssignBool(b) case ipld.Kind_Int: i, err := node.AsInt() if err != nil { return err } return w.AssignInt(i) + case ipld.Kind_Float: + f, err := node.AsFloat() + if err != nil { + return err + } + return w.AssignFloat(f) case ipld.Kind_String: s, err := node.AsString() if err != nil { diff --git a/node/bindnode/repr.go b/node/bindnode/repr.go index b26aacc4321e2ecd9d4df62ff474fa3f6e12cfe0..e8f373ca490d03f6dd22064a9d51c5d0bde39d9d 100644 --- a/node/bindnode/repr.go +++ b/node/bindnode/repr.go @@ -152,8 +152,10 @@ func (w *_nodeRepr) LookupByString(key string) (ipld.Node, error) { func (w *_nodeRepr) LookupByNode(key ipld.Node) (ipld.Node, error) { return nil, ipld.ErrWrongKind{ - TypeName: w.schemaType.Name().String(), - MethodName: "LookupByNode", AppropriateKind: ipld.KindSet_JustList, ActualKind: ipld.Kind_Map, + TypeName: w.schemaType.Name().String(), + MethodName: "LookupByNode", + AppropriateKind: ipld.KindSet_JustList, + // TODO } } @@ -240,31 +242,52 @@ func (w *_nodeRepr) Length() int64 { } func (w *_nodeRepr) IsAbsent() bool { + if reprStrategy(w.schemaType) == nil { + return (*_node)(w).IsAbsent() + } return false } func (w *_nodeRepr) IsNull() bool { + if reprStrategy(w.schemaType) == nil { + return (*_node)(w).IsNull() + } return false } func (w *_nodeRepr) AsBool() (bool, error) { + if reprStrategy(w.schemaType) == nil { + return (*_node)(w).AsBool() + } return false, ipld.ErrWrongKind{ - TypeName: w.schemaType.Name().String(), - MethodName: "AsBool", AppropriateKind: ipld.KindSet_JustBool, ActualKind: ipld.Kind_Map, + TypeName: w.schemaType.Name().String(), + MethodName: "AsBool", + AppropriateKind: ipld.KindSet_JustBool, + // TODO } } func (w *_nodeRepr) AsInt() (int64, error) { + if reprStrategy(w.schemaType) == nil { + return (*_node)(w).AsInt() + } return 0, ipld.ErrWrongKind{ - TypeName: w.schemaType.Name().String(), MethodName: "AsInt", - AppropriateKind: ipld.KindSet_JustInt, ActualKind: ipld.Kind_Map, + TypeName: w.schemaType.Name().String(), + MethodName: "AsInt", + AppropriateKind: ipld.KindSet_JustInt, + // TODO } } func (w *_nodeRepr) AsFloat() (float64, error) { + if reprStrategy(w.schemaType) == nil { + return (*_node)(w).AsFloat() + } return 0, ipld.ErrWrongKind{ - TypeName: w.schemaType.Name().String(), MethodName: "AsFloat", - AppropriateKind: ipld.KindSet_JustFloat, ActualKind: ipld.Kind_Map, + TypeName: w.schemaType.Name().String(), + MethodName: "AsFloat", + AppropriateKind: ipld.KindSet_JustFloat, + // TODO } } @@ -314,16 +337,26 @@ func (w *_nodeRepr) AsString() (string, error) { } func (w *_nodeRepr) AsBytes() ([]byte, error) { + if reprStrategy(w.schemaType) == nil { + return (*_node)(w).AsBytes() + } return nil, ipld.ErrWrongKind{ - TypeName: w.schemaType.Name().String(), MethodName: "AsBytes", - AppropriateKind: ipld.KindSet_JustBytes, ActualKind: ipld.Kind_Map, + TypeName: w.schemaType.Name().String(), + MethodName: "AsBytes", + AppropriateKind: ipld.KindSet_JustBytes, + // TODO } } func (w *_nodeRepr) AsLink() (ipld.Link, error) { + if reprStrategy(w.schemaType) == nil { + return (*_node)(w).AsLink() + } return nil, ipld.ErrWrongKind{ - TypeName: w.schemaType.Name().String(), MethodName: "AsLink", - AppropriateKind: ipld.KindSet_JustLink, ActualKind: ipld.Kind_Map, + TypeName: w.schemaType.Name().String(), + MethodName: "AsLink", + AppropriateKind: ipld.KindSet_JustLink, + // TODO } } diff --git a/node/tests/schema.go b/node/tests/schema.go index d7f7c75715e843c76bd7678324499fd84ae4c31d..0c9fc9aa42914b59f0bd7026b5e55d658843cb36 100644 --- a/node/tests/schema.go +++ b/node/tests/schema.go @@ -13,7 +13,7 @@ var allSchemaTests = []struct { {"MapsContainingMaybe", SchemaTestMapsContainingMaybe}, {"MapsContainingMaps", SchemaTestMapsContainingMaps}, {"MapsWithComplexKeys", SchemaTestMapsWithComplexKeys}, - {"String", SchemaTestString}, + {"Scalars", SchemaTestScalars}, {"RequiredFields", SchemaTestRequiredFields}, {"StructNesting", SchemaTestStructNesting}, {"StructReprStringjoin", SchemaTestStructReprStringjoin}, diff --git a/node/tests/schemaScalars.go b/node/tests/schemaScalars.go new file mode 100644 index 0000000000000000000000000000000000000000..c2fa8b0f4aefac72a92f016c848d6ed522251aa9 --- /dev/null +++ b/node/tests/schemaScalars.go @@ -0,0 +1,126 @@ +package tests + +import ( + "fmt" + "testing" + + qt "github.com/frankban/quicktest" + + "github.com/ipld/go-ipld-prime" + "github.com/ipld/go-ipld-prime/schema" +) + +func assignValue(am ipld.NodeAssembler, value interface{}) error { + switch value := value.(type) { + case bool: + return am.AssignBool(value) + case int64: + return am.AssignInt(value) + case float64: + return am.AssignFloat(value) + case string: + return am.AssignString(value) + case []byte: + return am.AssignBytes(value) + default: + panic(fmt.Sprintf("%T", value)) + } +} + +func SchemaTestScalars(t *testing.T, engine Engine) { + ts := schema.TypeSystem{} + ts.Init() + + ts.Accumulate(schema.SpawnBool("Bool")) + ts.Accumulate(schema.SpawnInt("Int")) + ts.Accumulate(schema.SpawnFloat("Float")) + ts.Accumulate(schema.SpawnString("String")) + ts.Accumulate(schema.SpawnBytes("Bytes")) + engine.Init(t, ts) + + var tests = []struct { + name string + kind ipld.Kind + value interface{} + }{ + {"Bool", ipld.Kind_Bool, true}, + {"Int", ipld.Kind_Int, int64(23)}, + {"Float", ipld.Kind_Float, 12.25}, + {"String", ipld.Kind_String, "foo"}, + {"Bytes", ipld.Kind_Bytes, []byte("bar")}, + } + + // We test each of the five scalar prototypes in subtests. + for _, testProto := range tests { + np := engine.PrototypeByName(testProto.name) + + // For each prototype, we try assigning all scalar values. + for _, testAssign := range tests { + + // We try both AssignKind and AssignNode. + for _, useAssignNode := range []bool{false, true} { + testName := fmt.Sprintf("%s-Assign%s", testProto.name, testAssign.name) + if useAssignNode { + testName = fmt.Sprintf("%s-AssignNode-%s", testProto.name, testAssign.name) + } + t.Run(testName, func(t *testing.T) { + nb := np.NewBuilder() + + // Assigning the right value for the kind should succeed. + var err error + if useAssignNode { + np2 := engine.PrototypeByName(testAssign.name) + nb2 := np2.NewBuilder() + qt.Assert(t, assignValue(nb2, testAssign.value), qt.IsNil) + n2 := nb2.Build() + + err = nb.AssignNode(n2) + } else { + err = assignValue(nb, testAssign.value) + } + if testAssign.kind == testProto.kind { + qt.Assert(t, err, qt.IsNil) + } else { + qt.Assert(t, err, qt.Not(qt.IsNil)) + + // Assign something anyway, just so we can Build later. + err := assignValue(nb, testProto.value) + qt.Assert(t, err, qt.IsNil) + } + + n := nb.Build() + + // For both the regular node and its repr version, + // getting the right value for the kind should work. + for _, n := range []ipld.Node{ + n, + n.(schema.TypedNode).Representation(), + } { + var gotValue interface{} + err = nil + switch testAssign.kind { + case ipld.Kind_Bool: + gotValue, err = n.AsBool() + case ipld.Kind_Int: + gotValue, err = n.AsInt() + case ipld.Kind_Float: + gotValue, err = n.AsFloat() + case ipld.Kind_String: + gotValue, err = n.AsString() + case ipld.Kind_Bytes: + gotValue, err = n.AsBytes() + default: + t.Fatal(testAssign.kind) + } + if testAssign.kind == testProto.kind { + qt.Assert(t, err, qt.IsNil) + qt.Assert(t, gotValue, qt.DeepEquals, testAssign.value) + } else { + qt.Assert(t, err, qt.Not(qt.IsNil)) + } + } + }) + } + } + } +} diff --git a/node/tests/schemaString.go b/node/tests/schemaString.go deleted file mode 100644 index f8f523daa7d872c6a2d48747849c886d45228ce3..0000000000000000000000000000000000000000 --- a/node/tests/schemaString.go +++ /dev/null @@ -1,37 +0,0 @@ -package tests - -import ( - "testing" - - . "github.com/warpfork/go-wish" - - "github.com/ipld/go-ipld-prime" - "github.com/ipld/go-ipld-prime/schema" -) - -func SchemaTestString(t *testing.T, engine Engine) { - ts := schema.TypeSystem{} - ts.Init() - - ts.Accumulate(schema.SpawnString("String")) - engine.Init(t, ts) - - np := engine.PrototypeByName("String") - t.Run("create string", func(t *testing.T) { - nb := np.NewBuilder() - Wish(t, nb.AssignString("woiu"), ShouldEqual, nil) - n := nb.Build().(schema.TypedNode) - t.Run("read string", func(t *testing.T) { - Wish(t, n.Kind(), ShouldEqual, ipld.Kind_String) - }) - t.Run("read representation", func(t *testing.T) { - nr := n.Representation() - Wish(t, nr.Kind(), ShouldEqual, ipld.Kind_String) - Wish(t, str(nr), ShouldEqual, "woiu") - }) - }) - t.Run("create null is rejected", func(t *testing.T) { - nb := np.NewBuilder() - Wish(t, nb.AssignNull(), ShouldBeSameTypeAs, ipld.ErrWrongKind{}) - }) -} diff --git a/schema/gen/go/testScalars_test.go b/schema/gen/go/testScalars_test.go new file mode 100644 index 0000000000000000000000000000000000000000..ff3bc22b82d540b12800cfc0c2165743807be1f8 --- /dev/null +++ b/schema/gen/go/testScalars_test.go @@ -0,0 +1,14 @@ +package gengo + +import ( + "testing" + + "github.com/ipld/go-ipld-prime/node/tests" +) + +func TestScalars(t *testing.T) { + t.Parallel() + + engine := &genAndCompileEngine{prefix: "scalars"} + tests.SchemaTestScalars(t, engine) +} diff --git a/schema/gen/go/testString_test.go b/schema/gen/go/testString_test.go deleted file mode 100644 index 038f28734adea157bdb952049276617771b05d0e..0000000000000000000000000000000000000000 --- a/schema/gen/go/testString_test.go +++ /dev/null @@ -1,14 +0,0 @@ -package gengo - -import ( - "testing" - - "github.com/ipld/go-ipld-prime/node/tests" -) - -func TestString(t *testing.T) { - t.Parallel() - - engine := &genAndCompileEngine{prefix: "string"} - tests.SchemaTestString(t, engine) -}