github.com/filecoin-project/bacalhau@v0.3.23-0.20230228154132-45c989550ace/pkg/model/v1beta1/ipld.go (about) 1 package v1beta1 2 3 import ( 4 "bytes" 5 "embed" 6 "reflect" 7 8 "github.com/ipld/go-ipld-prime" 9 "github.com/ipld/go-ipld-prime/codec" 10 "github.com/ipld/go-ipld-prime/codec/json" 11 "github.com/ipld/go-ipld-prime/datamodel" 12 "github.com/ipld/go-ipld-prime/schema" 13 ) 14 15 //go:embed schemas 16 var schemas embed.FS 17 18 const ( 19 ucanTaskSchemaPath string = "schemas/invocation.ipldsch" 20 bacalhauTaskSchemaPath string = "schemas/bacalhau.ipldsch" 21 ) 22 23 func load(path string) *Schema { 24 file, err := schemas.Open(path) 25 if err != nil { 26 panic(err) 27 } 28 29 schema, err := ipld.LoadSchema(path, file) 30 if err != nil { 31 panic(err) 32 } 33 return (*Schema)(schema) 34 } 35 36 type Schema schema.TypeSystem 37 38 var ( 39 // The UCAN Task schema is the standardized Invocation IPLD schema, defined 40 // by https://github.com/ucan-wg/invocation. 41 UCANTaskSchema *Schema = load(ucanTaskSchemaPath) 42 43 // The Bacalhau schema includes the Bacalhau specific extensions to the UCAN 44 // Task IPLD spec, i.e. input structures for specific job types. 45 BacalhauTaskSchema *Schema = load(bacalhauTaskSchemaPath) 46 ) 47 48 // GetSchemaTypeName returns the name of the corresponding IPLD type in the 49 // schema for the passed Go object. If the type cannot be in the schema, it 50 // returns an empty string. It may return a non-empty string even if the type is 51 // not in the schema. 52 func (s *Schema) GetSchemaTypeName(obj interface{}) string { 53 // Convention: all go types share the same name as their schema types 54 return reflect.TypeOf(obj).Elem().Name() 55 } 56 57 // GetSchemaType returns the IPLD type from the schema for the passed Go object. 58 // If the type is not in the schema, it returns nil. 59 func (s *Schema) GetSchemaType(obj interface{}) schema.Type { 60 name := s.GetSchemaTypeName(obj) 61 return (*schema.TypeSystem)(s).TypeByName(name) 62 } 63 64 // UnmarshalIPLD parses the given bytes as a Go object using the passed decoder. 65 // Returns an error if the object cannot be parsed. 66 func UnmarshalIPLD[T any](b []byte, decoder codec.Decoder, schema *Schema) (*T, error) { 67 t := new(T) 68 _, err := ipld.Unmarshal(b, decoder, t, schema.GetSchemaType(t)) 69 return t, err 70 } 71 72 // Reinterpret re-parses the datamodel.Node as an object of the defined type. 73 func Reinterpret[T any](node datamodel.Node, schema *Schema) (*T, error) { 74 // This is obviously slightly hacky and slow. but it is the most fool-proof 75 // way of doing this at time of writing, because go-ipld-prime cannot handle 76 // an algorithm using bindnode.Prototype and builder.AssignNode 77 schemaType := schema.GetSchemaType((*T)(nil)) 78 79 var buf bytes.Buffer 80 var val = new(T) 81 err := json.Encode(node, &buf) 82 if err != nil { 83 return val, err 84 } 85 86 _, err = ipld.Unmarshal(buf.Bytes(), json.Decode, val, schemaType) 87 return val, err 88 } 89 90 // IPLD Maps are parsed by the ipld library into structures of this type rather 91 // than just plain Go maps. 92 type IPLDMap[K comparable, V any] struct { 93 Keys []K 94 Values map[K]V 95 }