package codec import ( "fmt" "github.com/polydawn/refmt/shared" "github.com/polydawn/refmt/tok" ld "gitlab.dms3.io/ld/go-ld-prime" ) // Marshal provides a very general node-to-tokens marshalling feature. // It can handle either cbor or json by being combined with a refmt TokenSink. // // It is valid for all the data model types except links, which are only // supported if the nodes are typed and provide additional information // to clarify how links should be encoded through their type info. // (The dag-cbor and dag-json formats can be used if links are of CID // implementation and need to be encoded in a schemafree way.) func Marshal(n ld.Node, sink shared.TokenSink) error { var tk tok.Token return marshal(n, &tk, sink) } func marshal(n ld.Node, tk *tok.Token, sink shared.TokenSink) error { switch n.Kind() { case ld.Kind_Invalid: return fmt.Errorf("cannot traverse a node that is absent") case ld.Kind_Null: tk.Type = tok.TNull _, err := sink.Step(tk) return err case ld.Kind_Map: // Emit start of map. tk.Type = tok.TMapOpen tk.Length = int(n.Length()) // TODO: overflow check if _, err := sink.Step(tk); err != nil { return err } // Emit map contents (and recurse). for itr := n.MapIterator(); !itr.Done(); { k, v, err := itr.Next() if err != nil { return err } tk.Type = tok.TString tk.Str, err = k.AsString() if err != nil { return err } if _, err := sink.Step(tk); err != nil { return err } if err := marshal(v, tk, sink); err != nil { return err } } // Emit map close. tk.Type = tok.TMapClose _, err := sink.Step(tk) return err case ld.Kind_List: // Emit start of list. tk.Type = tok.TArrOpen l := n.Length() tk.Length = int(l) // TODO: overflow check if _, err := sink.Step(tk); err != nil { return err } // Emit list contents (and recurse). for i := int64(0); i < l; i++ { v, err := n.LookupByIndex(i) if err != nil { return err } if err := marshal(v, tk, sink); err != nil { return err } } // Emit list close. tk.Type = tok.TArrClose _, err := sink.Step(tk) return err case ld.Kind_Bool: v, err := n.AsBool() if err != nil { return err } tk.Type = tok.TBool tk.Bool = v _, err = sink.Step(tk) return err case ld.Kind_Int: v, err := n.AsInt() if err != nil { return err } tk.Type = tok.TInt tk.Int = int64(v) _, err = sink.Step(tk) return err case ld.Kind_Float: v, err := n.AsFloat() if err != nil { return err } tk.Type = tok.TFloat64 tk.Float64 = v _, err = sink.Step(tk) return err case ld.Kind_String: v, err := n.AsString() if err != nil { return err } tk.Type = tok.TString tk.Str = v _, err = sink.Step(tk) return err case ld.Kind_Bytes: v, err := n.AsBytes() if err != nil { return err } tk.Type = tok.TBytes tk.Bytes = v _, err = sink.Step(tk) return err case ld.Kind_Link: return fmt.Errorf("link emission not supported by this codec without a schema! (maybe you want dag-cbor or dag-json)") default: panic("unreachable") } }