From 1b7059c029556db3ab5fbd3aa27febacced40826 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Mur=C3=A9?= Date: Tue, 1 Oct 2024 14:17:02 +0200 Subject: [PATCH] token/read: add the usual collections of readers --- tokens/internal/envelope/ipld.go | 65 +++++++++++--------------------- tokens/read.go | 54 +++++++++++++++++++++++++- 2 files changed, 73 insertions(+), 46 deletions(-) diff --git a/tokens/internal/envelope/ipld.go b/tokens/internal/envelope/ipld.go index a85fe5d..6803692 100644 --- a/tokens/internal/envelope/ipld.go +++ b/tokens/internal/envelope/ipld.go @@ -94,19 +94,7 @@ func DecodeReader[T Tokener](r io.Reader, decFn codec.Decoder) (T, error) { // An error is returned if the conversion fails, or if the resulting // Tokener is invalid. func FromDagCbor[T Tokener](b []byte) (T, error) { - undef := *new(T) - - node, err := ipld.Decode(b, dagcbor.Decode) - if err != nil { - return undef, err - } - - tkn, err := fromIPLD[T](node) - if err != nil { - return undef, err - } - - return tkn, nil + return Decode[T](b, dagcbor.Decode) } // FromDagCborReader is the same as FromDagCbor, but accept an io.Reader. @@ -132,20 +120,9 @@ func FromDagJsonReader[T Tokener](r io.Reader) (T, error) { // An error is returned if the conversion fails, or if the resulting // Tokener is invalid. func FromIPLD[T Tokener](node datamodel.Node) (T, error) { - undef := *new(T) - - tkn, err := fromIPLD[T](node) - if err != nil { - return undef, err - } - - return tkn, nil -} - -func fromIPLD[T Tokener](node datamodel.Node) (T, error) { zero := *new(T) - info, err := inspect(node) + info, err := Inspect(node) if err != nil { return zero, err } @@ -154,7 +131,7 @@ func fromIPLD[T Tokener](node datamodel.Node) (T, error) { return zero, errors.New("data doesn't match the expected type") } - tokenPayloadNode, err := info.SigPayloadNode.LookupByString(info.Tag) + tokenPayloadNode, err := info.sigPayloadNode.LookupByString(info.Tag) if err != nil { return zero, err } @@ -215,7 +192,7 @@ func fromIPLD[T Tokener](node datamodel.Node) (T, error) { return zero, errors.New("the VarsigHeader key type doesn't match the issuer's key type") } - data, err := ipld.Encode(info.SigPayloadNode, dagcbor.Encode) + data, err := ipld.Encode(info.sigPayloadNode, dagcbor.Encode) if err != nil { return zero, err } @@ -335,50 +312,50 @@ func FindTag(node datamodel.Node) (string, error) { return "", fmt.Errorf("no token tag found") } -type info struct { +type Info struct { Tag string Signature []byte - SigPayloadNode datamodel.Node + sigPayloadNode datamodel.Node // private, we don't want to expose that VarsigHeader []byte } -func inspect(node datamodel.Node) (info, error) { - var res info +func Inspect(node datamodel.Node) (Info, error) { + var res Info signatureNode, err := node.LookupByIndex(0) if err != nil { - return info{}, err + return Info{}, err } res.Signature, err = signatureNode.AsBytes() if err != nil { - return info{}, err + return Info{}, err } - res.SigPayloadNode, err = node.LookupByIndex(1) + res.sigPayloadNode, err = node.LookupByIndex(1) if err != nil { - return info{}, err + return Info{}, err } - it := res.SigPayloadNode.MapIterator() + it := res.sigPayloadNode.MapIterator() foundVarsigHeader := false foundTokenPayload := false i := 0 for !it.Done() { if i >= 2 { - return info{}, fmt.Errorf("expected two and only two fields in SigPayload") + return Info{}, fmt.Errorf("expected two and only two fields in SigPayload") } i++ k, v, err := it.Next() if err != nil { - return info{}, err + return Info{}, err } key, err := k.AsString() if err != nil { - return info{}, err + return Info{}, err } switch { @@ -386,24 +363,24 @@ func inspect(node datamodel.Node) (info, error) { foundVarsigHeader = true res.VarsigHeader, err = v.AsBytes() if err != nil { - return info{}, err + return Info{}, err } case strings.HasPrefix(key, UCANTagPrefix): foundTokenPayload = true res.Tag = key default: - return info{}, fmt.Errorf("unexpected key type %q", key) + return Info{}, fmt.Errorf("unexpected key type %q", key) } } if i != 2 { - return info{}, fmt.Errorf("expected two and only two fields in SigPayload: %d", i) + return Info{}, fmt.Errorf("expected two and only two fields in SigPayload: %d", i) } if !foundVarsigHeader { - return info{}, errors.New("failed to find VarsigHeader field") + return Info{}, errors.New("failed to find VarsigHeader field") } if !foundTokenPayload { - return info{}, errors.New("failed to find TokenPayload field") + return Info{}, errors.New("failed to find TokenPayload field") } return res, nil diff --git a/tokens/read.go b/tokens/read.go index 43f67ad..999fc50 100644 --- a/tokens/read.go +++ b/tokens/read.go @@ -2,20 +2,70 @@ package tokens import ( "fmt" + "io" "github.com/ipld/go-ipld-prime" + "github.com/ipld/go-ipld-prime/codec" "github.com/ipld/go-ipld-prime/codec/dagcbor" + "github.com/ipld/go-ipld-prime/codec/dagjson" + "github.com/ipld/go-ipld-prime/datamodel" "github.com/ucan-wg/go-ucan/tokens/delegation" "github.com/ucan-wg/go-ucan/tokens/internal/envelope" ) -func FromDagCbor(b []byte) (any, error) { - node, err := ipld.Decode(b, dagcbor.Decode) +// Decode unmarshals the input data using the format specified by the +// provided codec.Decoder into an arbitrary UCAN token. +// An error is returned if the conversion fails, or if the resulting +// Token is invalid. +// Supported and returned types are: +// - delegation.Token +func Decode(b []byte, decFn codec.Decoder) (any, error) { + node, err := ipld.Decode(b, decFn) if err != nil { return nil, err } + return fromIPLD(node) +} +// DecodeReader is the same as Decode, but accept an io.Reader. +func DecodeReader(r io.Reader, decFn codec.Decoder) (any, error) { + node, err := ipld.DecodeStreaming(r, decFn) + if err != nil { + return nil, err + } + return fromIPLD(node) +} + +// FromDagCbor unmarshals an arbitrary DagCbor encoded UCAN token. +// An error is returned if the conversion fails, or if the resulting +// Token is invalid. +// Supported and returned types are: +// - delegation.Token +func FromDagCbor(b []byte) (any, error) { + return Decode(b, dagcbor.Decode) +} + +// FromDagCborReader is the same as FromDagCbor, but accept an io.Reader. +func FromDagCborReader(r io.Reader) (any, error) { + return DecodeReader(r, dagcbor.Decode) +} + +// FromDagCbor unmarshals an arbitrary DagJson encoded UCAN token. +// An error is returned if the conversion fails, or if the resulting +// Token is invalid. +// Supported and returned types are: +// - delegation.Token +func FromDagJson(b []byte) (any, error) { + return Decode(b, dagjson.Decode) +} + +// FromDagJsonReader is the same as FromDagJson, but accept an io.Reader. +func FromDagJsonReader(r io.Reader) (any, error) { + return DecodeReader(r, dagjson.Decode) +} + +func fromIPLD(node datamodel.Node) (any, error) { tag, err := envelope.FindTag(node) if err != nil { return nil, err