token/read: add the usual collections of readers

This commit is contained in:
Michael Muré
2024-10-01 14:17:02 +02:00
parent f3a5209cec
commit 1b7059c029
2 changed files with 73 additions and 46 deletions

View File

@@ -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 // An error is returned if the conversion fails, or if the resulting
// Tokener is invalid. // Tokener is invalid.
func FromDagCbor[T Tokener](b []byte) (T, error) { func FromDagCbor[T Tokener](b []byte) (T, error) {
undef := *new(T) return Decode[T](b, dagcbor.Decode)
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
} }
// FromDagCborReader is the same as FromDagCbor, but accept an io.Reader. // 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 // An error is returned if the conversion fails, or if the resulting
// Tokener is invalid. // Tokener is invalid.
func FromIPLD[T Tokener](node datamodel.Node) (T, error) { 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) zero := *new(T)
info, err := inspect(node) info, err := Inspect(node)
if err != nil { if err != nil {
return zero, err 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") 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 { if err != nil {
return zero, err 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") 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 { if err != nil {
return zero, err return zero, err
} }
@@ -335,50 +312,50 @@ func FindTag(node datamodel.Node) (string, error) {
return "", fmt.Errorf("no token tag found") return "", fmt.Errorf("no token tag found")
} }
type info struct { type Info struct {
Tag string Tag string
Signature []byte Signature []byte
SigPayloadNode datamodel.Node sigPayloadNode datamodel.Node // private, we don't want to expose that
VarsigHeader []byte VarsigHeader []byte
} }
func inspect(node datamodel.Node) (info, error) { func Inspect(node datamodel.Node) (Info, error) {
var res info var res Info
signatureNode, err := node.LookupByIndex(0) signatureNode, err := node.LookupByIndex(0)
if err != nil { if err != nil {
return info{}, err return Info{}, err
} }
res.Signature, err = signatureNode.AsBytes() res.Signature, err = signatureNode.AsBytes()
if err != nil { if err != nil {
return info{}, err return Info{}, err
} }
res.SigPayloadNode, err = node.LookupByIndex(1) res.sigPayloadNode, err = node.LookupByIndex(1)
if err != nil { if err != nil {
return info{}, err return Info{}, err
} }
it := res.SigPayloadNode.MapIterator() it := res.sigPayloadNode.MapIterator()
foundVarsigHeader := false foundVarsigHeader := false
foundTokenPayload := false foundTokenPayload := false
i := 0 i := 0
for !it.Done() { for !it.Done() {
if i >= 2 { 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++ i++
k, v, err := it.Next() k, v, err := it.Next()
if err != nil { if err != nil {
return info{}, err return Info{}, err
} }
key, err := k.AsString() key, err := k.AsString()
if err != nil { if err != nil {
return info{}, err return Info{}, err
} }
switch { switch {
@@ -386,24 +363,24 @@ func inspect(node datamodel.Node) (info, error) {
foundVarsigHeader = true foundVarsigHeader = true
res.VarsigHeader, err = v.AsBytes() res.VarsigHeader, err = v.AsBytes()
if err != nil { if err != nil {
return info{}, err return Info{}, err
} }
case strings.HasPrefix(key, UCANTagPrefix): case strings.HasPrefix(key, UCANTagPrefix):
foundTokenPayload = true foundTokenPayload = true
res.Tag = key res.Tag = key
default: default:
return info{}, fmt.Errorf("unexpected key type %q", key) return Info{}, fmt.Errorf("unexpected key type %q", key)
} }
} }
if i != 2 { 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 { if !foundVarsigHeader {
return info{}, errors.New("failed to find VarsigHeader field") return Info{}, errors.New("failed to find VarsigHeader field")
} }
if !foundTokenPayload { if !foundTokenPayload {
return info{}, errors.New("failed to find TokenPayload field") return Info{}, errors.New("failed to find TokenPayload field")
} }
return res, nil return res, nil

View File

@@ -2,20 +2,70 @@ package tokens
import ( import (
"fmt" "fmt"
"io"
"github.com/ipld/go-ipld-prime" "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/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/delegation"
"github.com/ucan-wg/go-ucan/tokens/internal/envelope" "github.com/ucan-wg/go-ucan/tokens/internal/envelope"
) )
func FromDagCbor(b []byte) (any, error) { // Decode unmarshals the input data using the format specified by the
node, err := ipld.Decode(b, dagcbor.Decode) // 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 { if err != nil {
return nil, err 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) tag, err := envelope.FindTag(node)
if err != nil { if err != nil {
return nil, err return nil, err