From ec4f6290b0a05c618087cfd9e9de96f1e8ca5fd6 Mon Sep 17 00:00:00 2001 From: Eric Myhre Date: Tue, 15 Jun 2021 18:12:50 -0700 Subject: [PATCH] codecs: error upon encountering an 'absent' value during encodes. --- CHANGELOG.md | 4 +++- codec/dagcbor/marshal.go | 5 ++++- codec/dagjson/marshal.go | 6 +++++- codec/marshal.go | 5 ++++- unit.go | 2 +- 5 files changed, 17 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7af73158..6e4ea9eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,7 +37,9 @@ Unreleased on master Changes here are on the master branch, but not in any tagged release yet. When a release tag is made, this block of bullet points will just slide down to the [Released Changes](#released-changes) section. -- _nothing yet :)_ +- Changed: encoding functions for codecs in this repo (dag-cbor, dag-json, cbor, json) will now **error** if they encounter an `ipld.Absent` node (or more specifically, if `IsAbsent() == true`). [[]] + - Previously, these codecs would quietly emit a serial "null" token in this scenario -- which was effectively implicitly coercing a value into null. Implicit coercion creates bugs and is bad, so we will not be doing it anymore. + - This situation is only reachable when working with schema-typed data (since otherwise `Absent` values aren't used). In that situation, handing that typed value to an encode function without having called `.Representation()` first is also typically a logical error, so, returning an explicit error now is also probably the right thing to do. diff --git a/codec/dagcbor/marshal.go b/codec/dagcbor/marshal.go index 5af638c8..e864c5c4 100644 --- a/codec/dagcbor/marshal.go +++ b/codec/dagcbor/marshal.go @@ -22,8 +22,11 @@ func Marshal(n ipld.Node, sink shared.TokenSink, allowLinks bool) error { func marshal(n ipld.Node, tk *tok.Token, sink shared.TokenSink, allowLinks bool) error { switch n.Kind() { case ipld.Kind_Invalid: - return fmt.Errorf("cannot traverse a node that is absent") + return fmt.Errorf("invalid node encountered") case ipld.Kind_Null: + if n.IsAbsent() { + return fmt.Errorf("cannot encode a node that is absent") + } tk.Type = tok.TNull _, err := sink.Step(tk) return err diff --git a/codec/dagjson/marshal.go b/codec/dagjson/marshal.go index 3e292fdc..ac178bef 100644 --- a/codec/dagjson/marshal.go +++ b/codec/dagjson/marshal.go @@ -19,7 +19,11 @@ func Marshal(n ipld.Node, sink shared.TokenSink, allowLinks bool) error { var tk tok.Token switch n.Kind() { case ipld.Kind_Invalid: - return fmt.Errorf("cannot traverse a node that is absent") + return fmt.Errorf("invalid node encountered") + case ipld.Kind_Null: + if n.IsAbsent() { + return fmt.Errorf("cannot encode a node that is absent") + } case ipld.Kind_Null: tk.Type = tok.TNull _, err := sink.Step(&tk) diff --git a/codec/marshal.go b/codec/marshal.go index 2b2eb5c6..c81ff964 100644 --- a/codec/marshal.go +++ b/codec/marshal.go @@ -25,8 +25,11 @@ func Marshal(n ipld.Node, sink shared.TokenSink) error { func marshal(n ipld.Node, tk *tok.Token, sink shared.TokenSink) error { switch n.Kind() { case ipld.Kind_Invalid: - return fmt.Errorf("cannot traverse a node that is absent") + return fmt.Errorf("invalid node encountered") case ipld.Kind_Null: + if n.IsAbsent() { + return fmt.Errorf("cannot encode a node that is absent") + } tk.Type = tok.TNull _, err := sink.Step(tk) return err diff --git a/unit.go b/unit.go index 8cc4647f..cb135bb9 100644 --- a/unit.go +++ b/unit.go @@ -67,7 +67,7 @@ var Absent Node = absentNode{} type absentNode struct{} func (absentNode) Kind() Kind { - return Kind_Null + return Kind_Null // It's questionable if this is a good idea. Perhaps it should be Kind_Invalid. See https://github.com/ipld/go-ipld-prime/issues/191 . } func (absentNode) LookupByString(key string) (Node, error) { return nil, ErrWrongKind{TypeName: "absent", MethodName: "LookupByString", AppropriateKind: KindSet_JustMap, ActualKind: Kind_Null}