2024-11-07 12:58:53 -05:00
|
|
|
// Package args provides the type that represents the Arguments passed to
|
|
|
|
|
// a command within an invocation.Token as well as a convenient Add method
|
|
|
|
|
// to incrementally build the underlying map.
|
|
|
|
|
package args
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"fmt"
|
2024-11-20 18:27:01 +01:00
|
|
|
"iter"
|
2024-11-12 12:14:58 +01:00
|
|
|
"sort"
|
2024-11-20 12:34:24 +01:00
|
|
|
"strings"
|
2024-11-07 12:58:53 -05:00
|
|
|
|
|
|
|
|
"github.com/ipld/go-ipld-prime"
|
2024-11-12 12:14:58 +01:00
|
|
|
"github.com/ipld/go-ipld-prime/datamodel"
|
|
|
|
|
"github.com/ipld/go-ipld-prime/fluent/qp"
|
|
|
|
|
"github.com/ipld/go-ipld-prime/node/basicnode"
|
2024-11-20 12:34:24 +01:00
|
|
|
"github.com/ipld/go-ipld-prime/printer"
|
2024-11-07 12:58:53 -05:00
|
|
|
|
2024-11-12 12:14:58 +01:00
|
|
|
"github.com/ucan-wg/go-ucan/pkg/policy/literal"
|
2024-11-07 12:58:53 -05:00
|
|
|
)
|
|
|
|
|
|
2024-11-12 13:05:48 +01:00
|
|
|
// Args are the Command's arguments when an invocation Token is processed by the executor.
|
|
|
|
|
// 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-11-07 12:58:53 -05:00
|
|
|
type Args struct {
|
2024-11-12 13:05:48 +01:00
|
|
|
// This type must be compatible with the IPLD type represented by the IPLD
|
|
|
|
|
// schema { String : Any }.
|
|
|
|
|
|
2024-11-07 12:58:53 -05:00
|
|
|
Keys []string
|
|
|
|
|
Values map[string]ipld.Node
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// New returns a pointer to an initialized Args value.
|
|
|
|
|
func New() *Args {
|
|
|
|
|
return &Args{
|
|
|
|
|
Values: map[string]ipld.Node{},
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Add inserts a key/value pair in the Args set.
|
|
|
|
|
//
|
2024-11-12 13:05:48 +01:00
|
|
|
// Accepted types for val are any CBOR compatible type, or directly IPLD values.
|
2024-11-12 12:14:58 +01:00
|
|
|
func (a *Args) Add(key string, val any) error {
|
|
|
|
|
if _, ok := a.Values[key]; ok {
|
2024-11-07 12:58:53 -05:00
|
|
|
return fmt.Errorf("duplicate key %q", key)
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-12 12:14:58 +01:00
|
|
|
node, err := literal.Any(val)
|
2024-11-07 12:58:53 -05:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-12 12:14:58 +01:00
|
|
|
a.Values[key] = node
|
|
|
|
|
a.Keys = append(a.Keys, key)
|
2024-11-07 12:58:53 -05:00
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-20 18:27:01 +01:00
|
|
|
type Iterator interface {
|
|
|
|
|
Iter() iter.Seq2[string, ipld.Node]
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-07 12:58:53 -05:00
|
|
|
// Include merges the provided arguments into the existing arguments.
|
|
|
|
|
//
|
|
|
|
|
// If duplicate keys are encountered, the new value is silently dropped
|
|
|
|
|
// without causing an error.
|
2024-11-20 18:27:01 +01:00
|
|
|
func (a *Args) Include(other Iterator) {
|
|
|
|
|
for key, value := range other.Iter() {
|
2024-11-12 12:14:58 +01:00
|
|
|
if _, ok := a.Values[key]; ok {
|
2024-11-07 12:58:53 -05:00
|
|
|
// don't overwrite
|
|
|
|
|
continue
|
|
|
|
|
}
|
2024-11-20 18:27:01 +01:00
|
|
|
a.Values[key] = value
|
2024-11-12 12:14:58 +01:00
|
|
|
a.Keys = append(a.Keys, key)
|
2024-11-07 12:58:53 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-20 18:27:01 +01:00
|
|
|
// Iter iterates over the args key/values
|
|
|
|
|
func (a *Args) Iter() iter.Seq2[string, ipld.Node] {
|
|
|
|
|
return func(yield func(string, ipld.Node) bool) {
|
|
|
|
|
for _, key := range a.Keys {
|
|
|
|
|
if !yield(key, a.Values[key]) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-07 12:58:53 -05:00
|
|
|
// ToIPLD wraps an instance of an Args with an ipld.Node.
|
2024-11-12 12:14:58 +01:00
|
|
|
func (a *Args) ToIPLD() (ipld.Node, error) {
|
|
|
|
|
sort.Strings(a.Keys)
|
2024-11-20 12:34:24 +01:00
|
|
|
|
2024-11-12 12:14:58 +01:00
|
|
|
return qp.BuildMap(basicnode.Prototype.Any, int64(len(a.Keys)), func(ma datamodel.MapAssembler) {
|
|
|
|
|
for _, key := range a.Keys {
|
|
|
|
|
qp.MapEntry(ma, key, qp.Node(a.Values[key]))
|
|
|
|
|
}
|
|
|
|
|
})
|
2024-11-07 12:58:53 -05:00
|
|
|
}
|
|
|
|
|
|
2024-11-12 12:14:58 +01:00
|
|
|
// Equals tells if two Args hold the same values.
|
|
|
|
|
func (a *Args) Equals(other *Args) bool {
|
|
|
|
|
if len(a.Keys) != len(other.Keys) {
|
|
|
|
|
return false
|
2024-11-07 12:58:53 -05:00
|
|
|
}
|
2024-11-12 12:14:58 +01:00
|
|
|
if len(a.Values) != len(other.Values) {
|
|
|
|
|
return false
|
2024-11-07 12:58:53 -05:00
|
|
|
}
|
2024-11-12 12:14:58 +01:00
|
|
|
for _, key := range a.Keys {
|
|
|
|
|
if !ipld.DeepEqual(a.Values[key], other.Values[key]) {
|
|
|
|
|
return false
|
2024-11-07 12:58:53 -05:00
|
|
|
}
|
|
|
|
|
}
|
2024-11-12 12:14:58 +01:00
|
|
|
return true
|
2024-11-07 12:58:53 -05:00
|
|
|
}
|
2024-11-20 12:34:24 +01:00
|
|
|
|
|
|
|
|
func (a *Args) String() string {
|
|
|
|
|
sort.Strings(a.Keys)
|
|
|
|
|
|
|
|
|
|
buf := strings.Builder{}
|
|
|
|
|
buf.WriteString("{")
|
|
|
|
|
|
|
|
|
|
for _, key := range a.Keys {
|
|
|
|
|
buf.WriteString("\n\t")
|
|
|
|
|
buf.WriteString(key)
|
|
|
|
|
buf.WriteString(": ")
|
|
|
|
|
buf.WriteString(strings.ReplaceAll(printer.Sprint(a.Values[key]), "\n", "\n\t"))
|
|
|
|
|
buf.WriteString(",")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(a.Keys) > 0 {
|
|
|
|
|
buf.WriteString("\n")
|
|
|
|
|
}
|
|
|
|
|
buf.WriteString("}")
|
|
|
|
|
|
|
|
|
|
return buf.String()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ReadOnly returns a read-only version of Args.
|
|
|
|
|
func (a *Args) ReadOnly() ReadOnly {
|
|
|
|
|
return ReadOnly{args: a}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Clone makes a deep copy.
|
|
|
|
|
func (a *Args) Clone() *Args {
|
|
|
|
|
res := &Args{
|
|
|
|
|
Keys: make([]string, len(a.Keys)),
|
|
|
|
|
Values: make(map[string]ipld.Node, len(a.Values)),
|
|
|
|
|
}
|
|
|
|
|
copy(res.Keys, a.Keys)
|
|
|
|
|
for k, v := range a.Values {
|
|
|
|
|
res.Values[k] = v
|
|
|
|
|
}
|
|
|
|
|
return res
|
|
|
|
|
}
|