-
Notifications
You must be signed in to change notification settings - Fork 4
automatically convert between common protobuf time/durations #6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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{ | ||
| // <left> = make(<leftType>, len(<right>)) | ||
|
|
@@ -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{ | ||
| // <left> = make(<leftType>, len(<right>)) | ||
|
|
@@ -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: "_"}}, | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We specifically ignore the error here by assigning to the blank identifier.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also in 2 more functions below. |
||
| []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. | ||
|
|
||
| // <left1>, <left2>, ... = <funcName>(<right1>, <right2>, ...) | ||
| 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, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -165,9 +165,9 @@ func generateConversion(cfg structConfig, t targetStruct, imports *imports) (gen | |
| } | ||
|
|
||
| // the assignmentKind is <target> := <source> so target==LHS source==RHS | ||
| rawKind, ok := computeAssignment(field.Type(), sourceField.SourceType) | ||
| if !ok { | ||
| assignErrFn(nil) | ||
| rawKind, err := computeAssignment(field.Type(), sourceField.SourceType, imports) | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I really should've had this return an error instead of a boolean the first time, so i fixed that since i found it useful to return a real error in the case where you are using protobufs but not in the exact way this PR handles. |
||
| 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(), | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We always generate both the TO and FROM functions at the same time, so when we apply the protobuf conversion in reverse we have to flip the direction so the function we use switches as well. |
||
| )) | ||
| 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(), | ||
| )) | ||
| } | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the target side of the conversion. |
||
| // 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 | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| module github.com/hashicorp/mog/internal/e2e | ||
|
|
||
| go 1.14 | ||
|
|
||
| require github.com/golang/protobuf v1.3.5 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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= |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is only handling pointer-protobufs to non-pointer-stdlib conversions for now, which is the most common. |
||
| // 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 | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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 | | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. TODO: I was going to figure out how to use this attribute to determine protobuf versions so the generated code could freely switch to different conversion functions if needed. |
||
| 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") | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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") | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Needed this so the E2E test could use it after converting |
||
|
|
||
| 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) | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Variation of
astAssignthat works for generatingfoo, bar := gir, ziminstead of justfoo := gir