From d242cbe3a85ecdc043c41697f304cfb7353aa5d5 Mon Sep 17 00:00:00 2001 From: "R.B. Boyer" Date: Wed, 23 Mar 2022 14:34:46 -0500 Subject: [PATCH] automatically convert between common protobuf time/durations Fixes #1 --- ast.go | 71 +++++++++- config.go | 9 ++ generate.go | 12 +- internal/e2e/core/cluster_node.go | 19 ++- internal/e2e/go.mod | 5 + internal/e2e/go.sum | 2 + internal/e2e/sourcepkg/node.go | 16 ++- load.go | 13 +- load_test.go | 4 +- main.go | 8 +- main_e2e_test.go | 14 +- mapping.go | 187 ++++++++++++++++++++++---- testdata/TestE2E-expected-node_gen.go | 9 +- 13 files changed, 313 insertions(+), 56 deletions(-) create mode 100644 internal/e2e/go.mod create mode 100644 internal/e2e/go.sum diff --git a/ast.go b/ast.go index 66ed175..4ec1202 100644 --- a/ast.go +++ b/ast.go @@ -14,6 +14,14 @@ func astAssign(left, right ast.Expr) ast.Stmt { } } +func astAssignMulti(left, right []ast.Expr) ast.Stmt { + return &ast.AssignStmt{ + Lhs: left, + Tok: token.ASSIGN, + Rhs: right, + } +} + func astDeclare(varName string, varType ast.Expr) ast.Stmt { return &ast.DeclStmt{Decl: &ast.GenDecl{ Tok: token.VAR, @@ -157,6 +165,7 @@ func newAssignStmtSlice( convertFuncName string, direct bool, convert bool, + special specialAssignmentAttributes, ) ast.Stmt { return &ast.BlockStmt{List: []ast.Stmt{ // = make(, len()) @@ -198,6 +207,7 @@ func newAssignStmtSlice( convertFuncName, direct, convert, + special, ), }}, }, @@ -213,6 +223,7 @@ func newAssignStmtMap( convertFuncName string, direct bool, convert bool, + special specialAssignmentAttributes, ) ast.Stmt { return &ast.BlockStmt{List: []ast.Stmt{ // = make(, len()) @@ -254,6 +265,7 @@ func newAssignStmtMap( convertFuncName, direct, convert, + special, ), newAssignStmtStructsAndPointers( &ast.IndexExpr{ @@ -277,16 +289,55 @@ func newAssignStmt( convertFuncName string, direct bool, convert bool, + special specialAssignmentAttributes, ) ast.Stmt { - if direct && convert { + switch { + case direct && convert: panic("direct and convert cannot both be set") + case direct && !special.IsZero(): + panic("direct and special cannot both be set") + case convert && !special.IsZero(): + panic("convert and special cannot both be set") } + if convert { right = &ast.CallExpr{ Fun: leftType, Args: []ast.Expr{right}, } } + + switch { + case special.ProtobufDuration && special.ProtobufDirection == DirTo: + // struct-to-pointer + return newAssignStmtUserFunc( + left, + right, + "ptypes.DurationProto", + ) + case special.ProtobufDuration && special.ProtobufDirection == DirFrom: + // pointer-to-struct + return newAssignMultiStmtUserFunc( + []ast.Expr{left, &ast.Ident{Name: "_"}}, + []ast.Expr{right}, + "ptypes.Duration", + ) + case special.ProtobufTimestamp && special.ProtobufDirection == DirTo: + // struct-to-pointer + return newAssignMultiStmtUserFunc( + []ast.Expr{left, &ast.Ident{Name: "_"}}, + []ast.Expr{right}, + "ptypes.TimestampProto", + ) + case special.ProtobufTimestamp && special.ProtobufDirection == DirFrom: + // pointer-to-struct + return newAssignMultiStmtUserFunc( + []ast.Expr{left, &ast.Ident{Name: "_"}}, + []ast.Expr{right}, + "ptypes.Timestamp", + ) + } + if convertFuncName != "" && !direct && !convert { return newAssignStmtConvertible( left, @@ -320,6 +371,24 @@ func newAssignStmtUserFunc( }) } +func newAssignMultiStmtUserFunc( + left []ast.Expr, + right []ast.Expr, + userFuncName string, +) ast.Stmt { + // No special handling for pointers here if someone used the mog + // annotations themselves. The assumption is the user knows what + // they're doing. + + // , , ... = (, , ...) + return astAssignMulti(left, []ast.Expr{ + &ast.CallExpr{ + Fun: &ast.Ident{Name: userFuncName}, + Args: right, + }, + }) +} + // TODO: do the pointer stuff with go/types instead like everything else now? func newAssignStmtStructsAndPointers( left ast.Expr, diff --git a/config.go b/config.go index 4b8a63e..da779c5 100644 --- a/config.go +++ b/config.go @@ -80,6 +80,15 @@ type Direction string func (d Direction) String() string { return string(d) } +func (d Direction) Reverse() Direction { + if d == DirFrom { + return DirTo + } else if d == DirTo { + return DirFrom + } + return d +} + const ( DirFrom Direction = "From" DirTo Direction = "To" diff --git a/generate.go b/generate.go index 3726752..bc639a6 100644 --- a/generate.go +++ b/generate.go @@ -165,9 +165,9 @@ func generateConversion(cfg structConfig, t targetStruct, imports *imports) (gen } // the assignmentKind is := so target==LHS source==RHS - rawKind, ok := computeAssignment(field.Type(), sourceField.SourceType) - if !ok { - assignErrFn(nil) + rawKind, err := computeAssignment(field.Type(), sourceField.SourceType, imports) + if err != nil { + assignErrFn(err) continue } @@ -181,6 +181,7 @@ func generateConversion(cfg structConfig, t targetStruct, imports *imports) (gen sourceField.ConvertFuncName(DirTo), kind.Direct, kind.Convert, + kind.Special, )) from.Body.List = append(from.Body.List, newAssignStmt( srcExpr, @@ -190,6 +191,7 @@ func generateConversion(cfg structConfig, t targetStruct, imports *imports) (gen sourceField.ConvertFuncName(DirFrom), kind.Direct, kind.Convert, + kind.Special.ReverseDirection(), )) case *sliceAssignmentKind: targetElemTypeElem := typeToExpr(kind.LeftElem, imports, true) @@ -213,6 +215,7 @@ func generateConversion(cfg structConfig, t targetStruct, imports *imports) (gen sourceField.ConvertFuncName(DirTo), kind.ElemDirect, kind.ElemConvert, + kind.ElemSpecial, )) from.Body.List = append(from.Body.List, newAssignStmtSlice( srcExpr, @@ -223,6 +226,7 @@ func generateConversion(cfg structConfig, t targetStruct, imports *imports) (gen sourceField.ConvertFuncName(DirFrom), kind.ElemDirect, kind.ElemConvert, + kind.ElemSpecial.ReverseDirection(), )) case *mapAssignmentKind: targetKeyTypeElem := typeToExpr(kind.LeftKey, imports, true) @@ -258,6 +262,7 @@ func generateConversion(cfg structConfig, t targetStruct, imports *imports) (gen sourceField.ConvertFuncName(DirTo), kind.ElemDirect, kind.ElemConvert, + kind.ElemSpecial, )) from.Body.List = append(from.Body.List, newAssignStmtMap( srcExpr, @@ -268,6 +273,7 @@ func generateConversion(cfg structConfig, t targetStruct, imports *imports) (gen sourceField.ConvertFuncName(DirFrom), kind.ElemDirect, kind.ElemConvert, + kind.ElemSpecial.ReverseDirection(), )) } } diff --git a/internal/e2e/core/cluster_node.go b/internal/e2e/core/cluster_node.go index bde0f12..3a834ee 100644 --- a/internal/e2e/core/cluster_node.go +++ b/internal/e2e/core/cluster_node.go @@ -1,6 +1,10 @@ package core -import "github.com/hashicorp/mog/internal/e2e/core/inner" +import ( + "time" + + "github.com/hashicorp/mog/internal/e2e/core/inner" +) type Label string @@ -45,10 +49,15 @@ type ClusterNode struct { M7 map[string]Workload M8 map[string]*Workload - // S1 Workload // for testing struct-to-struct for slices - // S2 *Workload // for testing ptr-to-ptr for slices - // S3 Workload // for testing ptr-to-struct for slices - // S4 *Workload // for testing struct-to-ptr for slices + // T1 time.Time // for testing struct-to-struct for time + // T2 *time.Time // for testing ptr-to-ptr for time + T3 time.Time // for testing ptr-to-struct for time + // T4 *time.Time // for testing struct-to-ptr for time + + // D1 time.Duration // for testing struct-to-struct for duration + // D2 *time.Duration // for testing ptr-to-ptr for duration + D3 time.Duration // for testing ptr-to-struct for duration + // D4 *time.Duration // for testing struct-to-ptr for duration } type StringSlice []string diff --git a/internal/e2e/go.mod b/internal/e2e/go.mod new file mode 100644 index 0000000..beeeff2 --- /dev/null +++ b/internal/e2e/go.mod @@ -0,0 +1,5 @@ +module github.com/hashicorp/mog/internal/e2e + +go 1.14 + +require github.com/golang/protobuf v1.3.5 diff --git a/internal/e2e/go.sum b/internal/e2e/go.sum new file mode 100644 index 0000000..6124ed3 --- /dev/null +++ b/internal/e2e/go.sum @@ -0,0 +1,2 @@ +github.com/golang/protobuf v1.3.5 h1:F768QJ1E9tib+q5Sc8MkdJi1RxLTbRcTf8LJV56aRls= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= diff --git a/internal/e2e/sourcepkg/node.go b/internal/e2e/sourcepkg/node.go index fa48614..26c9668 100644 --- a/internal/e2e/sourcepkg/node.go +++ b/internal/e2e/sourcepkg/node.go @@ -1,6 +1,9 @@ package sourcepkg import ( + "github.com/golang/protobuf/ptypes/duration" + "github.com/golang/protobuf/ptypes/timestamp" + "github.com/hashicorp/mog/internal/e2e/core" "github.com/hashicorp/mog/internal/e2e/core/inner" ) @@ -56,10 +59,15 @@ type Node struct { M7 map[string]*Workload M8 map[string]Workload - // S1 Workload // for testing struct-to-struct for slices - // S2 *Workload // for testing ptr-to-ptr for slices - // S3 *Workload // for testing ptr-to-struct for slices - // S4 Workload // for testing struct-to-ptr for slices + // T1 timestamp.Timestamp // for testing struct-to-struct for time + // T2 *timestamp.Timestamp // for testing ptr-to-ptr for time + T3 *timestamp.Timestamp // for testing ptr-to-struct for time + // T4 timestamp.Timestamp // for testing struct-to-ptr for time + + // D1 duration.Duration // for testing struct-to-struct for duration + // D2 *duration.Duration // for testing ptr-to-ptr for duration + D3 *duration.Duration // for testing ptr-to-struct for duration + // D4 duration.Duration // for testing struct-to-ptr for duration } type StringSlice []string diff --git a/load.go b/load.go index 523849a..f3f2b98 100644 --- a/load.go +++ b/load.go @@ -53,10 +53,11 @@ type handlePkgLoadErr func(pkg *packages.Package) error // loadSourceStructs scans the provided package for struct definitions that // have mog annotations. -func loadSourceStructs(path string, tags string, handleErr handlePkgLoadErr) (sourcePkg, error) { +func loadSourceStructs(path string, workDir string, tags string, handleErr handlePkgLoadErr) (sourcePkg, error) { p := sourcePkg{Structs: map[string]structDecl{}} cfg := &packages.Config{ Mode: modeLoadAll, + Dir: workDir, } if tags == "" { tags = os.Getenv("GOTAGS") @@ -66,7 +67,11 @@ func loadSourceStructs(path string, tags string, handleErr handlePkgLoadErr) (so } { - fi, err := os.Stat(path) + fsPath := path + if workDir != "" { + fsPath = filepath.Join(workDir, path) + } + fi, err := os.Stat(fsPath) if err != nil { return p, err } @@ -180,6 +185,7 @@ var modeLoadAll = packages.NeedName | packages.NeedFiles | packages.NeedCompiledGoFiles | packages.NeedImports | + packages.NeedModule | packages.NeedDeps | packages.NeedTypes | packages.NeedSyntax | @@ -254,10 +260,11 @@ type targetStruct struct { Fields []*types.Var } -func loadTargetStructs(names []string, tags string) (map[string]targetPkg, error) { +func loadTargetStructs(names []string, workDir string, tags string) (map[string]targetPkg, error) { mode := packages.NeedTypes | packages.NeedTypesInfo | packages.NeedName cfg := &packages.Config{ Mode: mode, + Dir: workDir, } if tags == "" { tags = os.Getenv("GOTAGS") diff --git a/load_test.go b/load_test.go index d5d16ed..b9d887d 100644 --- a/load_test.go +++ b/load_test.go @@ -10,7 +10,7 @@ import ( ) func BenchmarkSourceStructs(b *testing.B) { - actual, err := loadSourceStructs("./internal/sourcepkg", "", packageLoadErrors) + actual, err := loadSourceStructs("./internal/sourcepkg", "", "", packageLoadErrors) require.NoError(b, err) require.Equal(b, []string{"GroupedSample", "Sample"}, actual.StructNames()) _, ok := actual.Structs["Sample"] @@ -24,7 +24,7 @@ func BenchmarkSourceStructs(b *testing.B) { // TODO: test non-built-in types // TODO: test types from other packages func BenchmarkLoadTargetStructs(b *testing.B) { - actual, err := loadTargetStructs([]string{"./internal/targetpkgone", "./internal/targetpkgtwo"}, "") + actual, err := loadTargetStructs([]string{"./internal/targetpkgone", "./internal/targetpkgtwo"}, "", "") assert.NilError(b, err) expected := map[string]targetPkg{ diff --git a/main.go b/main.go index 9d7aff5..3ae34a8 100644 --- a/main.go +++ b/main.go @@ -32,6 +32,7 @@ func run(args []string) error { type options struct { source string + moduleWorkDir string ignorePackageLoadErrors bool tags string } @@ -54,7 +55,8 @@ func setupFlags(name string) (*flag.FlagSet, *options) { // TODO: make this a positional arg, set a Usage func to document it flags.StringVar(&opts.source, "source", ".", "package path for source structs") - // TODO: make this a positional arg, set a Usage func to document it + flags.StringVar(&opts.moduleWorkDir, "modworkdir", "", "module working directory to run from") + flags.StringVar(&opts.tags, "tags", ".", "build tags to be passed when parsing the packages") flags.BoolVar(&opts.ignorePackageLoadErrors, "ignore-package-load-errors", false, @@ -67,7 +69,7 @@ func runMog(opts options) error { return fmt.Errorf("missing required source package") } - sources, err := loadSourceStructs(opts.source, opts.tags, opts.handlePackageLoadErrors) + sources, err := loadSourceStructs(opts.source, opts.moduleWorkDir, opts.tags, opts.handlePackageLoadErrors) if err != nil { return fmt.Errorf("failed to load source from %s: %w", opts.source, err) } @@ -82,7 +84,7 @@ func runMog(opts options) error { return nil } - targets, err := loadTargetStructs(targetPackages(cfg.Structs), opts.tags) + targets, err := loadTargetStructs(targetPackages(cfg.Structs), opts.moduleWorkDir, opts.tags) if err != nil { return fmt.Errorf("failed to load targets: %w", err) } diff --git a/main_e2e_test.go b/main_e2e_test.go index 101d01d..48b7422 100644 --- a/main_e2e_test.go +++ b/main_e2e_test.go @@ -20,7 +20,7 @@ var ( ) func TestE2E_NoSourcesFound(t *testing.T) { - sourcepkg := "./internal/e2e/sourcepkg-empty" + sourcepkg := "./sourcepkg-empty" // Cleanup the generated file when the test ends. The source must be a // loadable Go package, so it can not be easily generated into a temporary // directory. @@ -29,7 +29,7 @@ func TestE2E_NoSourcesFound(t *testing.T) { os.Remove(output) }) - args := []string{"mog", "-source", sourcepkg} + args := []string{"mog", "-source", sourcepkg, "-modworkdir", "./internal/e2e"} err := run(args) assert.NilError(t, err) @@ -42,7 +42,7 @@ func TestE2E(t *testing.T) { t.Skip("e2e test too slow for -short") } - sourcepkg := "./internal/e2e/sourcepkg" + sourcepkg := "./sourcepkg" // Cleanup the generated file when the test ends. The source must be a // loadable Go package, so it can not be easily generated into a temporary // directory. @@ -51,13 +51,17 @@ func TestE2E(t *testing.T) { os.Remove(output) }) - args := []string{"mog", "-source", sourcepkg} + args := []string{"mog", "-source", sourcepkg, "-modworkdir", "./internal/e2e"} err := run(args) assert.NilError(t, err) if *shouldVet { // go vet the file to check that it is valid Go syntax - icmd.RunCommand("go", "vet", sourcepkg).Assert(t, icmd.Success) + // icmd.RunCommand("go", "vet", sourcepkg).Assert(t, icmd.Success) + icmd.RunCmd(icmd.Cmd{ + Command: []string{"go", "vet", sourcepkg}, + Dir: "./internal/e2e", + }).Assert(t, icmd.Success) } actual, err := ioutil.ReadFile(output) diff --git a/mapping.go b/mapping.go index 1ef80a3..1d43e8e 100644 --- a/mapping.go +++ b/mapping.go @@ -1,6 +1,7 @@ package main import ( + "errors" "fmt" "go/types" ) @@ -12,6 +13,38 @@ type assignmentKind interface { fmt.Stringer } +type specialAssignmentAttributes struct { + // Convenience helpers + ProtobufTimestamp bool + ProtobufDuration bool + ProtobufDirection Direction +} + +func (s specialAssignmentAttributes) ReverseDirection() specialAssignmentAttributes { + s2 := s + s2.ProtobufDirection = s.ProtobufDirection.Reverse() + return s2 +} + +func (s specialAssignmentAttributes) IsZero() bool { + return s == specialAssignmentAttributes{} +} + +func (s specialAssignmentAttributes) GoString() string { + return s.String() +} + +func (s specialAssignmentAttributes) String() string { + switch { + case s.ProtobufTimestamp: + return "protobuf " + s.ProtobufDirection.String() + " timestamp" + case s.ProtobufDuration: + return "protobuf " + s.ProtobufDirection.String() + " duration" + default: + return "" + } +} + // singleAssignmentKind is a mapping operation between two fields that // ultimately are: // @@ -31,6 +64,8 @@ type singleAssignmentKind struct { // Convert implies that a simple type conversion is required. Convert bool + + Special specialAssignmentAttributes } var _ assignmentKind = (*singleAssignmentKind)(nil) @@ -44,6 +79,9 @@ func (o *singleAssignmentKind) String() string { if o.Convert { s += " (convert)" } + if !o.Special.IsZero() { + s += " (" + o.Special.String() + ")" + } return s } @@ -73,6 +111,8 @@ type sliceAssignmentKind struct { // ElemConvert implies that a simple type conversion is required for // elements of the slice. ElemConvert bool + + ElemSpecial specialAssignmentAttributes } var _ assignmentKind = (*sliceAssignmentKind)(nil) @@ -91,6 +131,9 @@ func (o *sliceAssignmentKind) String() string { if o.ElemConvert { s += " (convert)" } + if !o.ElemSpecial.IsZero() { + s += " (" + o.ElemSpecial.String() + ")" + } return s } @@ -129,6 +172,8 @@ type mapAssignmentKind struct { // ElemConvert implies that a simple type conversion is required for // elements of the map. ElemConvert bool + + ElemSpecial specialAssignmentAttributes } var _ assignmentKind = (*mapAssignmentKind)(nil) @@ -149,6 +194,9 @@ func (o *mapAssignmentKind) String() string { if o.ElemConvert { s += " (convert)" } + if !o.ElemSpecial.IsZero() { + s += " (" + o.ElemSpecial.String() + ")" + } return s } @@ -171,23 +219,71 @@ func convertibleButNotIdentical(typ, typeDecode types.Type) bool { return false } +func isAnyProtobufTimeOrDuration(types ...types.Type) bool { + for _, typ := range types { + switch { + case isProtobufDuration(typ), isProtobufTimestamp(typ): + return true + } + } + return false +} + +func isProtobufDuration(typ types.Type) bool { + pt, ok := typ.(*types.Pointer) + if !ok { + return false + } + if nt, ok := pt.Elem().(*types.Named); ok { + return nt.String() == "github.com/golang/protobuf/ptypes/duration.Duration" + } + return false +} + +func isProtobufTimestamp(typ types.Type) bool { + pt, ok := typ.(*types.Pointer) + if !ok { + return false + } + if nt, ok := pt.Elem().(*types.Named); ok { + return nt.String() == "github.com/golang/protobuf/ptypes/timestamp.Timestamp" + } + return false +} + +func isGoDuration(typ types.Type) bool { + if nt, ok := typ.(*types.Named); ok { + return nt.String() == "time.Duration" + } + return false +} + +func isGoTime(typ types.Type) bool { + if nt, ok := typ.(*types.Named); ok { + return nt.String() == "time.Time" + } + return false +} + +var errAssignmentNotSupported = errors.New("assignment not supported") + // computeAssignment attempts to determine how to assign something of the // rightType to something of the leftType. // -// If this is not possible, or not currently supported (nil, false) is -// returned. -func computeAssignment(leftType, rightType types.Type) (assignmentKind, bool) { +// If this is not possible, or not currently supported +// errAssignmentNotSupported is returned. +func computeAssignment(leftType, rightType types.Type, imports *imports) (assignmentKind, error) { // First check if the types are naturally directly assignable. Only allow // type pairs that are symmetrically assignable for simplicity. if types.AssignableTo(rightType, leftType) { if !types.AssignableTo(leftType, rightType) { - return nil, false + return nil, errAssignmentNotSupported } return &singleAssignmentKind{ Left: leftType, Right: rightType, Direct: true, - }, true + }, nil } // We don't really care about type aliases or pointerness here, so peel @@ -195,7 +291,38 @@ func computeAssignment(leftType, rightType types.Type) (assignmentKind, bool) { leftTypeDecode, leftOk := decodeType(leftType) rightTypeDecode, rightOk := decodeType(rightType) if !leftOk || !rightOk { - return nil, false + return nil, errAssignmentNotSupported + } + + if isAnyProtobufTimeOrDuration(leftType, rightType) { + // Note: we only do conversions for non-pointer stdlib to pointer + // protobufs with no aliasing. + + kind := &singleAssignmentKind{ + Left: leftType, + Right: rightType, + } + switch { + case isProtobufTimestamp(leftType) && isGoTime(rightType): + kind.Special.ProtobufTimestamp = true + kind.Special.ProtobufDirection = DirTo + case isProtobufDuration(leftType) && isGoDuration(rightType): + kind.Special.ProtobufDuration = true + kind.Special.ProtobufDirection = DirTo + case isGoTime(leftType) && isProtobufTimestamp(rightType): + kind.Special.ProtobufTimestamp = true + kind.Special.ProtobufDirection = DirFrom + case isGoDuration(leftType) && isProtobufDuration(rightType): + kind.Special.ProtobufDuration = true + kind.Special.ProtobufDirection = DirFrom + default: + // will require a helper function + return nil, fmt.Errorf("one struct field is a protobuf time or duration and requires a user func") + } + + imports.Add("", "github.com/golang/protobuf/ptypes") + + return kind, nil } if convertibleButNotIdentical(rightType, rightTypeDecode) || @@ -205,7 +332,7 @@ func computeAssignment(leftType, rightType types.Type) (assignmentKind, bool) { Left: leftType, Right: rightType, Convert: true, - }, true + }, nil } switch left := leftTypeDecode.(type) { @@ -213,39 +340,39 @@ func computeAssignment(leftType, rightType types.Type) (assignmentKind, bool) { // basic can only assign to basic _, ok := rightTypeDecode.(*types.Basic) if !ok { - return nil, false + return nil, errAssignmentNotSupported } return &singleAssignmentKind{ Left: leftType, Right: rightType, Direct: true, - }, true + }, nil case *types.Named: // named can only assign to named _, ok := rightTypeDecode.(*types.Named) if !ok { - return nil, false + return nil, errAssignmentNotSupported } return &singleAssignmentKind{ Left: leftType, Right: rightType, - }, true + }, nil case *types.Slice: // slices can only assign to slices right, ok := rightTypeDecode.(*types.Slice) if !ok { - return nil, false + return nil, errAssignmentNotSupported } // the elements have to be assignable - rawOp, ok := computeAssignment(left.Elem(), right.Elem()) - if !ok { - return nil, false + rawOp, err := computeAssignment(left.Elem(), right.Elem(), imports) + if err != nil { + return nil, err } op, ok := rawOp.(*singleAssignmentKind) if !ok { - return nil, false + return nil, errAssignmentNotSupported } return &sliceAssignmentKind{ @@ -255,36 +382,37 @@ func computeAssignment(leftType, rightType types.Type) (assignmentKind, bool) { RightElem: right.Elem(), ElemDirect: op.Direct, ElemConvert: op.Convert, - }, true + ElemSpecial: op.Special, + }, nil case *types.Map: right, ok := rightTypeDecode.(*types.Map) if !ok { - return nil, false + return nil, errAssignmentNotSupported } - rawKeyOp, ok := computeAssignment(left.Key(), right.Key()) - if !ok { - return nil, false + rawKeyOp, err := computeAssignment(left.Key(), right.Key(), imports) + if err != nil { + return nil, err } // the map keys have to be directly assignable keyOp, ok := rawKeyOp.(*singleAssignmentKind) if !ok { - return nil, false + return nil, errAssignmentNotSupported } if !keyOp.Direct { - return nil, false + return nil, errAssignmentNotSupported } // the map values have to be assignable - rawOp, ok := computeAssignment(left.Elem(), right.Elem()) - if !ok { - return nil, false + rawOp, err := computeAssignment(left.Elem(), right.Elem(), imports) + if err != nil { + return nil, err } op, ok := rawOp.(*singleAssignmentKind) if !ok { - return nil, false + return nil, errAssignmentNotSupported } return &mapAssignmentKind{ @@ -296,8 +424,9 @@ func computeAssignment(leftType, rightType types.Type) (assignmentKind, bool) { RightElem: right.Elem(), ElemDirect: op.Direct, ElemConvert: op.Convert, - }, true + ElemSpecial: op.Special, + }, nil } - return nil, false + return nil, errAssignmentNotSupported } diff --git a/testdata/TestE2E-expected-node_gen.go b/testdata/TestE2E-expected-node_gen.go index effa7b4..7b2d918 100644 --- a/testdata/TestE2E-expected-node_gen.go +++ b/testdata/TestE2E-expected-node_gen.go @@ -2,7 +2,10 @@ package sourcepkg -import "github.com/hashicorp/mog/internal/e2e/core" +import ( + "github.com/golang/protobuf/ptypes" + "github.com/hashicorp/mog/internal/e2e/core" +) func NodeToCore(s *Node, t *core.ClusterNode) { if s == nil { @@ -166,6 +169,8 @@ func NodeToCore(s *Node, t *core.ClusterNode) { t.M8[k] = y } } + t.T3, _ = ptypes.Timestamp(s.T3) + t.D3, _ = ptypes.Duration(s.D3) } func NodeFromCore(t *core.ClusterNode, s *Node) { if s == nil { @@ -323,6 +328,8 @@ func NodeFromCore(t *core.ClusterNode, s *Node) { s.M8[k] = y } } + s.T3, _ = ptypes.TimestampProto(t.T3) + s.D3 = ptypes.DurationProto(t.D3) } func WorkloadToCore(s *Workload, t *core.Workload) { if s == nil {