Files
ucan/pkg/meta/meta.go

214 lines
5.2 KiB
Go
Raw Normal View History

2024-09-17 19:12:31 +02:00
package meta
import (
"errors"
2024-09-24 11:40:28 -04:00
"fmt"
"strings"
2024-09-17 19:12:31 +02:00
"github.com/ipld/go-ipld-prime"
"github.com/ipld/go-ipld-prime/printer"
"github.com/ucan-wg/go-ucan/pkg/meta/internal/crypto"
"github.com/ucan-wg/go-ucan/pkg/policy/literal"
)
2024-11-12 15:31:21 +01:00
var ErrNotFound = errors.New("key not found in meta")
2024-09-24 11:40:28 -04:00
var ErrNotEncryptable = errors.New("value of this type cannot be encrypted")
2024-09-17 19:12:31 +02:00
// Meta is a container for meta key-value pairs in a UCAN token.
// This also serves as a way to construct the underlying IPLD data with minimum allocations
// and transformations, while hiding the IPLD complexity from the caller.
2024-09-17 19:12:31 +02:00
type Meta struct {
// This type must be compatible with the IPLD type represented by the IPLD
// schema { String : Any }.
2024-09-17 19:12:31 +02:00
Keys []string
Values map[string]ipld.Node
}
// NewMeta constructs a new Meta.
func NewMeta() *Meta {
return &Meta{Values: map[string]ipld.Node{}}
}
// GetBool retrieves a value as a bool.
// Returns ErrNotFound if the given key is missing.
// Returns datamodel.ErrWrongKind if the value has the wrong type.
2024-09-19 10:48:25 +02:00
func (m *Meta) GetBool(key string) (bool, error) {
2024-09-17 19:12:31 +02:00
v, ok := m.Values[key]
if !ok {
return false, ErrNotFound
}
return v.AsBool()
}
// GetString retrieves a value as a string.
// Returns ErrNotFound if the given key is missing.
// Returns datamodel.ErrWrongKind if the value has the wrong type.
2024-09-19 10:48:25 +02:00
func (m *Meta) GetString(key string) (string, error) {
2024-09-17 19:12:31 +02:00
v, ok := m.Values[key]
if !ok {
return "", ErrNotFound
}
return v.AsString()
}
// GetEncryptedString decorates GetString and decrypt its output with the given symmetric encryption key.
func (m *Meta) GetEncryptedString(key string, encryptionKey []byte) (string, error) {
v, err := m.GetBytes(key)
if err != nil {
return "", err
}
decrypted, err := crypto.DecryptStringWithAESKey(v, encryptionKey)
if err != nil {
return "", err
}
return string(decrypted), nil
}
2024-09-17 19:12:31 +02:00
// GetInt64 retrieves a value as an int64.
// Returns ErrNotFound if the given key is missing.
// Returns datamodel.ErrWrongKind if the value has the wrong type.
2024-09-19 10:48:25 +02:00
func (m *Meta) GetInt64(key string) (int64, error) {
2024-09-17 19:12:31 +02:00
v, ok := m.Values[key]
if !ok {
return 0, ErrNotFound
}
return v.AsInt()
}
// GetFloat64 retrieves a value as a float64.
// Returns ErrNotFound if the given key is missing.
// Returns datamodel.ErrWrongKind if the value has the wrong type.
2024-09-19 10:48:25 +02:00
func (m *Meta) GetFloat64(key string) (float64, error) {
2024-09-17 19:12:31 +02:00
v, ok := m.Values[key]
if !ok {
return 0, ErrNotFound
}
return v.AsFloat()
}
// GetBytes retrieves a value as a []byte.
// Returns ErrNotFound if the given key is missing.
// Returns datamodel.ErrWrongKind if the value has the wrong type.
2024-09-19 10:48:25 +02:00
func (m *Meta) GetBytes(key string) ([]byte, error) {
2024-09-17 19:12:31 +02:00
v, ok := m.Values[key]
if !ok {
return nil, ErrNotFound
}
return v.AsBytes()
}
// GetEncryptedBytes decorates GetBytes and decrypt its output with the given symmetric encryption key.
func (m *Meta) GetEncryptedBytes(key string, encryptionKey []byte) ([]byte, error) {
v, err := m.GetBytes(key)
if err != nil {
return nil, err
}
decrypted, err := crypto.DecryptStringWithAESKey(v, encryptionKey)
if err != nil {
return nil, err
}
return decrypted, nil
}
2024-09-17 19:12:31 +02:00
// GetNode retrieves a value as a raw IPLD node.
// Returns ErrNotFound if the given key is missing.
// Returns datamodel.ErrWrongKind if the value has the wrong type.
2024-09-19 10:48:25 +02:00
func (m *Meta) GetNode(key string) (ipld.Node, error) {
2024-09-17 19:12:31 +02:00
v, ok := m.Values[key]
if !ok {
return nil, ErrNotFound
}
return v, nil
}
// Add adds a key/value pair in the meta set.
// Accepted types for val are any CBOR compatible type, or directly IPLD values.
2024-09-19 10:48:25 +02:00
func (m *Meta) Add(key string, val any) error {
2024-11-06 18:06:46 +01:00
if _, ok := m.Values[key]; ok {
return fmt.Errorf("duplicate key %q", key)
}
node, err := literal.Any(val)
if err != nil {
return err
2024-09-17 19:12:31 +02:00
}
2024-09-17 19:12:31 +02:00
m.Keys = append(m.Keys, key)
m.Values[key] = node
2024-09-17 19:12:31 +02:00
return nil
}
2024-09-24 11:40:28 -04:00
// AddEncrypted adds a key/value pair in the meta set.
// The value is encrypted with the given encryptionKey.
// Accepted types for the value are: string, []byte.
func (m *Meta) AddEncrypted(key string, val any, encryptionKey []byte) error {
var encrypted []byte
var err error
switch val := val.(type) {
case string:
encrypted, err = crypto.EncryptWithAESKey([]byte(val), encryptionKey)
if err != nil {
return err
}
case []byte:
encrypted, err = crypto.EncryptWithAESKey(val, encryptionKey)
if err != nil {
return err
}
default:
return ErrNotEncryptable
}
return m.Add(key, encrypted)
}
2024-10-02 11:56:32 +02:00
// Equals tells if two Meta hold the same key/values.
func (m *Meta) Equals(other *Meta) bool {
if len(m.Keys) != len(other.Keys) {
return false
}
if len(m.Values) != len(other.Values) {
return false
}
for _, key := range m.Keys {
if !ipld.DeepEqual(m.Values[key], other.Values[key]) {
return false
}
}
return true
}
func (m *Meta) String() string {
buf := strings.Builder{}
buf.WriteString("{")
for key, node := range m.Values {
buf.WriteString("\n\t")
buf.WriteString(key)
buf.WriteString(": ")
buf.WriteString(strings.ReplaceAll(printer.Sprint(node), "\n", "\n\t"))
buf.WriteString(",")
}
if len(m.Values) > 0 {
buf.WriteString("\n")
}
buf.WriteString("}")
return buf.String()
}
// ReadOnly returns a read-only version of Meta.
func (m *Meta) ReadOnly() ReadOnly {
return ReadOnly{m: m}
}