From fd4241a23231a2c111a9b435c4dd8ef743cc39b0 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Fri, 7 Jul 2023 16:35:58 +1000 Subject: [PATCH] feat!: Node#Length() now has an error return Closes: https://github.com/ipld/go-ipld-prime/issues/531 ADL and other Node implementations that need to perform non-trivial work in calculating their Length(), such as by loading multiple child blocks, may encounter errors which should not be silently ignored. --- adl/rot13adl/rot13node.go | 4 +- adl/rot13adl/rot13substrate.go | 4 +- codec/dagcbor/marshal.go | 31 ++++++--- codec/dagjson/marshal.go | 20 ++++-- codecHelpers_test.go | 3 +- datamodel/copy.go | 12 +++- datamodel/equal.go | 20 +++++- datamodel/node.go | 2 +- datamodel/unit.go | 8 +-- examples_test.go | 3 +- fluent/fluentBuilder_test.go | 24 +++++-- fluent/reflect_test.go | 16 +++-- fluent/toInterfaceValue.go | 12 +++- linking/linkingExamples_test.go | 3 +- node/basicnode/bool.go | 4 +- node/basicnode/bytes.go | 4 +- node/basicnode/bytes_stream.go | 4 +- node/basicnode/float.go | 4 +- node/basicnode/int.go | 8 +-- node/basicnode/link.go | 4 +- node/basicnode/list.go | 10 ++- node/basicnode/map.go | 4 +- node/basicnode/string.go | 4 +- node/bindnode/infer_test.go | 12 +++- node/bindnode/node.go | 20 +++--- node/bindnode/repr.go | 10 +-- node/gendemo/ipldsch_satisfaction.go | 56 ++++++++------- node/tests/listSpecs.go | 4 +- node/tests/mapSpecs.go | 16 +++-- node/tests/schemaLists.go | 56 ++++++++++----- node/tests/schemaMaps.go | 58 +++++++++++----- node/tests/schemaScalars.go | 4 +- node/tests/schemaStruct.go | 8 ++- node/tests/schemaStructReprListpairs.go | 88 ++++++++++++++++++------ node/tests/schemaStructReprStringjoin.go | 20 ++++-- node/tests/schemaStructReprTuple.go | 24 +++++-- node/tests/testcase.go | 6 +- printer/printer.go | 18 ++++- printer/printer_test.go | 4 +- schema/gen/go/genList.go | 16 +++-- schema/gen/go/genListReprList.go | 4 +- schema/gen/go/genMap.go | 4 +- schema/gen/go/genMapReprMap.go | 4 +- schema/gen/go/genStruct.go | 4 +- schema/gen/go/genStructReprMap.go | 4 +- schema/gen/go/genStructReprTuple.go | 4 +- schema/gen/go/genUnion.go | 4 +- schema/gen/go/genUnionReprKeyed.go | 4 +- schema/gen/go/genUnionReprKinded.go | 16 +++-- schema/gen/go/mixins/kindTraits.go | 4 +- traversal/focus.go | 18 ++++- traversal/focus_test.go | 18 +++-- traversal/patch/eval.go | 6 +- traversal/selector/condition.go | 6 +- traversal/selector/exploreFields.go | 8 ++- traversal/selector/exploreRecursive.go | 6 +- traversal/selector/exploreUnion.go | 6 +- traversal/selector/selector.go | 6 +- traversal/walk.go | 12 +++- 59 files changed, 537 insertions(+), 229 deletions(-) diff --git a/adl/rot13adl/rot13node.go b/adl/rot13adl/rot13node.go index 619dd748..9049c1fe 100644 --- a/adl/rot13adl/rot13node.go +++ b/adl/rot13adl/rot13node.go @@ -58,8 +58,8 @@ func (*_R13String) MapIterator() datamodel.MapIterator { func (*_R13String) ListIterator() datamodel.ListIterator { return nil } -func (*_R13String) Length() int64 { - return -1 +func (*_R13String) Length() (int64, error) { + return -1, nil } func (*_R13String) IsAbsent() bool { return false diff --git a/adl/rot13adl/rot13substrate.go b/adl/rot13adl/rot13substrate.go index 511475ad..618e2056 100644 --- a/adl/rot13adl/rot13substrate.go +++ b/adl/rot13adl/rot13substrate.go @@ -51,8 +51,8 @@ func (*_Substrate) MapIterator() datamodel.MapIterator { func (*_Substrate) ListIterator() datamodel.ListIterator { return nil } -func (*_Substrate) Length() int64 { - return -1 +func (*_Substrate) Length() (int64, error) { + return -1, nil } func (*_Substrate) IsAbsent() bool { return false diff --git a/codec/dagcbor/marshal.go b/codec/dagcbor/marshal.go index a7ee1620..790cedeb 100644 --- a/codec/dagcbor/marshal.go +++ b/codec/dagcbor/marshal.go @@ -70,7 +70,10 @@ func marshal(n datamodel.Node, tk *tok.Token, sink shared.TokenSink, options Enc case datamodel.Kind_List: // Emit start of list. tk.Type = tok.TArrOpen - l := n.Length() + l, err := n.Length() + if err != nil { + return err + } tk.Length = int(l) // TODO: overflow check if _, err := sink.Step(tk); err != nil { return err @@ -87,7 +90,7 @@ func marshal(n datamodel.Node, tk *tok.Token, sink shared.TokenSink, options Enc } // Emit list close. tk.Type = tok.TArrClose - _, err := sink.Step(tk) + _, err = sink.Step(tk) return err case datamodel.Kind_Bool: v, err := n.AsBool() @@ -174,8 +177,11 @@ func marshal(n datamodel.Node, tk *tok.Token, sink shared.TokenSink, options Enc func marshalMap(n datamodel.Node, tk *tok.Token, sink shared.TokenSink, options EncodeOptions) error { // Emit start of map. tk.Type = tok.TMapOpen - expectedLength := int(n.Length()) - tk.Length = expectedLength // TODO: overflow check + expectedLength, err := n.Length() + if err != nil { + return err + } + tk.Length = int(expectedLength) // TODO: overflow check if _, err := sink.Step(tk); err != nil { return err } @@ -197,7 +203,7 @@ func marshalMap(n datamodel.Node, tk *tok.Token, sink shared.TokenSink, options } entries = append(entries, entry{keyStr, v}) } - if len(entries) != expectedLength { + if len(entries) != int(expectedLength) { return fmt.Errorf("map Length() does not match number of MapIterator() entries") } // Apply the desired sort function. @@ -229,7 +235,7 @@ func marshalMap(n datamodel.Node, tk *tok.Token, sink shared.TokenSink, options } } else { // no sorting // Emit map contents (and recurse). - var entryCount int + var entryCount int64 for itr := n.MapIterator(); !itr.Done(); { k, v, err := itr.Next() if err != nil { @@ -254,7 +260,7 @@ func marshalMap(n datamodel.Node, tk *tok.Token, sink shared.TokenSink, options } // Emit map close. tk.Type = tok.TMapClose - _, err := sink.Step(tk) + _, err = sink.Step(tk) return err } @@ -272,7 +278,11 @@ func EncodedLength(n datamodel.Node) (int64, error) { case datamodel.Kind_Null: return 1, nil // 0xf6 case datamodel.Kind_Map: - length := uintLength(uint64(n.Length())) // length prefixed major 5 + l, err := n.Length() + if err != nil { + return 0, err + } + length := uintLength(uint64(l)) // length prefixed major 5 for itr := n.MapIterator(); !itr.Done(); { k, v, err := itr.Next() if err != nil { @@ -291,7 +301,10 @@ func EncodedLength(n datamodel.Node) (int64, error) { } return length, nil case datamodel.Kind_List: - nl := n.Length() + nl, err := n.Length() + if err != nil { + return 0, err + } length := uintLength(uint64(nl)) // length prefixed major 4 for i := int64(0); i < nl; i++ { v, err := n.LookupByIndex(i) diff --git a/codec/dagjson/marshal.go b/codec/dagjson/marshal.go index 2fe39680..33e20ee2 100644 --- a/codec/dagjson/marshal.go +++ b/codec/dagjson/marshal.go @@ -61,8 +61,11 @@ func Marshal(n datamodel.Node, sink shared.TokenSink, options EncodeOptions) err case datamodel.Kind_Map: // Emit start of map. tk.Type = tok.TMapOpen - expectedLength := int(n.Length()) - tk.Length = expectedLength // TODO: overflow check + expectedLength, err := n.Length() + if err != nil { + return err + } + tk.Length = int(expectedLength) // TODO: overflow check if _, err := sink.Step(&tk); err != nil { return err } @@ -84,7 +87,7 @@ func Marshal(n datamodel.Node, sink shared.TokenSink, options EncodeOptions) err } entries = append(entries, entry{keyStr, v}) } - if len(entries) != expectedLength { + if len(entries) != int(expectedLength) { return fmt.Errorf("map Length() does not match number of MapIterator() entries") } // Apply the desired sort function. @@ -104,7 +107,7 @@ func Marshal(n datamodel.Node, sink shared.TokenSink, options EncodeOptions) err }) } // Emit map contents (and recurse). - var entryCount int + var entryCount int64 for _, e := range entries { tk.Type = tok.TString tk.Str = e.key @@ -141,12 +144,15 @@ func Marshal(n datamodel.Node, sink shared.TokenSink, options EncodeOptions) err } // Emit map close. tk.Type = tok.TMapClose - _, err := sink.Step(&tk) + _, err = sink.Step(&tk) return err case datamodel.Kind_List: // Emit start of list. tk.Type = tok.TArrOpen - l := n.Length() + l, err := n.Length() + if err != nil { + return err + } tk.Length = int(l) // TODO: overflow check if _, err := sink.Step(&tk); err != nil { return err @@ -163,7 +169,7 @@ func Marshal(n datamodel.Node, sink shared.TokenSink, options EncodeOptions) err } // Emit list close. tk.Type = tok.TArrClose - _, err := sink.Step(&tk) + _, err = sink.Step(&tk) return err case datamodel.Kind_Bool: v, err := n.AsBool() diff --git a/codecHelpers_test.go b/codecHelpers_test.go index a515f0c4..23f0deb3 100644 --- a/codecHelpers_test.go +++ b/codecHelpers_test.go @@ -49,7 +49,8 @@ func Example_unmarshal_withSchema() { n, err := ipld.Unmarshal(serial, json.Decode, &foobar, typesys.TypeByName("Foobar")) fmt.Printf("error: %v\n", err) fmt.Printf("go struct: %v\n", foobar) - fmt.Printf("node kind and length: %s, %d\n", n.Kind(), n.Length()) + l, _ := n.Length() + fmt.Printf("node kind and length: %s, %d\n", n.Kind(), l) fmt.Printf("node lookup 'foo': %q\n", must.String(must.Node(n.LookupByString("foo")))) // Output: diff --git a/datamodel/copy.go b/datamodel/copy.go index cedfb53e..f12ace77 100644 --- a/datamodel/copy.go +++ b/datamodel/copy.go @@ -67,7 +67,11 @@ func Copy(n Node, na NodeAssembler) error { } return na.AssignLink(v) case Kind_Map: - ma, err := na.BeginMap(n.Length()) + l, err := n.Length() + if err != nil { + return err + } + ma, err := na.BeginMap(l) if err != nil { return err } @@ -89,7 +93,11 @@ func Copy(n Node, na NodeAssembler) error { } return ma.Finish() case Kind_List: - la, err := na.BeginList(n.Length()) + l, err := n.Length() + if err != nil { + return err + } + la, err := na.BeginList(l) if err != nil { return err } diff --git a/datamodel/equal.go b/datamodel/equal.go index 8c476113..e3bc6bae 100644 --- a/datamodel/equal.go +++ b/datamodel/equal.go @@ -106,7 +106,15 @@ func DeepEqual(x, y Node) bool { // Recursive kinds. case Kind_Map: - if x.Length() != y.Length() { + xl, err := x.Length() + if err != nil { + panic(err) + } + yl, err := y.Length() + if err != nil { + panic(err) + } + if xl != yl { return false } xitr := x.MapIterator() @@ -129,7 +137,15 @@ func DeepEqual(x, y Node) bool { } return true case Kind_List: - if x.Length() != y.Length() { + xl, err := x.Length() + if err != nil { + panic(err) + } + yl, err := y.Length() + if err != nil { + panic(err) + } + if xl != yl { return false } xitr := x.ListIterator() diff --git a/datamodel/node.go b/datamodel/node.go index 625f472d..db993bfe 100644 --- a/datamodel/node.go +++ b/datamodel/node.go @@ -130,7 +130,7 @@ type Node interface { // Length returns the length of a list, or the number of entries in a map, // or -1 if the node is not of list nor map kind. - Length() int64 + Length() (int64, error) // Absent nodes are returned when traversing a struct field that is // defined by a schema but unset in the data. (Absent nodes are not diff --git a/datamodel/unit.go b/datamodel/unit.go index afc2e3e1..d6f955fa 100644 --- a/datamodel/unit.go +++ b/datamodel/unit.go @@ -31,8 +31,8 @@ func (nullNode) MapIterator() MapIterator { func (nullNode) ListIterator() ListIterator { return nil } -func (nullNode) Length() int64 { - return -1 +func (nullNode) Length() (int64, error) { + return -1, nil } func (nullNode) IsAbsent() bool { return false @@ -110,8 +110,8 @@ func (absentNode) MapIterator() MapIterator { func (absentNode) ListIterator() ListIterator { return nil } -func (absentNode) Length() int64 { - return -1 +func (absentNode) Length() (int64, error) { + return -1, nil } func (absentNode) IsAbsent() bool { return true diff --git a/examples_test.go b/examples_test.go index ed78b02f..deb08329 100644 --- a/examples_test.go +++ b/examples_test.go @@ -48,7 +48,8 @@ func Example_unmarshalData() { n := nb.Build() // Call 'Build' to get the resulting Node. (It's immutable!) fmt.Printf("the data decoded was a %s kind\n", n.Kind()) - fmt.Printf("the length of the node is %d\n", n.Length()) + l, _ := n.Length() + fmt.Printf("the length of the node is %d\n", l) // Output: // the data decoded was a map kind diff --git a/fluent/fluentBuilder_test.go b/fluent/fluentBuilder_test.go index 81d5ea61..1f9f49e5 100644 --- a/fluent/fluentBuilder_test.go +++ b/fluent/fluentBuilder_test.go @@ -34,11 +34,15 @@ func TestBuild(t *testing.T) { }) }) qt.Check(t, n.Kind(), qt.Equals, datamodel.Kind_Map) - qt.Check(t, n.Length(), qt.Equals, int64(3)) + l, err := n.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(3)) qt.Check(t, must.String(must.Node(n.LookupByString("k1"))), qt.Equals, "fine") qt.Check(t, must.String(must.Node(n.LookupByString("k2"))), qt.Equals, "super") n = must.Node(n.LookupByString("k3")) - qt.Check(t, n.Length(), qt.Equals, int64(3)) + l, err = n.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(3)) qt.Check(t, must.String(must.Node(n.LookupByString("k31"))), qt.Equals, "thanks") qt.Check(t, must.String(must.Node(n.LookupByString("k32"))), qt.Equals, "for") qt.Check(t, must.String(must.Node(n.LookupByString("k33"))), qt.Equals, "asking") @@ -56,16 +60,24 @@ func TestBuild(t *testing.T) { }) }) qt.Check(t, n.Kind(), qt.Equals, datamodel.Kind_List) - qt.Check(t, n.Length(), qt.Equals, int64(1)) + l, err := n.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(1)) n = must.Node(n.LookupByIndex(0)) qt.Check(t, n.Kind(), qt.Equals, datamodel.Kind_List) - qt.Check(t, n.Length(), qt.Equals, int64(1)) + l, err = n.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(1)) n = must.Node(n.LookupByIndex(0)) qt.Check(t, n.Kind(), qt.Equals, datamodel.Kind_List) - qt.Check(t, n.Length(), qt.Equals, int64(1)) + l, err = n.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(1)) n = must.Node(n.LookupByIndex(0)) qt.Check(t, n.Kind(), qt.Equals, datamodel.Kind_List) - qt.Check(t, n.Length(), qt.Equals, int64(1)) + l, err = n.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(1)) n = must.Node(n.LookupByIndex(0)) qt.Check(t, n.Kind(), qt.Equals, datamodel.Kind_Int) qt.Check(t, must.Int(n), qt.Equals, int64(2)) diff --git a/fluent/reflect_test.go b/fluent/reflect_test.go index 83744d35..8d7cd59a 100644 --- a/fluent/reflect_test.go +++ b/fluent/reflect_test.go @@ -25,11 +25,15 @@ func TestReflect(t *testing.T) { qt.Check(t, err, qt.IsNil) qt.Check(t, n.Kind(), qt.Equals, datamodel.Kind_Map) t.Run("CorrectContents", func(t *testing.T) { - qt.Check(t, n.Length(), qt.Equals, int64(3)) + l, err := n.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(3)) qt.Check(t, must.String(must.Node(n.LookupByString("k1"))), qt.Equals, "fine") qt.Check(t, must.String(must.Node(n.LookupByString("k2"))), qt.Equals, "super") n := must.Node(n.LookupByString("k3")) - qt.Check(t, n.Length(), qt.Equals, int64(3)) + l, err = n.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(3)) qt.Check(t, must.String(must.Node(n.LookupByString("k31"))), qt.Equals, "thanks") qt.Check(t, must.String(must.Node(n.LookupByString("k32"))), qt.Equals, "for") qt.Check(t, must.String(must.Node(n.LookupByString("k33"))), qt.Equals, "asking") @@ -69,11 +73,15 @@ func TestReflect(t *testing.T) { qt.Check(t, err, qt.IsNil) qt.Check(t, n.Kind(), qt.Equals, datamodel.Kind_Map) t.Run("CorrectContents", func(t *testing.T) { - qt.Check(t, n.Length(), qt.Equals, int64(3)) + l, err := n.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(3)) qt.Check(t, must.String(must.Node(n.LookupByString("X"))), qt.Equals, "fine") qt.Check(t, must.String(must.Node(n.LookupByString("Z"))), qt.Equals, "super") n := must.Node(n.LookupByString("M")) - qt.Check(t, n.Length(), qt.Equals, int64(2)) + l, err = n.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(2)) qt.Check(t, must.String(must.Node(n.LookupByString("A"))), qt.Equals, "thanks") qt.Check(t, must.String(must.Node(n.LookupByString("B"))), qt.Equals, "really") }) diff --git a/fluent/toInterfaceValue.go b/fluent/toInterfaceValue.go index b9ed8beb..e72e7939 100644 --- a/fluent/toInterfaceValue.go +++ b/fluent/toInterfaceValue.go @@ -34,7 +34,11 @@ func ToInterface(node datamodel.Node) (interface{}, error) { case datamodel.Kind_Link: return node.AsLink() case datamodel.Kind_Map: - outMap := make(map[string]interface{}, node.Length()) + l, err := node.Length() + if err != nil { + return nil, err + } + outMap := make(map[string]interface{}, l) for mi := node.MapIterator(); !mi.Done(); { k, v, err := mi.Next() if err != nil { @@ -52,7 +56,11 @@ func ToInterface(node datamodel.Node) (interface{}, error) { } return outMap, nil case datamodel.Kind_List: - outList := make([]interface{}, 0, node.Length()) + l, err := node.Length() + if err != nil { + return nil, err + } + outList := make([]interface{}, 0, l) for li := node.ListIterator(); !li.Done(); { _, v, err := li.Next() if err != nil { diff --git a/linking/linkingExamples_test.go b/linking/linkingExamples_test.go index 73984737..ef70bad2 100644 --- a/linking/linkingExamples_test.go +++ b/linking/linkingExamples_test.go @@ -126,7 +126,8 @@ func ExampleLinkSystem_Load() { } // Tada! We have the data as node that we can traverse and use as desired. - fmt.Printf("we loaded a %s with %d entries\n", n.Kind(), n.Length()) + l, _ := n.Length() + fmt.Printf("we loaded a %s with %d entries\n", n.Kind(), l) // Output: // we loaded a map with 1 entries diff --git a/node/basicnode/bool.go b/node/basicnode/bool.go index ecf85fad..b836a756 100644 --- a/node/basicnode/bool.go +++ b/node/basicnode/bool.go @@ -43,8 +43,8 @@ func (plainBool) MapIterator() datamodel.MapIterator { func (plainBool) ListIterator() datamodel.ListIterator { return nil } -func (plainBool) Length() int64 { - return -1 +func (plainBool) Length() (int64, error) { + return -1, nil } func (plainBool) IsAbsent() bool { return false diff --git a/node/basicnode/bytes.go b/node/basicnode/bytes.go index 566c32fe..e10f0119 100644 --- a/node/basicnode/bytes.go +++ b/node/basicnode/bytes.go @@ -46,8 +46,8 @@ func (plainBytes) MapIterator() datamodel.MapIterator { func (plainBytes) ListIterator() datamodel.ListIterator { return nil } -func (plainBytes) Length() int64 { - return -1 +func (plainBytes) Length() (int64, error) { + return -1, nil } func (plainBytes) IsAbsent() bool { return false diff --git a/node/basicnode/bytes_stream.go b/node/basicnode/bytes_stream.go index ad238bcf..7ef24e99 100644 --- a/node/basicnode/bytes_stream.go +++ b/node/basicnode/bytes_stream.go @@ -46,8 +46,8 @@ func (streamBytes) MapIterator() datamodel.MapIterator { func (streamBytes) ListIterator() datamodel.ListIterator { return nil } -func (streamBytes) Length() int64 { - return -1 +func (streamBytes) Length() (int64, error) { + return -1, nil } func (streamBytes) IsAbsent() bool { return false diff --git a/node/basicnode/float.go b/node/basicnode/float.go index f00db30d..87e3af76 100644 --- a/node/basicnode/float.go +++ b/node/basicnode/float.go @@ -43,8 +43,8 @@ func (plainFloat) MapIterator() datamodel.MapIterator { func (plainFloat) ListIterator() datamodel.ListIterator { return nil } -func (plainFloat) Length() int64 { - return -1 +func (plainFloat) Length() (int64, error) { + return -1, nil } func (plainFloat) IsAbsent() bool { return false diff --git a/node/basicnode/int.go b/node/basicnode/int.go index d8e8ac2f..7db3785d 100644 --- a/node/basicnode/int.go +++ b/node/basicnode/int.go @@ -58,8 +58,8 @@ func (plainInt) MapIterator() datamodel.MapIterator { func (plainInt) ListIterator() datamodel.ListIterator { return nil } -func (plainInt) Length() int64 { - return -1 +func (plainInt) Length() (int64, error) { + return -1, nil } func (plainInt) IsAbsent() bool { return false @@ -117,8 +117,8 @@ func (plainUint) MapIterator() datamodel.MapIterator { func (plainUint) ListIterator() datamodel.ListIterator { return nil } -func (plainUint) Length() int64 { - return -1 +func (plainUint) Length() (int64, error) { + return -1, nil } func (plainUint) IsAbsent() bool { return false diff --git a/node/basicnode/link.go b/node/basicnode/link.go index 41d2cf0d..c377e585 100644 --- a/node/basicnode/link.go +++ b/node/basicnode/link.go @@ -44,8 +44,8 @@ func (plainLink) MapIterator() datamodel.MapIterator { func (plainLink) ListIterator() datamodel.ListIterator { return nil } -func (plainLink) Length() int64 { - return -1 +func (plainLink) Length() (int64, error) { + return -1, nil } func (plainLink) IsAbsent() bool { return false diff --git a/node/basicnode/list.go b/node/basicnode/list.go index 6f7582bb..a4b7b82c 100644 --- a/node/basicnode/list.go +++ b/node/basicnode/list.go @@ -31,7 +31,11 @@ func (plainList) LookupByNode(datamodel.Node) (datamodel.Node, error) { return mixins.List{TypeName: "list"}.LookupByNode(nil) } func (n *plainList) LookupByIndex(idx int64) (datamodel.Node, error) { - if n.Length() <= idx { + l, err := n.Length() + if err != nil { + return nil, err + } + if l <= idx { return nil, datamodel.ErrNotExists{Segment: datamodel.PathSegmentOfInt(idx)} } return n.x[idx], nil @@ -49,8 +53,8 @@ func (plainList) MapIterator() datamodel.MapIterator { func (n *plainList) ListIterator() datamodel.ListIterator { return &plainList_ListIterator{n, 0} } -func (n *plainList) Length() int64 { - return int64(len(n.x)) +func (n *plainList) Length() (int64, error) { + return int64(len(n.x)), nil } func (plainList) IsAbsent() bool { return false diff --git a/node/basicnode/map.go b/node/basicnode/map.go index 9a86fc52..b872a6c8 100644 --- a/node/basicnode/map.go +++ b/node/basicnode/map.go @@ -59,8 +59,8 @@ func (n *plainMap) MapIterator() datamodel.MapIterator { func (plainMap) ListIterator() datamodel.ListIterator { return nil } -func (n *plainMap) Length() int64 { - return int64(len(n.t)) +func (n *plainMap) Length() (int64, error) { + return int64(len(n.t)), nil } func (plainMap) IsAbsent() bool { return false diff --git a/node/basicnode/string.go b/node/basicnode/string.go index 3d9856c8..1e0bf5e6 100644 --- a/node/basicnode/string.go +++ b/node/basicnode/string.go @@ -48,8 +48,8 @@ func (plainString) MapIterator() datamodel.MapIterator { func (plainString) ListIterator() datamodel.ListIterator { return nil } -func (plainString) Length() int64 { - return -1 +func (plainString) Length() (int64, error) { + return -1, nil } func (plainString) IsAbsent() bool { return false diff --git a/node/bindnode/infer_test.go b/node/bindnode/infer_test.go index cc7e40d0..ce52348f 100644 --- a/node/bindnode/infer_test.go +++ b/node/bindnode/infer_test.go @@ -616,7 +616,11 @@ func useNodeAsKind(node datamodel.Node, asKind datamodel.Kind) error { return err } - switch l := node.Length(); l { + l, err := node.Length() + if err != nil { + return err + } + switch l { case 2: case -1: // Return a dummy error to signal whether Length failed. @@ -657,7 +661,11 @@ func useNodeAsKind(node datamodel.Node, asKind datamodel.Kind) error { return err } - switch l := node.Length(); l { + l, err := node.Length() + if err != nil { + return err + } + switch l { case 2: case -1: // Return a dummy error to signal whether Length failed. diff --git a/node/bindnode/node.go b/node/bindnode/node.go index 04c1d760..81a1d89a 100644 --- a/node/bindnode/node.go +++ b/node/bindnode/node.go @@ -450,21 +450,21 @@ func (w *_node) ListIterator() datamodel.ListIterator { return nil } -func (w *_node) Length() int64 { +func (w *_node) Length() (int64, error) { val := nonPtrVal(w.val) switch w.Kind() { case datamodel.Kind_Map: switch typ := w.schemaType.(type) { case *schema.TypeStruct: - return int64(len(typ.Fields())) + return int64(len(typ.Fields())), nil case *schema.TypeUnion: - return 1 + return 1, nil } - return int64(val.FieldByName("Keys").Len()) + return int64(val.FieldByName("Keys").Len()), nil case datamodel.Kind_List: - return int64(val.Len()) + return int64(val.Len()), nil } - return -1 + return -1, nil } // TODO: better story around pointers and absent/null @@ -1647,8 +1647,8 @@ func (_uintNode) MapIterator() datamodel.MapIterator { func (_uintNode) ListIterator() datamodel.ListIterator { return nil } -func (_uintNode) Length() int64 { - return -1 +func (_uintNode) Length() (int64, error) { + return -1, nil } func (_uintNode) IsAbsent() bool { return false @@ -1705,8 +1705,8 @@ func (_uintNodeRepr) MapIterator() datamodel.MapIterator { func (_uintNodeRepr) ListIterator() datamodel.ListIterator { return nil } -func (_uintNodeRepr) Length() int64 { - return -1 +func (_uintNodeRepr) Length() (int64, error) { + return -1, nil } func (_uintNodeRepr) IsAbsent() bool { return false diff --git a/node/bindnode/repr.go b/node/bindnode/repr.go index 3c08a2b8..91f5b695 100644 --- a/node/bindnode/repr.go +++ b/node/bindnode/repr.go @@ -425,16 +425,16 @@ func (w *_nodeRepr) lengthMinusTrailingAbsents() int64 { return 0 } -func (w *_nodeRepr) Length() int64 { +func (w *_nodeRepr) Length() (int64, error) { switch stg := reprStrategy(w.schemaType).(type) { case schema.StructRepresentation_Stringjoin: - return -1 + return -1, nil case schema.StructRepresentation_Map: - return w.lengthMinusAbsents() + return w.lengthMinusAbsents(), nil case schema.StructRepresentation_Tuple: - return w.lengthMinusTrailingAbsents() + return w.lengthMinusTrailingAbsents(), nil case schema.StructRepresentation_ListPairs: - return w.lengthMinusAbsents() + return w.lengthMinusAbsents(), nil case schema.UnionRepresentation_Keyed: return (*_node)(w).Length() case schema.UnionRepresentation_Kinded: diff --git a/node/gendemo/ipldsch_satisfaction.go b/node/gendemo/ipldsch_satisfaction.go index fb60a350..0983c3b6 100644 --- a/node/gendemo/ipldsch_satisfaction.go +++ b/node/gendemo/ipldsch_satisfaction.go @@ -74,8 +74,8 @@ func (Bar) MapIterator() datamodel.MapIterator { func (Bar) ListIterator() datamodel.ListIterator { return nil } -func (Bar) Length() int64 { - return -1 +func (Bar) Length() (int64, error) { + return -1, nil } func (Bar) IsAbsent() bool { return false @@ -283,8 +283,8 @@ func (Baz) MapIterator() datamodel.MapIterator { func (Baz) ListIterator() datamodel.ListIterator { return nil } -func (Baz) Length() int64 { - return -1 +func (Baz) Length() (int64, error) { + return -1, nil } func (Baz) IsAbsent() bool { return false @@ -488,8 +488,8 @@ func (Foo) MapIterator() datamodel.MapIterator { func (Foo) ListIterator() datamodel.ListIterator { return nil } -func (Foo) Length() int64 { - return -1 +func (Foo) Length() (int64, error) { + return -1, nil } func (Foo) IsAbsent() bool { return false @@ -693,8 +693,8 @@ func (Int) MapIterator() datamodel.MapIterator { func (Int) ListIterator() datamodel.ListIterator { return nil } -func (Int) Length() int64 { - return -1 +func (Int) Length() (int64, error) { + return -1, nil } func (Int) IsAbsent() bool { return false @@ -970,8 +970,8 @@ func (itr *_Map__String__Msg3__MapItr) Done() bool { func (Map__String__Msg3) ListIterator() datamodel.ListIterator { return nil } -func (n Map__String__Msg3) Length() int64 { - return int64(len(n.t)) +func (n Map__String__Msg3) Length() (int64, error) { + return int64(len(n.t)), nil } func (Map__String__Msg3) IsAbsent() bool { return false @@ -1304,8 +1304,8 @@ func (itr *_Map__String__Msg3__ReprMapItr) Done() bool { func (_Map__String__Msg3__Repr) ListIterator() datamodel.ListIterator { return nil } -func (rn *_Map__String__Msg3__Repr) Length() int64 { - return int64(len(rn.t)) +func (rn *_Map__String__Msg3__Repr) Length() (int64, error) { + return int64(len(rn.t)), nil } func (_Map__String__Msg3__Repr) IsAbsent() bool { return false @@ -1701,8 +1701,8 @@ func (itr *_Msg3__MapItr) Done() bool { func (Msg3) ListIterator() datamodel.ListIterator { return nil } -func (Msg3) Length() int64 { - return 3 +func (Msg3) Length() (int64, error) { + return 3, nil } func (Msg3) IsAbsent() bool { return false @@ -2194,9 +2194,9 @@ func (itr *_Msg3__ReprMapItr) Done() bool { func (_Msg3__Repr) ListIterator() datamodel.ListIterator { return nil } -func (rn *_Msg3__Repr) Length() int64 { +func (rn *_Msg3__Repr) Length() (int64, error) { l := 3 - return int64(l) + return int64(l), nil } func (_Msg3__Repr) IsAbsent() bool { return false @@ -2669,8 +2669,8 @@ func (String) MapIterator() datamodel.MapIterator { func (String) ListIterator() datamodel.ListIterator { return nil } -func (String) Length() int64 { - return -1 +func (String) Length() (int64, error) { + return -1, nil } func (String) IsAbsent() bool { return false @@ -2933,8 +2933,8 @@ func (itr *_UnionKinded__MapItr) Done() bool { func (UnionKinded) ListIterator() datamodel.ListIterator { return nil } -func (UnionKinded) Length() int64 { - return 1 +func (UnionKinded) Length() (int64, error) { + return 1, nil } func (UnionKinded) IsAbsent() bool { return false @@ -3352,8 +3352,8 @@ func (n *_UnionKinded__Repr) MapIterator() datamodel.MapIterator { func (n *_UnionKinded__Repr) ListIterator() datamodel.ListIterator { return nil } -func (n *_UnionKinded__Repr) Length() int64 { - return -1 +func (n *_UnionKinded__Repr) Length() (int64, error) { + return -1, nil } func (n *_UnionKinded__Repr) IsAbsent() bool { return false @@ -3585,7 +3585,11 @@ func (na *_UnionKinded__ReprAssembler) AssignNode(v datamodel.Node) error { v2, _ := v.AsBytes() return na.AssignBytes(v2) case datamodel.Kind_Map: - na, err := na.BeginMap(v.Length()) + l, err := v.Length() + if err != nil { + return err + } + na, err := na.BeginMap(l) if err != nil { return err } @@ -3604,7 +3608,11 @@ func (na *_UnionKinded__ReprAssembler) AssignNode(v datamodel.Node) error { } return na.Finish() case datamodel.Kind_List: - na, err := na.BeginList(v.Length()) + l, err := v.Length() + if err != nil { + return err + } + na, err := na.BeginList(l) if err != nil { return err } diff --git a/node/tests/listSpecs.go b/node/tests/listSpecs.go index 8dddf10d..0aed325f 100644 --- a/node/tests/listSpecs.go +++ b/node/tests/listSpecs.go @@ -18,7 +18,9 @@ func SpecTestListString(t *testing.T, np datamodel.NodePrototype) { la.AssembleValue().AssignString("three") }) t.Run("reads back out", func(t *testing.T) { - qt.Check(t, n.Length(), qt.Equals, int64(3)) + l, err := n.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(3)) v, err := n.LookupByIndex(0) qt.Check(t, err, qt.IsNil) diff --git a/node/tests/mapSpecs.go b/node/tests/mapSpecs.go index 9aaa9e57..08f5de3a 100644 --- a/node/tests/mapSpecs.go +++ b/node/tests/mapSpecs.go @@ -13,7 +13,9 @@ func SpecTestMapStrInt(t *testing.T, np datamodel.NodePrototype) { t.Run("map, 3 entries", func(t *testing.T) { n := buildMapStrIntN3(np) t.Run("reads back out", func(t *testing.T) { - qt.Check(t, n.Length(), qt.Equals, int64(3)) + l, err := n.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(3)) v, err := n.LookupByString("whee") qt.Check(t, err, qt.IsNil) @@ -124,7 +126,9 @@ func SpecTestMapStrInt(t *testing.T, np datamodel.NodePrototype) { // ... and neither of these should've had visible effects! qt.Check(t, ma.Finish(), qt.IsNil) n := nb.Build() - qt.Check(t, n.Length(), qt.Equals, int64(1)) + l, err := n.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(1)) v, err := n.LookupByString("whee") qt.Check(t, err, qt.IsNil) v2, err := v.AsInt() @@ -169,7 +173,9 @@ func SpecTestMapStrMapStrInt(t *testing.T, np datamodel.NodePrototype) { n := nb.Build() t.Run("reads back out", func(t *testing.T) { - qt.Check(t, n.Length(), qt.Equals, int64(3)) + l, err := n.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(3)) v, err := n.LookupByString("woot") qt.Check(t, err, qt.IsNil) @@ -214,7 +220,9 @@ func SpecTestMapStrListStr(t *testing.T, np datamodel.NodePrototype) { n := nb.Build() t.Run("reads back out", func(t *testing.T) { - qt.Check(t, n.Length(), qt.Equals, int64(3)) + l, err := n.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(3)) v, err := n.LookupByString("qwer") qt.Check(t, err, qt.IsNil) diff --git a/node/tests/schemaLists.go b/node/tests/schemaLists.go index bf2945fc..a2fd1c93 100644 --- a/node/tests/schemaLists.go +++ b/node/tests/schemaLists.go @@ -33,7 +33,9 @@ func SchemaTestListsContainingMaybe(t *testing.T, engine Engine) { }).(schema.TypedNode) t.Run("typed-read", func(t *testing.T) { qt.Assert(t, n.Kind(), qt.Equals, datamodel.Kind_List) - qt.Check(t, n.Length(), qt.Equals, int64(2)) + l, err := n.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(2)) qt.Check(t, must.String(must.Node(n.LookupByIndex(0))), qt.Equals, "1") qt.Check(t, must.String(must.Node(n.LookupByIndex(1))), qt.Equals, "2") @@ -41,13 +43,15 @@ func SchemaTestListsContainingMaybe(t *testing.T, engine Engine) { qt.Check(t, must.String(must.Node(n.LookupBySegment(datamodel.PathSegmentOfInt(0)))), qt.Equals, "1") qt.Check(t, must.String(must.Node(n.LookupByNode(basicnode.NewInt(0)))), qt.Equals, "1") - _, err := n.LookupByIndex(3) + _, err = n.LookupByIndex(3) qt.Check(t, err, qt.ErrorAs, &datamodel.ErrNotExists{}) }) t.Run("repr-read", func(t *testing.T) { nr := n.Representation() qt.Assert(t, nr.Kind(), qt.Equals, datamodel.Kind_List) - qt.Check(t, nr.Length(), qt.Equals, int64(2)) + l, err := nr.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(2)) qt.Check(t, must.String(must.Node(nr.LookupByIndex(0))), qt.Equals, "1") qt.Check(t, must.String(must.Node(nr.LookupByIndex(1))), qt.Equals, "2") @@ -55,7 +59,7 @@ func SchemaTestListsContainingMaybe(t *testing.T, engine Engine) { qt.Check(t, must.String(must.Node(n.LookupBySegment(datamodel.PathSegmentOfInt(0)))), qt.Equals, "1") qt.Check(t, must.String(must.Node(n.LookupByNode(basicnode.NewInt(0)))), qt.Equals, "1") - _, err := n.LookupByIndex(3) + _, err = n.LookupByIndex(3) qt.Check(t, err, qt.ErrorAs, &datamodel.ErrNotExists{}) }) }) @@ -78,19 +82,23 @@ func SchemaTestListsContainingMaybe(t *testing.T, engine Engine) { }).(schema.TypedNode) t.Run("typed-read", func(t *testing.T) { qt.Assert(t, n.Kind(), qt.Equals, datamodel.Kind_List) - qt.Check(t, n.Length(), qt.Equals, int64(2)) + l, err := n.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(2)) qt.Check(t, must.String(must.Node(n.LookupByIndex(0))), qt.Equals, "1") qt.Check(t, must.Node(n.LookupByIndex(1)), qt.Equals, datamodel.Null) - _, err := n.LookupByIndex(3) + _, err = n.LookupByIndex(3) qt.Check(t, err, qt.ErrorAs, &datamodel.ErrNotExists{}) }) t.Run("repr-read", func(t *testing.T) { nr := n.Representation() qt.Assert(t, nr.Kind(), qt.Equals, datamodel.Kind_List) - qt.Check(t, nr.Length(), qt.Equals, int64(2)) + l, err := nr.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(2)) qt.Check(t, must.String(must.Node(n.LookupByIndex(0))), qt.Equals, "1") qt.Check(t, must.Node(n.LookupByIndex(1)), qt.Equals, datamodel.Null) - _, err := n.LookupByIndex(3) + _, err = n.LookupByIndex(3) qt.Check(t, err, qt.ErrorAs, &datamodel.ErrNotExists{}) }) }) @@ -147,10 +155,18 @@ func SchemaTestListsContainingLists(t *testing.T, engine Engine) { }).(schema.TypedNode) t.Run("typed-read", func(t *testing.T) { qt.Assert(t, n.Kind(), qt.Equals, datamodel.Kind_List) - qt.Assert(t, n.Length(), qt.Equals, int64(3)) - qt.Assert(t, must.Node(n.LookupByIndex(0)).Length(), qt.Equals, int64(3)) - qt.Assert(t, must.Node(n.LookupByIndex(1)).Length(), qt.Equals, int64(1)) - qt.Assert(t, must.Node(n.LookupByIndex(2)).Length(), qt.Equals, int64(2)) + l, err := n.Length() + qt.Check(t, err, qt.IsNil) + qt.Assert(t, l, qt.Equals, int64(3)) + l, err = must.Node(n.LookupByIndex(0)).Length() + qt.Check(t, err, qt.IsNil) + qt.Assert(t, l, qt.Equals, int64(3)) + l, err = must.Node(n.LookupByIndex(1)).Length() + qt.Check(t, err, qt.IsNil) + qt.Assert(t, l, qt.Equals, int64(1)) + l, err = must.Node(n.LookupByIndex(2)).Length() + qt.Check(t, err, qt.IsNil) + qt.Assert(t, l, qt.Equals, int64(2)) qt.Check(t, must.String(must.Node(must.Node(must.Node(n.LookupByIndex(0)).LookupByIndex(0)).LookupByString("field"))), qt.Equals, "11") qt.Check(t, must.String(must.Node(must.Node(must.Node(n.LookupByIndex(0)).LookupByIndex(2)).LookupByString("field"))), qt.Equals, "13") @@ -160,10 +176,18 @@ func SchemaTestListsContainingLists(t *testing.T, engine Engine) { t.Run("repr-read", func(t *testing.T) { nr := n.Representation() qt.Assert(t, nr.Kind(), qt.Equals, datamodel.Kind_List) - qt.Assert(t, nr.Length(), qt.Equals, int64(3)) - qt.Assert(t, must.Node(nr.LookupByIndex(0)).Length(), qt.Equals, int64(3)) - qt.Assert(t, must.Node(nr.LookupByIndex(1)).Length(), qt.Equals, int64(1)) - qt.Assert(t, must.Node(nr.LookupByIndex(2)).Length(), qt.Equals, int64(2)) + l, err := nr.Length() + qt.Assert(t, err, qt.IsNil) + qt.Assert(t, l, qt.Equals, int64(3)) + l, err = must.Node(nr.LookupByIndex(0)).Length() + qt.Assert(t, err, qt.IsNil) + qt.Assert(t, l, qt.Equals, int64(3)) + l, err = must.Node(nr.LookupByIndex(1)).Length() + qt.Assert(t, err, qt.IsNil) + qt.Assert(t, l, qt.Equals, int64(1)) + l, err = must.Node(nr.LookupByIndex(2)).Length() + qt.Assert(t, err, qt.IsNil) + qt.Assert(t, l, qt.Equals, int64(2)) qt.Check(t, must.String(must.Node(must.Node(must.Node(nr.LookupByIndex(0)).LookupByIndex(0)).LookupByString("encoded"))), qt.Equals, "11") qt.Check(t, must.String(must.Node(must.Node(must.Node(nr.LookupByIndex(0)).LookupByIndex(2)).LookupByString("encoded"))), qt.Equals, "13") diff --git a/node/tests/schemaMaps.go b/node/tests/schemaMaps.go index b804870a..7b779df9 100644 --- a/node/tests/schemaMaps.go +++ b/node/tests/schemaMaps.go @@ -32,19 +32,23 @@ func SchemaTestMapsContainingMaybe(t *testing.T, engine Engine) { }).(schema.TypedNode) t.Run("typed-read", func(t *testing.T) { qt.Assert(t, n.Kind(), qt.Equals, datamodel.Kind_Map) - qt.Check(t, n.Length(), qt.Equals, int64(2)) + l, err := n.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(2)) qt.Check(t, must.String(must.Node(n.LookupByString("one"))), qt.Equals, "1") qt.Check(t, must.String(must.Node(n.LookupByString("two"))), qt.Equals, "2") - _, err := n.LookupByString("miss") + _, err = n.LookupByString("miss") qt.Check(t, err, qt.ErrorAs, &datamodel.ErrNotExists{}) }) t.Run("repr-read", func(t *testing.T) { nr := n.Representation() qt.Assert(t, nr.Kind(), qt.Equals, datamodel.Kind_Map) - qt.Check(t, nr.Length(), qt.Equals, int64(2)) + l, err := nr.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(2)) qt.Check(t, must.String(must.Node(nr.LookupByString("one"))), qt.Equals, "1") qt.Check(t, must.String(must.Node(nr.LookupByString("two"))), qt.Equals, "2") - _, err := nr.LookupByString("miss") + _, err = nr.LookupByString("miss") qt.Check(t, err, qt.ErrorAs, &datamodel.ErrNotExists{}) }) }) @@ -67,19 +71,23 @@ func SchemaTestMapsContainingMaybe(t *testing.T, engine Engine) { }).(schema.TypedNode) t.Run("typed-read", func(t *testing.T) { qt.Assert(t, n.Kind(), qt.Equals, datamodel.Kind_Map) - qt.Check(t, n.Length(), qt.Equals, int64(2)) + l, err := n.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(2)) qt.Check(t, must.String(must.Node(n.LookupByString("one"))), qt.Equals, "1") qt.Check(t, must.Node(n.LookupByString("none")), qt.Equals, datamodel.Null) - _, err := n.LookupByString("miss") + _, err = n.LookupByString("miss") qt.Check(t, err, qt.ErrorAs, &datamodel.ErrNotExists{}) }) t.Run("repr-read", func(t *testing.T) { nr := n.Representation() qt.Assert(t, nr.Kind(), qt.Equals, datamodel.Kind_Map) - qt.Check(t, nr.Length(), qt.Equals, int64(2)) + l, err := nr.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(2)) qt.Check(t, must.String(must.Node(nr.LookupByString("one"))), qt.Equals, "1") qt.Check(t, must.Node(nr.LookupByString("none")), qt.Equals, datamodel.Null) - _, err := nr.LookupByString("miss") + _, err = nr.LookupByString("miss") qt.Check(t, err, qt.ErrorAs, &datamodel.ErrNotExists{}) }) }) @@ -133,33 +141,45 @@ func SchemaTestMapsContainingMaps(t *testing.T, engine Engine) { reading := func(t *testing.T, n datamodel.Node, fieldName string) { withNode(n, func(n datamodel.Node) { qt.Assert(t, n.Kind(), qt.Equals, datamodel.Kind_Map) - qt.Check(t, n.Length(), qt.Equals, int64(3)) + l, err := n.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(3)) withNode(must.Node(n.LookupByString("one")), func(n datamodel.Node) { qt.Assert(t, n.Kind(), qt.Equals, datamodel.Kind_Map) - qt.Check(t, n.Length(), qt.Equals, int64(2)) + l, err := n.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(2)) withNode(must.Node(n.LookupByString("zot")), func(n datamodel.Node) { qt.Assert(t, n.Kind(), qt.Equals, datamodel.Kind_Map) - qt.Check(t, n.Length(), qt.Equals, int64(1)) + l, err := n.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(1)) qt.Check(t, must.String(must.Node(n.LookupByString(fieldName))), qt.Equals, "11") }) withNode(must.Node(n.LookupByString("zop")), func(n datamodel.Node) { qt.Assert(t, n.Kind(), qt.Equals, datamodel.Kind_Map) - qt.Check(t, n.Length(), qt.Equals, int64(1)) + l, err := n.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(1)) qt.Check(t, must.String(must.Node(n.LookupByString(fieldName))), qt.Equals, "12") }) }) withNode(must.Node(n.LookupByString("two")), func(n datamodel.Node) { - qt.Check(t, n.Length(), qt.Equals, int64(1)) + l, err := n.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(1)) withNode(must.Node(n.LookupByString("zim")), func(n datamodel.Node) { qt.Assert(t, n.Kind(), qt.Equals, datamodel.Kind_Map) - qt.Check(t, n.Length(), qt.Equals, int64(1)) + l, err := n.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(1)) qt.Check(t, must.String(must.Node(n.LookupByString(fieldName))), qt.Equals, "21") }) }) withNode(must.Node(n.LookupByString("none")), func(n datamodel.Node) { qt.Check(t, n, NodeContentEquals, datamodel.Null) }) - _, err := n.LookupByString("miss") + _, err = n.LookupByString("miss") qt.Check(t, err, qt.ErrorAs, &datamodel.ErrNotExists{}) }) } @@ -217,7 +237,9 @@ func SchemaTestMapsWithComplexKeys(t *testing.T, engine Engine) { }).(schema.TypedNode) t.Run("typed-read", func(t *testing.T) { qt.Assert(t, n.Kind(), qt.Equals, datamodel.Kind_Map) - qt.Check(t, n.Length(), qt.Equals, int64(3)) + l, err := n.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(3)) n2 := must.Node(n.LookupByString("c:d")) qt.Assert(t, n2.Kind(), qt.Equals, datamodel.Kind_String) qt.Check(t, must.String(n2), qt.Equals, "2") @@ -225,7 +247,9 @@ func SchemaTestMapsWithComplexKeys(t *testing.T, engine Engine) { t.Run("repr-read", func(t *testing.T) { nr := n.Representation() qt.Assert(t, nr.Kind(), qt.Equals, datamodel.Kind_Map) - qt.Check(t, nr.Length(), qt.Equals, int64(3)) + l, err := nr.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(3)) n2 := must.Node(nr.LookupByString("c:d")) qt.Assert(t, n2.Kind(), qt.Equals, datamodel.Kind_String) qt.Check(t, must.String(n2), qt.Equals, "2") diff --git a/node/tests/schemaScalars.go b/node/tests/schemaScalars.go index 54d5e1b1..0572ea14 100644 --- a/node/tests/schemaScalars.go +++ b/node/tests/schemaScalars.go @@ -138,7 +138,9 @@ func SchemaTestScalars(t *testing.T, engine Engine) { qt.Check(t, err, qt.Not(qt.IsNil)) qt.Check(t, n.MapIterator(), qt.IsNil) qt.Check(t, n.ListIterator(), qt.IsNil) - qt.Check(t, n.Length(), qt.Equals, int64(-1)) + l, err := n.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(-1)) qt.Check(t, n.IsAbsent(), qt.IsFalse) qt.Check(t, n.IsNull(), qt.IsFalse) }) diff --git a/node/tests/schemaStruct.go b/node/tests/schemaStruct.go index c614fdf8..6a014b49 100644 --- a/node/tests/schemaStruct.go +++ b/node/tests/schemaStruct.go @@ -101,7 +101,9 @@ func SchemaTestStructNesting(t *testing.T, engine Engine) { }).(schema.TypedNode) t.Run("typed-read", func(t *testing.T) { qt.Assert(t, n.Kind(), qt.Equals, datamodel.Kind_Map) - qt.Check(t, n.Length(), qt.Equals, int64(1)) + l, err := n.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(1)) n2 := must.Node(n.LookupByString("x")) qt.Assert(t, n2.Kind(), qt.Equals, datamodel.Kind_Map) @@ -117,7 +119,9 @@ func SchemaTestStructNesting(t *testing.T, engine Engine) { t.Run("repr-read", func(t *testing.T) { nr := n.Representation() qt.Assert(t, nr.Kind(), qt.Equals, datamodel.Kind_Map) - qt.Check(t, nr.Length(), qt.Equals, int64(1)) + l, err := nr.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(1)) n2 := must.Node(nr.LookupByString("r")) qt.Assert(t, n2.Kind(), qt.Equals, datamodel.Kind_Map) diff --git a/node/tests/schemaStructReprListpairs.go b/node/tests/schemaStructReprListpairs.go index f9e50c19..bae68a6c 100644 --- a/node/tests/schemaStructReprListpairs.go +++ b/node/tests/schemaStructReprListpairs.go @@ -51,16 +51,22 @@ func SchemaTestStructReprListPairs(t *testing.T, engine Engine) { }).(schema.TypedNode) t.Run("typed-read", func(t *testing.T) { qt.Assert(t, n.Kind(), qt.Equals, datamodel.Kind_Map) - qt.Check(t, n.Length(), qt.Equals, int64(1)) + l, err := n.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(1)) qt.Check(t, must.String(must.Node(n.LookupByString("field"))), qt.Equals, "valoo") }) t.Run("repr-read", func(t *testing.T) { nr := n.Representation() qt.Assert(t, nr.Kind(), qt.Equals, datamodel.Kind_List) - qt.Check(t, nr.Length(), qt.Equals, int64(1)) + l, err := nr.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(1)) kv := must.Node(nr.LookupByIndex(0)) qt.Assert(t, nr.Kind(), qt.Equals, datamodel.Kind_List) - qt.Check(t, kv.Length(), qt.Equals, int64(2)) + l, err = kv.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(2)) qt.Check(t, must.String(must.Node(kv.LookupByIndex(0))), qt.Equals, "field") qt.Check(t, must.String(must.Node(kv.LookupByIndex(1))), qt.Equals, "valoo") }) @@ -93,42 +99,58 @@ func SchemaTestStructReprListPairs(t *testing.T, engine Engine) { t.Run("typed-read", func(t *testing.T) { qt.Assert(t, n.Kind(), qt.Equals, datamodel.Kind_Map) - qt.Check(t, n.Length(), qt.Equals, int64(4)) + l, err := n.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(4)) qt.Check(t, must.String(must.Node(n.LookupByString("foo"))), qt.Equals, "0") qt.Check(t, must.String(must.Node(n.LookupByString("bar"))), qt.Equals, "1") qt.Check(t, must.String(must.Node(n.LookupByString("baz"))), qt.Equals, "2") qux := must.Node(n.LookupByString("qux")) qt.Assert(t, qux.Kind(), qt.Equals, datamodel.Kind_List) - qt.Check(t, qux.Length(), qt.Equals, int64(2)) + l, err = qux.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(2)) qt.Check(t, must.String(must.Node(qux.LookupByIndex(0))), qt.Equals, "3") qt.Check(t, must.String(must.Node(qux.LookupByIndex(1))), qt.Equals, "4") }) t.Run("repr-read", func(t *testing.T) { nr := n.Representation() qt.Assert(t, nr.Kind(), qt.Equals, datamodel.Kind_List) - qt.Check(t, nr.Length(), qt.Equals, int64(4)) + l, err := nr.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(4)) kv := must.Node(nr.LookupByIndex(0)) qt.Assert(t, nr.Kind(), qt.Equals, datamodel.Kind_List) - qt.Check(t, kv.Length(), qt.Equals, int64(2)) + l, err = kv.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(2)) qt.Check(t, must.String(must.Node(kv.LookupByIndex(0))), qt.Equals, "foo") qt.Check(t, must.String(must.Node(kv.LookupByIndex(1))), qt.Equals, "0") kv = must.Node(nr.LookupByIndex(1)) qt.Assert(t, nr.Kind(), qt.Equals, datamodel.Kind_List) - qt.Check(t, kv.Length(), qt.Equals, int64(2)) + l, err = kv.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(2)) qt.Check(t, must.String(must.Node(kv.LookupByIndex(0))), qt.Equals, "bar") qt.Check(t, must.String(must.Node(kv.LookupByIndex(1))), qt.Equals, "1") kv = must.Node(nr.LookupByIndex(2)) qt.Assert(t, nr.Kind(), qt.Equals, datamodel.Kind_List) - qt.Check(t, kv.Length(), qt.Equals, int64(2)) + l, err = kv.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(2)) qt.Check(t, must.String(must.Node(kv.LookupByIndex(0))), qt.Equals, "baz") qt.Check(t, must.String(must.Node(kv.LookupByIndex(1))), qt.Equals, "2") kv = must.Node(nr.LookupByIndex(3)) qt.Assert(t, nr.Kind(), qt.Equals, datamodel.Kind_List) - qt.Check(t, kv.Length(), qt.Equals, int64(2)) + l, err = kv.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(2)) qt.Check(t, must.String(must.Node(kv.LookupByIndex(0))), qt.Equals, "qux") qux := must.Node(kv.LookupByIndex(1)) qt.Assert(t, qux.Kind(), qt.Equals, datamodel.Kind_List) - qt.Check(t, qux.Length(), qt.Equals, int64(2)) + l, err = qux.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(2)) qt.Check(t, must.String(must.Node(qux.LookupByIndex(0))), qt.Equals, "3") qt.Check(t, must.String(must.Node(qux.LookupByIndex(1))), qt.Equals, "4") }) @@ -197,32 +219,44 @@ func SchemaTestStructReprListPairs(t *testing.T, engine Engine) { }).(schema.TypedNode) t.Run("typed-read", func(t *testing.T) { qt.Assert(t, n.Kind(), qt.Equals, datamodel.Kind_Map) - qt.Check(t, n.Length(), qt.Equals, int64(4)) + l, err := n.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(4)) qt.Check(t, must.Node(n.LookupByString("foo")), qt.Equals, datamodel.Null) qt.Check(t, must.Node(n.LookupByString("bar")), qt.Equals, datamodel.Absent) qt.Check(t, must.Node(n.LookupByString("baz")), qt.Equals, datamodel.Absent) qux := must.Node(n.LookupByString("qux")) qt.Assert(t, qux.Kind(), qt.Equals, datamodel.Kind_List) - qt.Check(t, qux.Length(), qt.Equals, int64(2)) + l, err = qux.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(2)) qt.Check(t, must.String(must.Node(qux.LookupByIndex(0))), qt.Equals, "1") qt.Check(t, must.String(must.Node(qux.LookupByIndex(1))), qt.Equals, "2") }) t.Run("repr-read", func(t *testing.T) { nr := n.Representation() qt.Assert(t, nr.Kind(), qt.Equals, datamodel.Kind_List) - qt.Check(t, nr.Length(), qt.Equals, int64(2)) + l, err := nr.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(2)) kv := must.Node(nr.LookupByIndex(0)) qt.Assert(t, nr.Kind(), qt.Equals, datamodel.Kind_List) - qt.Check(t, kv.Length(), qt.Equals, int64(2)) + l, err = kv.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(2)) qt.Check(t, must.String(must.Node(kv.LookupByIndex(0))), qt.Equals, "foo") qt.Check(t, must.Node(kv.LookupByIndex(1)), qt.Equals, datamodel.Null) kv = must.Node(nr.LookupByIndex(1)) qt.Assert(t, nr.Kind(), qt.Equals, datamodel.Kind_List) - qt.Check(t, kv.Length(), qt.Equals, int64(2)) + l, err = kv.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(2)) qt.Check(t, must.String(must.Node(kv.LookupByIndex(0))), qt.Equals, "qux") qux := must.Node(kv.LookupByIndex(1)) qt.Assert(t, qux.Kind(), qt.Equals, datamodel.Kind_List) - qt.Check(t, qux.Length(), qt.Equals, int64(2)) + l, err = qux.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(2)) qt.Check(t, must.String(must.Node(qux.LookupByIndex(0))), qt.Equals, "1") qt.Check(t, must.String(must.Node(qux.LookupByIndex(1))), qt.Equals, "2") }) @@ -278,7 +312,9 @@ func SchemaTestStructReprListPairs(t *testing.T, engine Engine) { }).(schema.TypedNode) t.Run("typed-read", func(t *testing.T) { qt.Assert(t, n.Kind(), qt.Equals, datamodel.Kind_Map) - qt.Check(t, n.Length(), qt.Equals, int64(2)) + l, err := n.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(2)) qt.Check(t, must.String(must.Node(n.LookupByString("str"))), qt.Equals, "boop") lp := must.Node(n.LookupByString("lp")) qt.Check(t, lp.Kind(), qt.Equals, datamodel.Kind_Map) @@ -287,20 +323,28 @@ func SchemaTestStructReprListPairs(t *testing.T, engine Engine) { t.Run("repr-read", func(t *testing.T) { nr := n.Representation() qt.Assert(t, nr.Kind(), qt.Equals, datamodel.Kind_List) - qt.Check(t, nr.Length(), qt.Equals, int64(2)) + l, err := nr.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(2)) kv := must.Node(nr.LookupByIndex(0)) qt.Assert(t, nr.Kind(), qt.Equals, datamodel.Kind_List) - qt.Check(t, kv.Length(), qt.Equals, int64(2)) + l, err = kv.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(2)) qt.Check(t, must.String(must.Node(kv.LookupByIndex(0))), qt.Equals, "str") qt.Check(t, must.String(must.Node(kv.LookupByIndex(1))), qt.Equals, "boop") kv = must.Node(nr.LookupByIndex(1)) qt.Assert(t, nr.Kind(), qt.Equals, datamodel.Kind_List) - qt.Check(t, kv.Length(), qt.Equals, int64(2)) + l, err = kv.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(2)) qt.Check(t, must.String(must.Node(kv.LookupByIndex(0))), qt.Equals, "lp") lp := must.Node(kv.LookupByIndex(1)) qt.Check(t, lp.Kind(), qt.Equals, datamodel.Kind_List) kv = must.Node(lp.LookupByIndex(0)) - qt.Check(t, kv.Length(), qt.Equals, int64(2)) + l, err = kv.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(2)) qt.Check(t, must.String(must.Node(kv.LookupByIndex(0))), qt.Equals, "field") qt.Check(t, must.String(must.Node(kv.LookupByIndex(1))), qt.Equals, "valoo") }) diff --git a/node/tests/schemaStructReprStringjoin.go b/node/tests/schemaStructReprStringjoin.go index 7684ca7d..9a8c3dad 100644 --- a/node/tests/schemaStructReprStringjoin.go +++ b/node/tests/schemaStructReprStringjoin.go @@ -52,7 +52,9 @@ func SchemaTestStructReprStringjoin(t *testing.T, engine Engine) { }).(schema.TypedNode) t.Run("typed-read", func(t *testing.T) { qt.Assert(t, n.Kind(), qt.Equals, datamodel.Kind_Map) - qt.Check(t, n.Length(), qt.Equals, int64(1)) + l, err := n.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(1)) qt.Check(t, must.String(must.Node(n.LookupByString("field"))), qt.Equals, "valoo") }) t.Run("repr-read", func(t *testing.T) { @@ -80,7 +82,9 @@ func SchemaTestStructReprStringjoin(t *testing.T, engine Engine) { }).(schema.TypedNode) t.Run("typed-read", func(t *testing.T) { qt.Assert(t, n.Kind(), qt.Equals, datamodel.Kind_Map) - qt.Check(t, n.Length(), qt.Equals, int64(2)) + l, err := n.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(2)) qt.Check(t, must.String(must.Node(n.LookupByString("foo"))), qt.Equals, "v1") qt.Check(t, must.String(must.Node(n.LookupByString("bar"))), qt.Equals, "v2") }) @@ -109,7 +113,9 @@ func SchemaTestStructReprStringjoin(t *testing.T, engine Engine) { }).(schema.TypedNode) t.Run("typed-read", func(t *testing.T) { qt.Assert(t, n.Kind(), qt.Equals, datamodel.Kind_Map) - qt.Check(t, n.Length(), qt.Equals, int64(2)) + l, err := n.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(2)) qt.Check(t, must.String(must.Node(n.LookupByString("foo"))), qt.Equals, "") qt.Check(t, must.String(must.Node(n.LookupByString("bar"))), qt.Equals, "v2") }) @@ -142,11 +148,15 @@ func SchemaTestStructReprStringjoin(t *testing.T, engine Engine) { }).(schema.TypedNode) t.Run("typed-read", func(t *testing.T) { qt.Assert(t, n.Kind(), qt.Equals, datamodel.Kind_Map) - qt.Check(t, n.Length(), qt.Equals, int64(3)) + l, err := n.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(3)) qt.Check(t, must.String(must.Node(n.LookupByString("foo"))), qt.Equals, "v1") qt.Check(t, must.String(must.Node(n.LookupByString("bar"))), qt.Equals, "v4") n2 := must.Node(n.LookupByString("zap")) - qt.Check(t, n2.Length(), qt.Equals, int64(2)) + l, err = n2.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(2)) qt.Check(t, must.String(must.Node(n2.LookupByString("foo"))), qt.Equals, "v2") qt.Check(t, must.String(must.Node(n2.LookupByString("bar"))), qt.Equals, "v3") }) diff --git a/node/tests/schemaStructReprTuple.go b/node/tests/schemaStructReprTuple.go index e57cae7c..9e8de308 100644 --- a/node/tests/schemaStructReprTuple.go +++ b/node/tests/schemaStructReprTuple.go @@ -42,13 +42,17 @@ func SchemaTestStructReprTuple(t *testing.T, engine Engine) { }).(schema.TypedNode) t.Run("typed-read", func(t *testing.T) { qt.Assert(t, n.Kind(), qt.Equals, datamodel.Kind_Map) - qt.Check(t, n.Length(), qt.Equals, int64(1)) + l, err := n.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(1)) qt.Check(t, must.String(must.Node(n.LookupByString("field"))), qt.Equals, "valoo") }) t.Run("repr-read", func(t *testing.T) { nr := n.Representation() qt.Assert(t, nr.Kind(), qt.Equals, datamodel.Kind_List) - qt.Check(t, nr.Length(), qt.Equals, int64(1)) + l, err := n.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(1)) qt.Check(t, must.String(must.Node(nr.LookupByIndex(0))), qt.Equals, "valoo") }) }) @@ -73,7 +77,9 @@ func SchemaTestStructReprTuple(t *testing.T, engine Engine) { }).(schema.TypedNode) t.Run("typed-read", func(t *testing.T) { qt.Assert(t, n.Kind(), qt.Equals, datamodel.Kind_Map) - qt.Check(t, n.Length(), qt.Equals, int64(4)) + l, err := n.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(4)) qt.Check(t, must.String(must.Node(n.LookupByString("foo"))), qt.Equals, "0") qt.Check(t, must.String(must.Node(n.LookupByString("bar"))), qt.Equals, "1") qt.Check(t, must.String(must.Node(n.LookupByString("baz"))), qt.Equals, "2") @@ -82,7 +88,9 @@ func SchemaTestStructReprTuple(t *testing.T, engine Engine) { t.Run("repr-read", func(t *testing.T) { nr := n.Representation() qt.Assert(t, nr.Kind(), qt.Equals, datamodel.Kind_List) - qt.Check(t, nr.Length(), qt.Equals, int64(4)) + l, err := n.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(4)) qt.Check(t, must.String(must.Node(nr.LookupByIndex(0))), qt.Equals, "0") qt.Check(t, must.String(must.Node(nr.LookupByIndex(1))), qt.Equals, "1") qt.Check(t, must.String(must.Node(nr.LookupByIndex(2))), qt.Equals, "2") @@ -111,7 +119,9 @@ func SchemaTestStructReprTuple(t *testing.T, engine Engine) { }).(schema.TypedNode) t.Run("typed-read", func(t *testing.T) { qt.Assert(t, n.Kind(), qt.Equals, datamodel.Kind_Map) - qt.Check(t, n.Length(), qt.Equals, int64(4)) + l, err := n.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(4)) qt.Check(t, must.String(must.Node(n.LookupByString("foo"))), qt.Equals, "0") qt.Check(t, must.Node(n.LookupByString("bar")), qt.Equals, datamodel.Null) qt.Check(t, must.Node(n.LookupByString("baz")), qt.Equals, datamodel.Absent) @@ -120,7 +130,9 @@ func SchemaTestStructReprTuple(t *testing.T, engine Engine) { t.Run("repr-read", func(t *testing.T) { nr := n.Representation() qt.Assert(t, nr.Kind(), qt.Equals, datamodel.Kind_List) - qt.Check(t, nr.Length(), qt.Equals, int64(2)) + l, err := nr.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(2)) qt.Check(t, must.String(must.Node(nr.LookupByIndex(0))), qt.Equals, "0") qt.Check(t, must.Node(nr.LookupByIndex(1)), qt.Equals, datamodel.Null) }) diff --git a/node/tests/testcase.go b/node/tests/testcase.go index 0273784d..d4064b2f 100644 --- a/node/tests/testcase.go +++ b/node/tests/testcase.go @@ -208,7 +208,11 @@ func (tcase testcase) Test(t *testing.T, np, npr datamodel.NodePrototype) { func shallowCopyMap(np datamodel.NodePrototype, n datamodel.Node) (datamodel.Node, error) { nb := np.NewBuilder() - ma, err := nb.BeginMap(n.Length()) + l, err := n.Length() + if err != nil { + return nil, err + } + ma, err := nb.BeginMap(l) if err != nil { return nil, err } diff --git a/printer/printer.go b/printer/printer.go index 55cb6bc1..927e12a8 100644 --- a/printer/printer.go +++ b/printer/printer.go @@ -233,7 +233,11 @@ func (z *printBuf) doString(indentLevel int, printState uint8, n datamodel.Node) childState = printState_isCmplxValue } z.writeString("{") - if !oneline && n.Length() > 0 { + l, err := n.Length() + if err != nil { + panic(err) + } + if !oneline && l > 0 { z.writeString("\n") } for itr := n.MapIterator(); !itr.Done(); { @@ -294,7 +298,11 @@ func (z *printBuf) doString(indentLevel int, printState uint8, n datamodel.Node) childKeyState = printState_isCmplxKey } z.writeString("{") - if n.Length() > 0 { + l, err := n.Length() + if err != nil { + panic(err) + } + if l > 0 { z.writeString("\n") } else { z.writeString("}") @@ -318,7 +326,11 @@ func (z *printBuf) doString(indentLevel int, printState uint8, n datamodel.Node) z.writeString("}") case datamodel.Kind_List: z.writeString("{") - if n.Length() > 0 { + l, err := n.Length() + if err != nil { + panic(err) + } + if l > 0 { z.writeString("\n") } else { z.writeString("}") diff --git a/printer/printer_test.go b/printer/printer_test.go index 9a53b617..8c5a0906 100644 --- a/printer/printer_test.go +++ b/printer/printer_test.go @@ -273,8 +273,8 @@ func (n nilTypedNode) ListIterator() datamodel.ListIterator { return nil } -func (n nilTypedNode) Length() int64 { - return 0 +func (n nilTypedNode) Length() (int64, error) { + return 0, nil } func (n nilTypedNode) IsAbsent() bool { diff --git a/schema/gen/go/genList.go b/schema/gen/go/genList.go index 390a80c9..b9f60264 100644 --- a/schema/gen/go/genList.go +++ b/schema/gen/go/genList.go @@ -42,7 +42,9 @@ func (g listGenerator) EmitNativeAccessors(w io.Writer) { // and may additionally incur a memcpy if the maybe for the value type doesn't use pointers internally). doTemplate(` func (n *_{{ .Type | TypeSymbol }}) Lookup(idx int64) {{ .Type.ValueType | TypeSymbol }} { - if n.Length() <= idx { + if l, err := n.Length(); err != nil { + return nil + } else if l <= idx { return nil } v := &n.x[idx] @@ -56,7 +58,9 @@ func (g listGenerator) EmitNativeAccessors(w io.Writer) { {{- end}} } func (n *_{{ .Type | TypeSymbol }}) LookupMaybe(idx int64) Maybe{{ .Type.ValueType | TypeSymbol }} { - if n.Length() <= idx { + if l, err := n.Length(); err != nil { + return nil + } else if l <= idx { return nil } v := &n.x[idx] @@ -145,7 +149,9 @@ func (g listGenerator) EmitNodeTypeAssertions(w io.Writer) { func (g listGenerator) EmitNodeMethodLookupByIndex(w io.Writer) { doTemplate(` func (n {{ .Type | TypeSymbol }}) LookupByIndex(idx int64) (datamodel.Node, error) { - if n.Length() <= idx { + if l, err := n.Length(); err != nil { + return nil, err + } else if l <= idx { return nil, datamodel.ErrNotExists{Segment: datamodel.PathSegmentOfInt(idx)} } v := &n.x[idx] @@ -214,8 +220,8 @@ func (g listGenerator) EmitNodeMethodListIterator(w io.Writer) { func (g listGenerator) EmitNodeMethodLength(w io.Writer) { doTemplate(` - func (n {{ .Type | TypeSymbol }}) Length() int64 { - return int64(len(n.x)) + func (n {{ .Type | TypeSymbol }}) Length() (int64, error) { + return int64(len(n.x)), nil } `, w, g.AdjCfg, g) } diff --git a/schema/gen/go/genListReprList.go b/schema/gen/go/genListReprList.go index d17a7e7f..0829c3d6 100644 --- a/schema/gen/go/genListReprList.go +++ b/schema/gen/go/genListReprList.go @@ -120,8 +120,8 @@ func (g listReprListReprGenerator) EmitNodeMethodListIterator(w io.Writer) { func (g listReprListReprGenerator) EmitNodeMethodLength(w io.Writer) { doTemplate(` - func (rn *_{{ .Type | TypeSymbol }}__Repr) Length() int64 { - return int64(len(rn.x)) + func (rn *_{{ .Type | TypeSymbol }}__Repr) Length() (int64, error) { + return int64(len(rn.x)), nil } `, w, g.AdjCfg, g) } diff --git a/schema/gen/go/genMap.go b/schema/gen/go/genMap.go index 6dc496ec..10e1eb46 100644 --- a/schema/gen/go/genMap.go +++ b/schema/gen/go/genMap.go @@ -259,8 +259,8 @@ func (g mapGenerator) EmitNodeMethodMapIterator(w io.Writer) { func (g mapGenerator) EmitNodeMethodLength(w io.Writer) { doTemplate(` - func (n {{ .Type | TypeSymbol }}) Length() int64 { - return int64(len(n.t)) + func (n {{ .Type | TypeSymbol }}) Length() (int64, error) { + return int64(len(n.t)), nil } `, w, g.AdjCfg, g) } diff --git a/schema/gen/go/genMapReprMap.go b/schema/gen/go/genMapReprMap.go index 977fc1a6..06614499 100644 --- a/schema/gen/go/genMapReprMap.go +++ b/schema/gen/go/genMapReprMap.go @@ -115,8 +115,8 @@ func (g mapReprMapReprGenerator) EmitNodeMethodMapIterator(w io.Writer) { } func (g mapReprMapReprGenerator) EmitNodeMethodLength(w io.Writer) { doTemplate(` - func (rn *_{{ .Type | TypeSymbol }}__Repr) Length() int64 { - return int64(len(rn.t)) + func (rn *_{{ .Type | TypeSymbol }}__Repr) Length() (int64, error) { + return int64(len(rn.t)), nil } `, w, g.AdjCfg, g) } diff --git a/schema/gen/go/genStruct.go b/schema/gen/go/genStruct.go index 1351eb3c..65f5cd17 100644 --- a/schema/gen/go/genStruct.go +++ b/schema/gen/go/genStruct.go @@ -193,8 +193,8 @@ func (g structGenerator) EmitNodeMethodMapIterator(w io.Writer) { func (g structGenerator) EmitNodeMethodLength(w io.Writer) { doTemplate(` - func ({{ .Type | TypeSymbol }}) Length() int64 { - return {{ len .Type.Fields }} + func ({{ .Type | TypeSymbol }}) Length() (int64, error) { + return {{ len .Type.Fields }}, nil } `, w, g.AdjCfg, g) } diff --git a/schema/gen/go/genStructReprMap.go b/schema/gen/go/genStructReprMap.go index 368a3111..417506e8 100644 --- a/schema/gen/go/genStructReprMap.go +++ b/schema/gen/go/genStructReprMap.go @@ -244,7 +244,7 @@ func (g structReprMapReprGenerator) EmitNodeMethodLength(w io.Writer) { // This is fun: it has to count down for any unset optional fields. // TODO : support for implicits is still future work. doTemplate(` - func (rn *_{{ .Type | TypeSymbol }}__Repr) Length() int64 { + func (rn *_{{ .Type | TypeSymbol }}__Repr) Length() (int64, error) { l := {{ len .Type.Fields }} {{- range $field := .Type.Fields }} {{- if $field.IsOptional }} @@ -253,7 +253,7 @@ func (g structReprMapReprGenerator) EmitNodeMethodLength(w io.Writer) { } {{- end}} {{- end}} - return int64(l) + return int64(l), nil } `, w, g.AdjCfg, g) } diff --git a/schema/gen/go/genStructReprTuple.go b/schema/gen/go/genStructReprTuple.go index f4a255ff..0c699a78 100644 --- a/schema/gen/go/genStructReprTuple.go +++ b/schema/gen/go/genStructReprTuple.go @@ -223,7 +223,7 @@ func (g structReprTupleReprGenerator) EmitNodeMethodListIterator(w io.Writer) { func (g structReprTupleReprGenerator) EmitNodeMethodLength(w io.Writer) { // This is fun: it has to count down for any unset optional fields. doTemplate(` - func (rn *_{{ .Type | TypeSymbol }}__Repr) Length() int64 { + func (rn *_{{ .Type | TypeSymbol }}__Repr) Length() (int64, error) { l := {{ len .Type.Fields }} {{- range $field := .Type.Fields }} {{- if $field.IsOptional }} @@ -232,7 +232,7 @@ func (g structReprTupleReprGenerator) EmitNodeMethodLength(w io.Writer) { } {{- end}} {{- end}} - return int64(l) + return int64(l), nil } `, w, g.AdjCfg, g) } diff --git a/schema/gen/go/genUnion.go b/schema/gen/go/genUnion.go index a185bbfd..00dca8db 100644 --- a/schema/gen/go/genUnion.go +++ b/schema/gen/go/genUnion.go @@ -231,8 +231,8 @@ func (g unionGenerator) EmitNodeMethodMapIterator(w io.Writer) { func (g unionGenerator) EmitNodeMethodLength(w io.Writer) { doTemplate(` - func ({{ .Type | TypeSymbol }}) Length() int64 { - return 1 + func ({{ .Type | TypeSymbol }}) Length() (int64, error) { + return 1, nil } `, w, g.AdjCfg, g) } diff --git a/schema/gen/go/genUnionReprKeyed.go b/schema/gen/go/genUnionReprKeyed.go index c2af0253..c4c302a1 100644 --- a/schema/gen/go/genUnionReprKeyed.go +++ b/schema/gen/go/genUnionReprKeyed.go @@ -160,8 +160,8 @@ func (g unionReprKeyedReprGenerator) EmitNodeMethodMapIterator(w io.Writer) { func (g unionReprKeyedReprGenerator) EmitNodeMethodLength(w io.Writer) { doTemplate(` - func (_{{ .Type | TypeSymbol }}__Repr) Length() int64 { - return 1 + func (_{{ .Type | TypeSymbol }}__Repr) Length() (int64, error) { + return 1, nil } `, w, g.AdjCfg, g) } diff --git a/schema/gen/go/genUnionReprKinded.go b/schema/gen/go/genUnionReprKinded.go index 6fe32f1a..04b8a2cd 100644 --- a/schema/gen/go/genUnionReprKinded.go +++ b/schema/gen/go/genUnionReprKinded.go @@ -228,12 +228,12 @@ func (g unionKindedReprGenerator) EmitNodeMethodListIterator(w io.Writer) { func (g unionKindedReprGenerator) EmitNodeMethodLength(w io.Writer) { doTemplate(kindedUnionNodeMethodTemplateMunge( `Length`, - `Length() int64`, + `Length() (int64, error)`, `{{- if or (.Type.RepresentationStrategy.GetMember (Kind "map")) (.Type.RepresentationStrategy.GetMember (Kind "list")) }}`, `{{- if or (eq $member.RepresentationBehavior.String "map") (eq $member.RepresentationBehavior.String "list") }}`, `.Length()`, `datamodel.KindSet_Recursive`, - `-1`, + `-1, nil`, true, ), w, g.AdjCfg, g) } @@ -590,7 +590,11 @@ func (g unionKindedReprBuilderGenerator) EmitNodeAssemblerMethodAssignNode(w io. v2, _ := v.AsBytes() return na.AssignBytes(v2) case datamodel.Kind_Map: - na, err := na.BeginMap(v.Length()) + l, err := v.Length() + if err != nil { + return err + } + na, err := na.BeginMap(l) if err != nil { return err } @@ -609,7 +613,11 @@ func (g unionKindedReprBuilderGenerator) EmitNodeAssemblerMethodAssignNode(w io. } return na.Finish() case datamodel.Kind_List: - na, err := na.BeginList(v.Length()) + l, err := v.Length() + if err != nil { + return err + } + na, err := na.BeginList(l) if err != nil { return err } diff --git a/schema/gen/go/mixins/kindTraits.go b/schema/gen/go/mixins/kindTraits.go index 61bf3c89..72f8ec1c 100644 --- a/schema/gen/go/mixins/kindTraits.go +++ b/schema/gen/go/mixins/kindTraits.go @@ -102,8 +102,8 @@ func (g kindTraitsGenerator) emitNodeMethodLength(w io.Writer) { panic("gen internals error: you should've overriden this") } doTemplate(` - func ({{ .TypeSymbol }}) Length() int64 { - return -1 + func ({{ .TypeSymbol }}) Length() (int64, error) { + return -1, nil } `, w, g) } diff --git a/traversal/focus.go b/traversal/focus.go index 225a0994..2ccf010a 100644 --- a/traversal/focus.go +++ b/traversal/focus.go @@ -252,7 +252,11 @@ func (prog Progress) focusedTransform(n datamodel.Node, na datamodel.NodeAssembl // so we only get to this case if we were expecting to go deeper. switch n.Kind() { case datamodel.Kind_Map: - ma, err := na.BeginMap(n.Length()) + l, err := n.Length() + if err != nil { + return err + } + ma, err := na.BeginMap(l) if err != nil { return err } @@ -332,7 +336,11 @@ func (prog Progress) focusedTransform(n datamodel.Node, na datamodel.NodeAssembl } return ma.Finish() case datamodel.Kind_List: - la, err := na.BeginList(n.Length()) + l, err := n.Length() + if err != nil { + return err + } + la, err := na.BeginList(l) if err != nil { return err } @@ -374,7 +382,11 @@ func (prog Progress) focusedTransform(n datamodel.Node, na datamodel.NodeAssembl if ti >= 0 { return fmt.Errorf("transform: cannot navigate path segment %q at %q because it is beyond the list bounds", seg, prog.Path) } - prog.Path = prog.Path.AppendSegment(datamodel.PathSegmentOfInt(n.Length())) + nl, err := n.Length() + if err != nil { + return err + } + prog.Path = prog.Path.AppendSegment(datamodel.PathSegmentOfInt(nl)) if err := prog.focusedTransform(nil, la.AssembleValue(), p2, fn, createParents); err != nil { return err } diff --git a/traversal/focus_test.go b/traversal/focus_test.go index 7467a8e8..15b904c0 100644 --- a/traversal/focus_test.go +++ b/traversal/focus_test.go @@ -277,7 +277,9 @@ func TestFocusedTransform(t *testing.T) { // updated value should be there qt.Check(t, must.Node(n.LookupByIndex(2)), nodetests.NodeContentEquals, basicnode.NewString("new string!")) // everything else should be there - qt.Check(t, n.Length(), qt.Equals, int64(4)) + l, err := n.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(4)) qt.Check(t, must.Node(n.LookupByIndex(0)), nodetests.NodeContentEquals, basicnode.NewLink(leafAlphaLnk)) qt.Check(t, must.Node(n.LookupByIndex(1)), nodetests.NodeContentEquals, basicnode.NewLink(leafAlphaLnk)) qt.Check(t, must.Node(n.LookupByIndex(3)), nodetests.NodeContentEquals, basicnode.NewLink(leafAlphaLnk)) @@ -293,7 +295,9 @@ func TestFocusedTransform(t *testing.T) { // updated value should be there qt.Check(t, must.Node(n.LookupByIndex(4)), nodetests.NodeContentEquals, basicnode.NewString("new string!")) // everything else should be there - qt.Check(t, n.Length(), qt.Equals, int64(5)) + l, err := n.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(5)) }) t.Run("ListBounds", func(t *testing.T) { _, err := traversal.FocusedTransform(middleListNode, datamodel.ParsePath("4"), func(progress traversal.Progress, prev datamodel.Node) (datamodel.Node, error) { @@ -313,7 +317,9 @@ func TestFocusedTransform(t *testing.T) { }, false) qt.Check(t, err, qt.IsNil) qt.Check(t, n.Kind(), qt.Equals, datamodel.Kind_List) - qt.Check(t, n.Length(), qt.Equals, int64(0)) + l, err := n.Length() + qt.Check(t, err, qt.IsNil) + qt.Check(t, l, qt.Equals, int64(0)) }) } @@ -367,7 +373,11 @@ func TestFocusedTransformWithLinks(t *testing.T) { } func keys(n datamodel.Node) []string { - v := make([]string, 0, n.Length()) + l, err := n.Length() + if err != nil { + panic(err) + } + v := make([]string, 0, l) for itr := n.MapIterator(); !itr.Done(); { k, _, _ := itr.Next() v = append(v, must.String(k)) diff --git a/traversal/patch/eval.go b/traversal/patch/eval.go index 9534033c..c8e1b3f5 100644 --- a/traversal/patch/eval.go +++ b/traversal/patch/eval.go @@ -64,7 +64,11 @@ func EvalOne(n datamodel.Node, op Operation) (datamodel.Node, error) { } nb := parent.Prototype().NewBuilder() - la, err := nb.BeginList(parent.Length() + 1) + pl, err := parent.Length() + if err != nil { + return nil, err + } + la, err := nb.BeginList(pl + 1) if err != nil { return nil, err } diff --git a/traversal/selector/condition.go b/traversal/selector/condition.go index 3d59cf49..01da54c0 100644 --- a/traversal/selector/condition.go +++ b/traversal/selector/condition.go @@ -55,7 +55,11 @@ func (pc ParseContext) ParseCondition(n datamodel.Node) (Condition, error) { if n.Kind() != datamodel.Kind_Map { return Condition{}, fmt.Errorf("selector spec parse rejected: condition body must be a map") } - if n.Length() != 1 { + l, err := n.Length() + if err != nil { + return Condition{}, err + } + if l != 1 { return Condition{}, fmt.Errorf("selector spec parse rejected: condition is a keyed union and thus must be single-entry map") } kn, v, _ := n.MapIterator().Next() diff --git a/traversal/selector/exploreFields.go b/traversal/selector/exploreFields.go index b2796ff2..1be9d8bd 100644 --- a/traversal/selector/exploreFields.go +++ b/traversal/selector/exploreFields.go @@ -56,9 +56,13 @@ func (pc ParseContext) ParseExploreFields(n datamodel.Node) (Selector, error) { if fields.Kind() != datamodel.Kind_Map { return nil, fmt.Errorf("selector spec parse rejected: fields in ExploreFields selector must be a map") } + fl, err := fields.Length() + if err != nil { + return nil, err + } x := ExploreFields{ - make(map[string]Selector, fields.Length()), - make([]datamodel.PathSegment, 0, fields.Length()), + make(map[string]Selector, fl), + make([]datamodel.PathSegment, 0, fl), } for itr := fields.MapIterator(); !itr.Done(); { kn, v, err := itr.Next() diff --git a/traversal/selector/exploreRecursive.go b/traversal/selector/exploreRecursive.go index 28a9bedc..b35e41c2 100644 --- a/traversal/selector/exploreRecursive.go +++ b/traversal/selector/exploreRecursive.go @@ -235,7 +235,11 @@ func parseLimit(n datamodel.Node) (RecursionLimit, error) { if n.Kind() != datamodel.Kind_Map { return RecursionLimit{}, fmt.Errorf("selector spec parse rejected: limit in ExploreRecursive is a keyed union and thus must be a map") } - if n.Length() != 1 { + l, err := n.Length() + if err != nil { + return RecursionLimit{}, err + } + if l != 1 { return RecursionLimit{}, fmt.Errorf("selector spec parse rejected: limit in ExploreRecursive is a keyed union and thus must be a single-entry map") } kn, v, _ := n.MapIterator().Next() diff --git a/traversal/selector/exploreUnion.go b/traversal/selector/exploreUnion.go index 21594954..6ded0b1a 100644 --- a/traversal/selector/exploreUnion.go +++ b/traversal/selector/exploreUnion.go @@ -92,8 +92,12 @@ func (pc ParseContext) ParseExploreUnion(n datamodel.Node) (Selector, error) { if n.Kind() != datamodel.Kind_List { return nil, fmt.Errorf("selector spec parse rejected: explore union selector must be a list") } + l, err := n.Length() + if err != nil { + return nil, err + } x := ExploreUnion{ - make([]Selector, 0, n.Length()), + make([]Selector, 0, l), } for itr := n.ListIterator(); !itr.Done(); { _, v, err := itr.Next() diff --git a/traversal/selector/selector.go b/traversal/selector/selector.go index b58fcd86..dfb5567a 100644 --- a/traversal/selector/selector.go +++ b/traversal/selector/selector.go @@ -150,7 +150,11 @@ func (pc ParseContext) ParseSelector(n datamodel.Node) (Selector, error) { if n.Kind() != datamodel.Kind_Map { return nil, fmt.Errorf("selector spec parse rejected: selector is a keyed union and thus must be a map") } - if n.Length() != 1 { + l, err := n.Length() + if err != nil { + return nil, err + } + if l != 1 { return nil, fmt.Errorf("selector spec parse rejected: selector is a keyed union and thus must be single-entry map") } kn, v, _ := n.MapIterator().Next() diff --git a/traversal/walk.go b/traversal/walk.go index 1bd4e6c2..06ea3a86 100644 --- a/traversal/walk.go +++ b/traversal/walk.go @@ -514,7 +514,11 @@ func contains(interest []datamodel.PathSegment, candidate datamodel.PathSegment) func (prog Progress) walk_transform_iterateList(n datamodel.Node, s selector.Selector, fn TransformFn, attn []datamodel.PathSegment) (datamodel.Node, error) { bldr := n.Prototype().NewBuilder() - lstBldr, err := bldr.BeginList(n.Length()) + l, err := n.Length() + if err != nil { + return nil, err + } + lstBldr, err := bldr.BeginList(l) if err != nil { return nil, err } @@ -576,7 +580,11 @@ func (prog Progress) walk_transform_iterateList(n datamodel.Node, s selector.Sel func (prog Progress) walk_transform_iterateMap(n datamodel.Node, s selector.Selector, fn TransformFn, attn []datamodel.PathSegment) (datamodel.Node, error) { bldr := n.Prototype().NewBuilder() - mapBldr, err := bldr.BeginMap(n.Length()) + l, err := n.Length() + if err != nil { + return nil, err + } + mapBldr, err := bldr.BeginMap(l) if err != nil { return nil, err }