diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..2906418 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,27 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 4 +tab_width = 4 +indent_style = space +insert_final_newline = false +max_line_length = 120 +trim_trailing_whitespace = true + +[*.go] +indent_style = tab +ij_smart_tabs = true +ij_go_GROUP_CURRENT_PROJECT_IMPORTS = true +ij_go_group_stdlib_imports = true +ij_go_import_sorting = goimports +ij_go_local_group_mode = project +ij_go_move_all_imports_in_one_declaration = true +ij_go_move_all_stdlib_imports_in_one_group = true +ij_go_remove_redundant_import_aliases = true + +[*.{yml,yaml}] +indent_size = 2 +tab_width = 2 +insert_final_newline = true \ No newline at end of file diff --git a/entrypoint.go b/entrypoint.go index d1fdf4a..29edd93 100644 --- a/entrypoint.go +++ b/entrypoint.go @@ -7,8 +7,8 @@ import ( _ "github.com/tetratelabs/tinymem" rpcv1 "code.icb4dc0.de/buildr/api/generated/rpc/v1" + "code.icb4dc0.de/buildr/common/protocol" "code.icb4dc0.de/buildr/wasi-module-sdk-go/mem" - "code.icb4dc0.de/buildr/wasi-module-sdk-go/protocol" ) var defaultRegistry = NewTypeRegistry() @@ -22,9 +22,23 @@ func Inventory() uint64 { var inventory rpcv1.PluginInventory for _, t := range defaultRegistry.List() { - inventory.Modules = append(inventory.Modules, &rpcv1.ModuleReference{ - ModuleCategory: t.Category, - ModuleType: t.Type, + m := defaultRegistry.Get(t.Category, t.Type) + spec, err := protocol.Marshal(m) + if err != nil { + panic(err) + } + + data, err := spec.MarshalVT() + if err != nil { + panic(err) + } + + inventory.Specs = append(inventory.Specs, &rpcv1.PluginInventory_InventorySpec{ + ModuleRef: &rpcv1.ModuleReference{ + ModuleCategory: t.Category, + ModuleType: t.Type, + }, + EmptySpec: data, }) } diff --git a/go.mod b/go.mod index 54a01cf..2104390 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,8 @@ module code.icb4dc0.de/buildr/wasi-module-sdk-go go 1.21 require ( - code.icb4dc0.de/buildr/api v0.0.0-20230816163132-0ed4d13b0082 + code.icb4dc0.de/buildr/api v0.0.0-20230823165833-52d0bd9e9741 + code.icb4dc0.de/buildr/common v0.0.0-20230823164419-5a757074eaa3 github.com/tetratelabs/tinymem v0.1.0 ) @@ -12,7 +13,7 @@ require ( golang.org/x/net v0.14.0 // indirect golang.org/x/sys v0.11.0 // indirect golang.org/x/text v0.12.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20230815205213-6bfd019c3878 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect google.golang.org/grpc v1.57.0 // indirect google.golang.org/protobuf v1.31.0 // indirect ) diff --git a/go.sum b/go.sum index 5041600..62e47e2 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,11 @@ -code.icb4dc0.de/buildr/api v0.0.0-20230816163132-0ed4d13b0082 h1:oO//6VUwvXstu6rNWis3CDgapUrcPvrdRZvcsY/7PBw= -code.icb4dc0.de/buildr/api v0.0.0-20230816163132-0ed4d13b0082/go.mod h1:Bxt+fw/9hH7/WESz3asYBIWPr81UlUMEleXJGTqX6ys= +code.icb4dc0.de/buildr/api v0.0.0-20230817151157-dbc0adad8f8b h1:pZDHPJCMRw3UM1Jd0y7XnPZaDOzZ5du17EvW09mgb4g= +code.icb4dc0.de/buildr/api v0.0.0-20230817151157-dbc0adad8f8b/go.mod h1:Bxt+fw/9hH7/WESz3asYBIWPr81UlUMEleXJGTqX6ys= +code.icb4dc0.de/buildr/api v0.0.0-20230823165833-52d0bd9e9741 h1:uG2ECI+OT5oA5x2+G7upBhwKsYBS49xBIX+qlZrs9gw= +code.icb4dc0.de/buildr/api v0.0.0-20230823165833-52d0bd9e9741/go.mod h1:eCeIvmMgYp/04nar7GnBDORMdO4jHYQl/RMiNJhyGf8= +code.icb4dc0.de/buildr/common v0.0.0-20230823164147-3bae637e78ae h1:j2qiEThFUogxgU/zhuYTA/k0gp6pbTiYMSglwVKz47w= +code.icb4dc0.de/buildr/common v0.0.0-20230823164147-3bae637e78ae/go.mod h1:u0PEZjq0l3ICCUgARZObM6Hc0ZlqkcjK0PBEozhAdNQ= +code.icb4dc0.de/buildr/common v0.0.0-20230823164419-5a757074eaa3 h1:jWQCCE56vhfNoOuJj4JlCEcfXUB/40iR8L59j5xeh+s= +code.icb4dc0.de/buildr/common v0.0.0-20230823164419-5a757074eaa3/go.mod h1:u0PEZjq0l3ICCUgARZObM6Hc0ZlqkcjK0PBEozhAdNQ= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= @@ -15,8 +21,8 @@ golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230815205213-6bfd019c3878 h1:lv6/DhyiFFGsmzxbsUUTOkN29II+zeWHxvT8Lpdxsv0= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230815205213-6bfd019c3878/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d h1:uvYuEyMHKNt+lT4K3bN6fGswmK8qSvcreM3BwjDh+y4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= google.golang.org/grpc v1.57.0 h1:kfzNeI/klCGD2YPMUlaGNT3pxvYfga7smW3Vth8Zsiw= google.golang.org/grpc v1.57.0/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= diff --git a/protocol/constraints.go b/protocol/constraints.go deleted file mode 100644 index b715a7c..0000000 --- a/protocol/constraints.go +++ /dev/null @@ -1,21 +0,0 @@ -package protocol - -type Signed interface { - ~int | ~int8 | ~int16 | ~int32 | ~int64 -} - -type Unsigned interface { - ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr -} - -type Integer interface { - Signed | Unsigned -} - -type Float interface { - ~float32 | ~float64 -} - -type Numeric interface { - Integer | Float -} diff --git a/protocol/marshal.go b/protocol/marshal.go deleted file mode 100644 index 8d83cf4..0000000 --- a/protocol/marshal.go +++ /dev/null @@ -1,189 +0,0 @@ -package protocol - -import ( - "errors" - "fmt" - "reflect" - - rpcv1 "code.icb4dc0.de/buildr/api/generated/rpc/v1" -) - -var ErrExpectedStruct = errors.New("expected struct") - -func Marshal(in any) (*rpcv1.ModuleSpec, error) { - val := reflect.ValueOf(in) - if val.Kind() == reflect.Ptr { - val = val.Elem() - } - - if val.Kind() != reflect.Struct { - return nil, fmt.Errorf("%w: got %T", ErrExpectedStruct, in) - } - - return marshal(val) -} - -func marshal(in reflect.Value) (*rpcv1.ModuleSpec, error) { - numField := in.NumField() - - out := &rpcv1.ModuleSpec{ - Values: make(map[string]*rpcv1.ModuleSpec_Value, numField), - } - - inputType := in.Type() - - for i := 0; i < numField; i++ { - structField := inputType.Field(i) - if !structField.IsExported() { - continue - } - - out.Values[structField.Name] = mapReflectValueToSpecValue(in.Field(i)) - } - - return out, nil -} - -func mapReflectValueToSpecValue(in reflect.Value) *rpcv1.ModuleSpec_Value { - switch in.Kind() { - case reflect.Bool: - return boolValue(in.Bool()) - case reflect.String: - return stringValue(in.String()) - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return intValue(in.Int()) - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - return intValue(in.Uint()) - case reflect.Float32, reflect.Float64: - return floatValue(in.Float()) - case reflect.Struct: - return structValue(in) - case reflect.Slice, reflect.Array: - switch in.Type().Elem().Kind() { - case reflect.Bool: - return boolSliceValue(in) - case reflect.String: - return stringSliceValue(in) - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return intSliceValue(in, reflect.Value.Int) - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - return intSliceValue(in, func(v reflect.Value) int64 { - return int64(v.Uint()) - }) - case reflect.Float32, reflect.Float64: - return floatSliceValue(in) - default: - return nil - } - default: - return nil - } -} - -func structValue(in reflect.Value) *rpcv1.ModuleSpec_Value { - inputType := in.Type() - numFields := inputType.NumField() - result := &rpcv1.ModuleSpec_Value{ - Type: rpcv1.ModuleSpec_ValueTypeObject, - ComplexValue: make(map[string]*rpcv1.ModuleSpec_Value, numFields), - } - - for i := 0; i < numFields; i++ { - structField := inputType.Field(i) - if !structField.IsExported() { - continue - } - - result.ComplexValue[structField.Name] = mapReflectValueToSpecValue(in.Field(i)) - } - - return result -} - -func boolValue(in bool) *rpcv1.ModuleSpec_Value { - return &rpcv1.ModuleSpec_Value{ - Type: rpcv1.ModuleSpec_ValueTypeSingle, - SingleValue: &rpcv1.ModuleSpec_Value_BoolValue{ - BoolValue: in, - }, - } -} - -func boolSliceValue(val reflect.Value) *rpcv1.ModuleSpec_Value { - result := &rpcv1.ModuleSpec_Value{ - Type: rpcv1.ModuleSpec_ValueTypeBoolSlice, - BoolValues: make([]bool, 0, val.Len()), - } - - for i := 0; i < val.Len(); i++ { - result.BoolValues = append(result.BoolValues, val.Index(i).Bool()) - } - - return result -} - -func intValue[T Integer](in T) *rpcv1.ModuleSpec_Value { - return &rpcv1.ModuleSpec_Value{ - Type: rpcv1.ModuleSpec_ValueTypeSingle, - SingleValue: &rpcv1.ModuleSpec_Value_IntValue{ - IntValue: int64(in), - }, - } -} - -func intSliceValue(val reflect.Value, selector func(v reflect.Value) int64) *rpcv1.ModuleSpec_Value { - result := &rpcv1.ModuleSpec_Value{ - Type: rpcv1.ModuleSpec_ValueTypeIntSlice, - IntValues: make([]int64, 0, val.Len()), - } - - for i := 0; i < val.Len(); i++ { - result.IntValues = append(result.IntValues, selector(val.Index(i))) - } - - return result -} - -func floatValue[T Float](in T) *rpcv1.ModuleSpec_Value { - return &rpcv1.ModuleSpec_Value{ - Type: rpcv1.ModuleSpec_ValueTypeSingle, - SingleValue: &rpcv1.ModuleSpec_Value_DoubleValue{ - DoubleValue: float64(in), - }, - } -} - -func floatSliceValue(val reflect.Value) *rpcv1.ModuleSpec_Value { - result := &rpcv1.ModuleSpec_Value{ - Type: rpcv1.ModuleSpec_ValueTypeIntSlice, - DoubleValues: make([]float64, 0, val.Len()), - } - - for i := 0; i < val.Len(); i++ { - result.DoubleValues = append(result.DoubleValues, val.Index(i).Float()) - } - - return result -} - -func stringValue(in string) *rpcv1.ModuleSpec_Value { - return &rpcv1.ModuleSpec_Value{ - Type: rpcv1.ModuleSpec_ValueTypeSingle, - SingleValue: &rpcv1.ModuleSpec_Value_StringValue{ - StringValue: in, - }, - } -} - -func stringSliceValue(val reflect.Value) *rpcv1.ModuleSpec_Value { - result := &rpcv1.ModuleSpec_Value{ - Type: rpcv1.ModuleSpec_ValueTypeIntSlice, - StringValues: make([]string, 0, val.Len()), - } - - for i := 0; i < val.Len(); i++ { - result.StringValues = append(result.StringValues, val.Index(i).String()) - } - - return result -} diff --git a/protocol/marshal_test.go b/protocol/marshal_test.go deleted file mode 100644 index df78740..0000000 --- a/protocol/marshal_test.go +++ /dev/null @@ -1,220 +0,0 @@ -package protocol_test - -import ( - "testing" - - rpcv1 "code.icb4dc0.de/buildr/api/generated/rpc/v1" - "code.icb4dc0.de/buildr/wasi-module-sdk-go/protocol" -) - -func TestMarshal_Bool_Success(t *testing.T) { - input := struct { - IsDeleted bool - }{ - IsDeleted: true, - } - - spec, err := protocol.Marshal(input) - if err != nil { - t.Fatal(err) - } - - nameVal, ok := spec.Values["IsDeleted"] - if !ok { - t.Fatal("IsDeleted not found") - } - - if nameVal.Type != rpcv1.ModuleSpec_ValueTypeSingle { - t.Fatalf("Expected single value type, got %v", nameVal.Type) - } - - if s, ok := nameVal.SingleValue.(*rpcv1.ModuleSpec_Value_BoolValue); !ok { - t.Fatalf("Expected string value, got %v", nameVal.SingleValue) - } else if !s.BoolValue { - t.Errorf("Expected bool value to be true, got %t", s.BoolValue) - } -} - -func TestMarshal_BoolSlice_Success(t *testing.T) { - input := struct { - IsDeleted []bool - }{ - IsDeleted: []bool{true}, - } - - spec, err := protocol.Marshal(input) - if err != nil { - t.Fatal(err) - } - - nameVal, ok := spec.Values["IsDeleted"] - if !ok { - t.Fatal("IsDeleted not found") - } - - if nameVal.Type != rpcv1.ModuleSpec_ValueTypeBoolSlice { - t.Fatalf("Expected bool slice value type, got %v", nameVal.Type) - } - - if len(nameVal.BoolValues) < 1 { - t.Fatalf("Expected at least one bool value, got %d", len(nameVal.BoolValues)) - } - - if !nameVal.BoolValues[0] { - t.Errorf("Expected bool value to be true, got %t", nameVal.BoolValues[0]) - } -} - -func TestMarshal_Int_Success(t *testing.T) { - input := struct { - Age int - }{ - Age: 42, - } - - spec, err := protocol.Marshal(input) - if err != nil { - t.Fatal(err) - } - - nameVal, ok := spec.Values["Age"] - if !ok { - t.Fatal("Age not found") - } - - if nameVal.Type != rpcv1.ModuleSpec_ValueTypeSingle { - t.Fatalf("Expected single value type, got %v", nameVal.Type) - } - - if s, ok := nameVal.SingleValue.(*rpcv1.ModuleSpec_Value_IntValue); !ok { - t.Fatalf("Expected string value, got %v", nameVal.SingleValue) - } else if s.IntValue != 42 { - t.Errorf("Expected int value to be 42, got %d", s.IntValue) - } -} - -func TestMarshal_IntSlice_Success(t *testing.T) { - input := struct { - Ages []int - }{ - Ages: []int{42}, - } - - spec, err := protocol.Marshal(input) - if err != nil { - t.Fatal(err) - } - - nameVal, ok := spec.Values["Ages"] - if !ok { - t.Fatal("Ages not found") - } - - if nameVal.Type != rpcv1.ModuleSpec_ValueTypeIntSlice { - t.Fatalf("Expected int slice value type, got %v", nameVal.Type) - } - - if len(nameVal.IntValues) < 1 { - t.Fatalf("Expected at least one bool value, got %d", len(nameVal.BoolValues)) - } - - if nameVal.IntValues[0] != 42 { - t.Errorf("Expected int value to be 52, got %d", nameVal.IntValues[0]) - } -} - -func TestMarshal_StringField_Success(t *testing.T) { - input := struct { - Name string - }{ - Name: "John Doe", - } - - spec, err := protocol.Marshal(input) - if err != nil { - t.Fatal(err) - } - - nameVal, ok := spec.Values["Name"] - if !ok { - t.Fatal("Name not found") - } - - if nameVal.Type != rpcv1.ModuleSpec_ValueTypeSingle { - t.Fatalf("Expected single value type, got %v", nameVal.Type) - } - - if s, ok := nameVal.SingleValue.(*rpcv1.ModuleSpec_Value_StringValue); !ok { - t.Fatalf("Expected string value, got %v", nameVal.SingleValue) - } else if s.StringValue != "John Doe" { - t.Errorf("Expected string value to be John Doe, got %s", s.StringValue) - } -} - -func TestMarshal_Float64_Success(t *testing.T) { - input := struct { - Pi float64 - }{ - Pi: 3.14, - } - - spec, err := protocol.Marshal(input) - if err != nil { - t.Fatal(err) - } - - nameVal, ok := spec.Values["Pi"] - if !ok { - t.Fatal("Pi not found") - } - - if nameVal.Type != rpcv1.ModuleSpec_ValueTypeSingle { - t.Fatalf("Expected single value type, got %v", nameVal.Type) - } - - if s, ok := nameVal.SingleValue.(*rpcv1.ModuleSpec_Value_DoubleValue); !ok { - t.Fatalf("Expected double value, got %v", nameVal.SingleValue) - } else if s.DoubleValue-3.14 > 0.000001 { - t.Errorf("Expected double value to be 3.14, got %f", s.DoubleValue) - } -} - -func TestMarshal_NestedStruct_Success(t *testing.T) { - type Address struct { - City string - } - input := struct { - Address Address - }{ - Address: Address{ - City: "New York", - }, - } - - spec, err := protocol.Marshal(input) - if err != nil { - t.Fatal(err) - } - - addressVal, ok := spec.Values["Address"] - if !ok { - t.Fatal("Address not found") - } - - if addressVal.Type != rpcv1.ModuleSpec_ValueTypeObject { - t.Fatalf("Expected object value type, got %v", addressVal.Type) - } - - cityVal, ok := addressVal.ComplexValue["City"] - if !ok { - t.Fatal("City not found") - } - - if cityVal.Type != rpcv1.ModuleSpec_ValueTypeSingle { - t.Fatalf("Expected single value type, got %v", cityVal.Type) - } - - if cityVal.SingleValue.(*rpcv1.ModuleSpec_Value_StringValue).StringValue != "New York" { - t.Errorf("Expected string value to be New York, got %s", cityVal.SingleValue.(*rpcv1.ModuleSpec_Value_StringValue).StringValue) - } -} diff --git a/protocol/unmarshal.go b/protocol/unmarshal.go deleted file mode 100644 index 2a289a0..0000000 --- a/protocol/unmarshal.go +++ /dev/null @@ -1,206 +0,0 @@ -package protocol - -import ( - "errors" - "fmt" - "reflect" - "strings" - - rpcv1 "code.icb4dc0.de/buildr/api/generated/rpc/v1" -) - -var ErrUnmatchingType = errors.New("field type does not match wire value") - -func Unmarshal(input *rpcv1.ModuleSpec, into any) error { - cfg := unmarshalConfig{ - TagName: "hcl", - } - - val := reflect.ValueOf(into) - if val.Kind() != reflect.Ptr { - return errors.New("into value must be a pointer") - } - return cfg.unmarshal(input.Values, val.Elem(), reflect.TypeOf(into).Elem()) -} - -type unmarshalConfig struct { - TagName string -} - -func (cfg unmarshalConfig) unmarshal(input map[string]*rpcv1.ModuleSpec_Value, into reflect.Value, intoType reflect.Type) error { - for i := 0; i < intoType.NumField(); i++ { - tf := intoType.Field(i) - if !tf.IsExported() { - continue - } - - fieldName := cfg.fieldName(tf) - - val, ok := input[fieldName] - if !ok { - continue - } - - if mapped, err := cfg.mapSpecValueTo(val, tf.Type); err != nil { - return fmt.Errorf("failed to map value for field %s: %w", tf.Name, err) - } else if into.Type().Kind() == reflect.Pointer { - into.Elem().Field(i).Set(mapped) - } else { - into.Field(i).Set(mapped) - } - - delete(input, fieldName) - } - - if len(input) > 0 { - keys := make([]string, 0, len(input)) - for k := range input { - keys = append(keys, k) - } - return fmt.Errorf("unknown fields: %v", keys) - } - - return nil -} - -func (cfg unmarshalConfig) mapSpecValueTo(val *rpcv1.ModuleSpec_Value, targetType reflect.Type) (reflect.Value, error) { - switch val.Type { - case rpcv1.ModuleSpec_ValueTypeUnknown: - return reflect.Value{}, ErrUnmatchingType - case rpcv1.ModuleSpec_ValueTypeObject: - if targetType.Kind() == reflect.Struct { - structVal := reflect.New(targetType) - if err := cfg.unmarshal(val.ComplexValue, structVal, targetType); err != nil { - return reflect.Value{}, err - } else { - return structVal.Elem(), nil - } - } else if targetType.Kind() == reflect.Pointer && targetType.Elem().Kind() == reflect.Struct { - structVal := reflect.New(targetType.Elem()) - if err := cfg.unmarshal(val.ComplexValue, structVal, targetType.Elem()); err != nil { - return reflect.Value{}, err - } else { - return structVal, nil - } - } else if targetType.Kind() != reflect.Map { - return reflect.Value{}, fmt.Errorf("%w: expected struct, got %s", ErrUnmatchingType, targetType.String()) - } - fallthrough - case rpcv1.ModuleSpec_ValueTypeMap: - if targetType.Kind() != reflect.Map { - return reflect.Value{}, fmt.Errorf("%w: expected map, got %v", ErrUnmatchingType, targetType) - } - - mapVal := reflect.MakeMap(targetType) - - for k, v := range val.ComplexValue { - if mappedVal, err := cfg.mapSpecValueTo(v, targetType.Elem()); err != nil { - return reflect.Value{}, err - } else { - mapVal.SetMapIndex(reflect.ValueOf(k), mappedVal) - } - } - - return mapVal, nil - - case rpcv1.ModuleSpec_ValueTypeSingle: - switch sv := val.SingleValue.(type) { - case *rpcv1.ModuleSpec_Value_BoolValue: - if targetType.Kind() != reflect.Bool { - return reflect.Value{}, fmt.Errorf("%w: expected bool, got %v", ErrUnmatchingType, targetType) - } - return reflect.ValueOf(sv.BoolValue), nil - case *rpcv1.ModuleSpec_Value_StringValue: - if targetType.Kind() != reflect.String { - return reflect.Value{}, fmt.Errorf("%w: expected string, got %v", ErrUnmatchingType, targetType) - } - return reflect.ValueOf(sv.StringValue), nil - case *rpcv1.ModuleSpec_Value_IntValue: - if targetType.Kind() != reflect.Int { - return reflect.Value{}, fmt.Errorf("%w: expected int, got %v", ErrUnmatchingType, targetType) - } - return reflect.ValueOf(int(sv.IntValue)), nil - case *rpcv1.ModuleSpec_Value_DoubleValue: - if targetType.Kind() != reflect.Float64 { - return reflect.Value{}, fmt.Errorf("%w: expected float64, got %v", ErrUnmatchingType, targetType) - } - return reflect.ValueOf(sv.DoubleValue), nil - } - case rpcv1.ModuleSpec_ValueTypeBoolSlice: - if targetType.Kind() != reflect.Slice { - return reflect.Value{}, fmt.Errorf("%w: expected slice, got %v", ErrUnmatchingType, targetType) - } else if targetType.Elem().Kind() != reflect.Bool { - return reflect.Value{}, fmt.Errorf("%w: expected bool, got %v", ErrUnmatchingType, targetType.Elem()) - } - return reflect.ValueOf(val.BoolValues), nil - case rpcv1.ModuleSpec_ValueTypeStringSlice: - if targetType.Kind() != reflect.Slice { - return reflect.Value{}, fmt.Errorf("%w: expected slice, got %v", ErrUnmatchingType, targetType) - } else if targetType.Elem().Kind() != reflect.String { - return reflect.Value{}, fmt.Errorf("%w: expected string, got %v", ErrUnmatchingType, targetType.Elem()) - } - return reflect.ValueOf(val.StringValues), nil - case rpcv1.ModuleSpec_ValueTypeIntSlice: - if targetType.Kind() != reflect.Slice { - return reflect.Value{}, fmt.Errorf("%w: expected slice, got %v", ErrUnmatchingType, targetType) - } - switch targetType.Elem().Kind() { - case reflect.Int: - return reflect.ValueOf(convertNumericSlice[int64, int](val.IntValues)), nil - case reflect.Int8: - return reflect.ValueOf(convertNumericSlice[int64, int8](val.IntValues)), nil - case reflect.Int16: - return reflect.ValueOf(convertNumericSlice[int64, int16](val.IntValues)), nil - case reflect.Int32: - return reflect.ValueOf(convertNumericSlice[int64, int32](val.IntValues)), nil - case reflect.Int64: - return reflect.ValueOf(val.IntValues), nil - default: - return reflect.Value{}, fmt.Errorf("%w: expected int, got %v", ErrUnmatchingType, targetType.Elem()) - } - case rpcv1.ModuleSpec_ValueTypeDoubleSlice: - if targetType.Kind() != reflect.Slice { - return reflect.Value{}, fmt.Errorf("%w: expected slice, got %v", ErrUnmatchingType, targetType) - } - switch targetType.Elem().Kind() { - case reflect.Float32: - return reflect.ValueOf(convertNumericSlice[float64, float32](val.DoubleValues)), nil - case reflect.Float64: - return reflect.ValueOf(val.DoubleValues), nil - default: - return reflect.Value{}, fmt.Errorf("%w: expected int, got %v", ErrUnmatchingType, targetType.Elem()) - } - } - - return reflect.Value{}, nil -} - -func (cfg unmarshalConfig) fieldName(field reflect.StructField) string { - tagVal, ok := field.Tag.Lookup(cfg.TagName) - if !ok { - return field.Name - } - - split := strings.Split(tagVal, ",") - - switch { - case len(split) == 0: - fallthrough - case split[0] == "": - return strings.ToLower(field.Name) - default: - return strings.ToLower(split[0]) - } -} - -type numeric interface { - Integer | Float -} - -func convertNumericSlice[TIn numeric, TOut numeric](input []TIn) []TOut { - output := make([]TOut, len(input)) - for i, v := range input { - output[i] = TOut(v) - } - return output -} diff --git a/protocol/unmarshal_test.go b/protocol/unmarshal_test.go deleted file mode 100644 index 69497a5..0000000 --- a/protocol/unmarshal_test.go +++ /dev/null @@ -1,515 +0,0 @@ -package protocol_test - -import ( - "testing" - - rpcv1 "code.icb4dc0.de/buildr/api/generated/rpc/v1" - "code.icb4dc0.de/buildr/wasi-module-sdk-go/protocol" -) - -func TestUnmarshal_Bool_Success(t *testing.T) { - target := struct { - Delete bool - }{} - - spec := &rpcv1.ModuleSpec{ - Values: map[string]*rpcv1.ModuleSpec_Value{ - "Delete": { - Type: rpcv1.ModuleSpec_ValueTypeSingle, - SingleValue: &rpcv1.ModuleSpec_Value_BoolValue{ - BoolValue: true, - }, - }, - }, - } - - if err := protocol.Unmarshal(spec, &target); err != nil { - t.Errorf("Failed to unmarshal: %v", err) - } - - if !target.Delete { - t.Errorf("Expected Delete to be true") - } -} - -func TestUnmarshal_Bool_Err(t *testing.T) { - target := struct { - Delete string - }{} - - spec := &rpcv1.ModuleSpec{ - Values: map[string]*rpcv1.ModuleSpec_Value{ - "Delete": { - Type: rpcv1.ModuleSpec_ValueTypeSingle, - SingleValue: &rpcv1.ModuleSpec_Value_BoolValue{ - BoolValue: true, - }, - }, - }, - } - - if err := protocol.Unmarshal(spec, &target); err == nil { - t.Errorf("Expected error") - } else { - t.Log(err.Error()) - } -} - -func TestUnmarshal_Bool_Slice_Success(t *testing.T) { - target := struct { - Delete []bool - }{} - - spec := &rpcv1.ModuleSpec{ - Values: map[string]*rpcv1.ModuleSpec_Value{ - "Delete": { - Type: rpcv1.ModuleSpec_ValueTypeBoolSlice, - BoolValues: []bool{true}, - }, - }, - } - - if err := protocol.Unmarshal(spec, &target); err != nil { - t.Errorf("Failed to unmarshal: %v", err) - } - - if len(target.Delete) < 1 { - t.Errorf("Expected Delete to have at least one element") - } else if !target.Delete[0] { - t.Errorf("Expected Delete[0] to be true") - } -} - -func TestUnmarshal_Bool_Slice_Err(t *testing.T) { - target := struct { - Delete []string - }{} - - spec := &rpcv1.ModuleSpec{ - Values: map[string]*rpcv1.ModuleSpec_Value{ - "Delete": { - Type: rpcv1.ModuleSpec_ValueTypeBoolSlice, - BoolValues: []bool{true}, - }, - }, - } - - if err := protocol.Unmarshal(spec, &target); err == nil { - t.Errorf("Expected error") - } else { - t.Log(err.Error()) - } -} - -func TestUnmarshal_String_Success(t *testing.T) { - target := struct { - Name string - }{} - - spec := &rpcv1.ModuleSpec{ - Values: map[string]*rpcv1.ModuleSpec_Value{ - "Name": { - Type: rpcv1.ModuleSpec_ValueTypeSingle, - SingleValue: &rpcv1.ModuleSpec_Value_StringValue{ - StringValue: "Ted", - }, - }, - }, - } - - if err := protocol.Unmarshal(spec, &target); err != nil { - t.Errorf("Failed to unmarshal: %v", err) - } - - if target.Name != "Ted" { - t.Errorf("Expected Name to be 'Ted'") - } -} - -func TestUnmarshal_String_Err(t *testing.T) { - target := struct { - Name int - }{} - - spec := &rpcv1.ModuleSpec{ - Values: map[string]*rpcv1.ModuleSpec_Value{ - "Name": { - Type: rpcv1.ModuleSpec_ValueTypeSingle, - SingleValue: &rpcv1.ModuleSpec_Value_StringValue{ - StringValue: "Ted", - }, - }, - }, - } - - if err := protocol.Unmarshal(spec, &target); err == nil { - t.Errorf("Expected error") - } else { - t.Log(err.Error()) - } -} - -func TestUnmarshal_String_Slice_Success(t *testing.T) { - target := struct { - Names []string - }{} - - spec := &rpcv1.ModuleSpec{ - Values: map[string]*rpcv1.ModuleSpec_Value{ - "Names": { - Type: rpcv1.ModuleSpec_ValueTypeStringSlice, - StringValues: []string{"Ted"}, - }, - }, - } - - if err := protocol.Unmarshal(spec, &target); err != nil { - t.Errorf("Failed to unmarshal: %v", err) - } - - if len(target.Names) < 1 { - t.Errorf("Expected Names to have at least one element") - } else if target.Names[0] != "Ted" { - t.Errorf("Expected Names[0] to be 'Ted'") - } -} - -func TestUnmarshal_String_Slice_Err(t *testing.T) { - target := struct { - Names []int - }{} - - spec := &rpcv1.ModuleSpec{ - Values: map[string]*rpcv1.ModuleSpec_Value{ - "Names": { - Type: rpcv1.ModuleSpec_ValueTypeStringSlice, - StringValues: []string{"Ted"}, - }, - }, - } - - if err := protocol.Unmarshal(spec, &target); err == nil { - t.Errorf("Expected error") - } else { - t.Log(err.Error()) - } -} - -func TestUnmarshal_Int_Success(t *testing.T) { - target := struct { - Age int - }{} - - spec := &rpcv1.ModuleSpec{ - Values: map[string]*rpcv1.ModuleSpec_Value{ - "Age": { - Type: rpcv1.ModuleSpec_ValueTypeSingle, - SingleValue: &rpcv1.ModuleSpec_Value_IntValue{ - IntValue: 42, - }, - }, - }, - } - - if err := protocol.Unmarshal(spec, &target); err != nil { - t.Errorf("Failed to unmarshal: %v", err) - } - - if target.Age != 42 { - t.Errorf("Expected Age to be 42") - } -} - -func TestUnmarshal_Int_Err(t *testing.T) { - target := struct { - Age string - }{} - - spec := &rpcv1.ModuleSpec{ - Values: map[string]*rpcv1.ModuleSpec_Value{ - "Age": { - Type: rpcv1.ModuleSpec_ValueTypeSingle, - SingleValue: &rpcv1.ModuleSpec_Value_IntValue{ - IntValue: 42, - }, - }, - }, - } - - if err := protocol.Unmarshal(spec, &target); err == nil { - t.Errorf("Expected error") - } else { - t.Log(err.Error()) - } -} - -func TestUnmarshal_Int_Slice_Success(t *testing.T) { - target := struct { - Ages []int - }{} - - spec := &rpcv1.ModuleSpec{ - Values: map[string]*rpcv1.ModuleSpec_Value{ - "Ages": { - Type: rpcv1.ModuleSpec_ValueTypeIntSlice, - IntValues: []int64{42}, - }, - }, - } - - if err := protocol.Unmarshal(spec, &target); err != nil { - t.Errorf("Failed to unmarshal: %v", err) - } - - if len(target.Ages) < 1 { - t.Errorf("Expected Ages to have at least one element") - } else if target.Ages[0] != 42 { - t.Errorf("Expected Ages[0] to be 42") - } -} - -func TestUnmarshal_Int_Slice_Err(t *testing.T) { - target := struct { - Ages []string - }{} - - spec := &rpcv1.ModuleSpec{ - Values: map[string]*rpcv1.ModuleSpec_Value{ - "Ages": { - Type: rpcv1.ModuleSpec_ValueTypeIntSlice, - IntValues: []int64{42}, - }, - }, - } - - if err := protocol.Unmarshal(spec, &target); err == nil { - t.Errorf("Expected error") - } else { - t.Log(err.Error()) - } -} - -func TestUnmarshal_Double_Success(t *testing.T) { - target := struct { - Pi float64 - }{} - - spec := &rpcv1.ModuleSpec{ - Values: map[string]*rpcv1.ModuleSpec_Value{ - "Pi": { - Type: rpcv1.ModuleSpec_ValueTypeSingle, - SingleValue: &rpcv1.ModuleSpec_Value_DoubleValue{ - DoubleValue: 3.14, - }, - }, - }, - } - - if err := protocol.Unmarshal(spec, &target); err != nil { - t.Errorf("Failed to unmarshal: %v", err) - } - - if target.Pi-3.14 > 0.0000001 { - t.Errorf("Expected Pi to be 3.14") - } -} - -func TestUnmarshal_Double_Err(t *testing.T) { - target := struct { - Pi string - }{} - - spec := &rpcv1.ModuleSpec{ - Values: map[string]*rpcv1.ModuleSpec_Value{ - "Pi": { - Type: rpcv1.ModuleSpec_ValueTypeSingle, - SingleValue: &rpcv1.ModuleSpec_Value_DoubleValue{ - DoubleValue: 3.14, - }, - }, - }, - } - - if err := protocol.Unmarshal(spec, &target); err == nil { - t.Errorf("Expected error") - } else { - t.Log(err.Error()) - } -} - -func TestUnmarshal_Double_Slice_Success(t *testing.T) { - target := struct { - Pis []float64 - }{} - - spec := &rpcv1.ModuleSpec{ - Values: map[string]*rpcv1.ModuleSpec_Value{ - "Pis": { - Type: rpcv1.ModuleSpec_ValueTypeDoubleSlice, - DoubleValues: []float64{3.14}, - }, - }, - } - - if err := protocol.Unmarshal(spec, &target); err != nil { - t.Errorf("Failed to unmarshal: %v", err) - } - - if len(target.Pis) < 1 { - t.Errorf("Expected Pis to have at least one element") - } else if target.Pis[0]-3.14 > 0.0000001 { - t.Errorf("Expected Pis[0] to be 3.14") - } -} - -func TestUnmarshal_Double_Slice_Err(t *testing.T) { - target := struct { - Pis []string - }{} - - spec := &rpcv1.ModuleSpec{ - Values: map[string]*rpcv1.ModuleSpec_Value{ - "Pis": { - Type: rpcv1.ModuleSpec_ValueTypeDoubleSlice, - DoubleValues: []float64{3.14}, - }, - }, - } - - if err := protocol.Unmarshal(spec, &target); err == nil { - t.Errorf("Expected error") - } else { - t.Log(err.Error()) - } -} - -func TestUnmarshal_NestedStruct_Success(t *testing.T) { - target := struct { - Address struct { - City string - } - }{} - - spec := &rpcv1.ModuleSpec{ - Values: map[string]*rpcv1.ModuleSpec_Value{ - "Address": { - Type: rpcv1.ModuleSpec_ValueTypeObject, - ComplexValue: map[string]*rpcv1.ModuleSpec_Value{ - "City": { - Type: rpcv1.ModuleSpec_ValueTypeSingle, - SingleValue: &rpcv1.ModuleSpec_Value_StringValue{ - StringValue: "New York", - }, - }, - }, - }, - }, - } - - if err := protocol.Unmarshal(spec, &target); err != nil { - t.Errorf("Failed to unmarshal: %v", err) - } - - if target.Address.City != "New York" { - t.Errorf("Expected City to be 'New York'") - } -} - -func TestUnmarshal_NestedStructPointer_Success(t *testing.T) { - target := struct { - Address *struct { - City string - } - }{} - - spec := &rpcv1.ModuleSpec{ - Values: map[string]*rpcv1.ModuleSpec_Value{ - "Address": { - Type: rpcv1.ModuleSpec_ValueTypeObject, - ComplexValue: map[string]*rpcv1.ModuleSpec_Value{ - "City": { - Type: rpcv1.ModuleSpec_ValueTypeSingle, - SingleValue: &rpcv1.ModuleSpec_Value_StringValue{ - StringValue: "New York", - }, - }, - }, - }, - }, - } - - if err := protocol.Unmarshal(spec, &target); err != nil { - t.Errorf("Failed to unmarshal: %v", err) - } - - if target.Address.City != "New York" { - t.Errorf("Expected City to be 'New York'") - } -} - -func TestUnmarshal_Map_Success(t *testing.T) { - target := struct { - Values map[string]string - }{} - - spec := &rpcv1.ModuleSpec{ - Values: map[string]*rpcv1.ModuleSpec_Value{ - "Values": { - Type: rpcv1.ModuleSpec_ValueTypeMap, - ComplexValue: map[string]*rpcv1.ModuleSpec_Value{ - "City": { - Type: rpcv1.ModuleSpec_ValueTypeSingle, - SingleValue: &rpcv1.ModuleSpec_Value_StringValue{ - StringValue: "New York", - }, - }, - }, - }, - }, - } - - if err := protocol.Unmarshal(spec, &target); err != nil { - t.Errorf("Failed to unmarshal: %v", err) - } - - if target.Values["City"] != "New York" { - t.Errorf("Expected City to be 'New York'") - } -} - -func TestUnmarshal_NestedMap_Success(t *testing.T) { - target := struct { - City struct { - Labels map[string]string - } - }{} - - spec := &rpcv1.ModuleSpec{ - Values: map[string]*rpcv1.ModuleSpec_Value{ - "City": { - Type: rpcv1.ModuleSpec_ValueTypeObject, - ComplexValue: map[string]*rpcv1.ModuleSpec_Value{ - "Labels": { - Type: rpcv1.ModuleSpec_ValueTypeMap, - ComplexValue: map[string]*rpcv1.ModuleSpec_Value{ - "Region": { - Type: rpcv1.ModuleSpec_ValueTypeSingle, - SingleValue: &rpcv1.ModuleSpec_Value_StringValue{ - StringValue: "west", - }, - }, - }, - }, - }, - }, - }, - } - - if err := protocol.Unmarshal(spec, &target); err != nil { - t.Errorf("Failed to unmarshal: %v", err) - } - - if target.City.Labels["Region"] != "west" { - t.Errorf("Expected 'Region' to be 'west'") - } -} diff --git a/renovate.json b/renovate.json new file mode 100644 index 0000000..89cc11e --- /dev/null +++ b/renovate.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + ":dependencyDashboard", + ":ignoreModulesAndTests", + ":semanticPrefixFixDepsChoreOthers", + ":autodetectPinVersions", + ":prHourlyLimit2", + ":prConcurrentLimit10", + "group:monorepos", + "group:recommended", + "workarounds:all" + ], + "postUpdateOptions": [ + "gomodTidy1.17", + "gomodUpdateImportPaths" + ] +}