Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 70 additions & 1 deletion ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ func astAssign(left, right ast.Expr) ast.Stmt {
}
}

func astAssignMulti(left, right []ast.Expr) ast.Stmt {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Variation of astAssign that works for generating foo, bar := gir, zim instead of just foo := gir

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,
Expand Down Expand Up @@ -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>))
Expand Down Expand Up @@ -198,6 +207,7 @@ func newAssignStmtSlice(
convertFuncName,
direct,
convert,
special,
),
}},
},
Expand All @@ -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>))
Expand Down Expand Up @@ -254,6 +265,7 @@ func newAssignStmtMap(
convertFuncName,
direct,
convert,
special,
),
newAssignStmtStructsAndPointers(
&ast.IndexExpr{
Expand All @@ -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: "_"}},
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We specifically ignore the error here by assigning to the blank identifier.

Copy link
Member Author

Choose a reason for hiding this comment

The 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,
Expand Down Expand Up @@ -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,
Expand Down
9 changes: 9 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
12 changes: 9 additions & 3 deletions generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Copy link
Member Author

Choose a reason for hiding this comment

The 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
}

Expand All @@ -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,
Expand All @@ -190,6 +191,7 @@ func generateConversion(cfg structConfig, t targetStruct, imports *imports) (gen
sourceField.ConvertFuncName(DirFrom),
kind.Direct,
kind.Convert,
kind.Special.ReverseDirection(),
Copy link
Member Author

Choose a reason for hiding this comment

The 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)
Expand All @@ -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,
Expand All @@ -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)
Expand Down Expand Up @@ -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,
Expand All @@ -268,6 +273,7 @@ func generateConversion(cfg structConfig, t targetStruct, imports *imports) (gen
sourceField.ConvertFuncName(DirFrom),
kind.ElemDirect,
kind.ElemConvert,
kind.ElemSpecial.ReverseDirection(),
))
}
}
Expand Down
19 changes: 14 additions & 5 deletions internal/e2e/core/cluster_node.go
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

Expand Down Expand Up @@ -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
Copy link
Member Author

Choose a reason for hiding this comment

The 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
Expand Down
5 changes: 5 additions & 0 deletions internal/e2e/go.mod
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
2 changes: 2 additions & 0 deletions internal/e2e/go.sum
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=
16 changes: 12 additions & 4 deletions internal/e2e/sourcepkg/node.go
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"
)
Expand Down Expand Up @@ -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
Copy link
Member Author

Choose a reason for hiding this comment

The 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
Expand Down
13 changes: 10 additions & 3 deletions load.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -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
}
Expand Down Expand Up @@ -180,6 +185,7 @@ var modeLoadAll = packages.NeedName |
packages.NeedFiles |
packages.NeedCompiledGoFiles |
packages.NeedImports |
packages.NeedModule |
Copy link
Member Author

Choose a reason for hiding this comment

The 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 |
Expand Down Expand Up @@ -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")
Expand Down
4 changes: 2 additions & 2 deletions load_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand All @@ -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{
Expand Down
8 changes: 5 additions & 3 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ func run(args []string) error {

type options struct {
source string
moduleWorkDir string
ignorePackageLoadErrors bool
tags string
}
Expand All @@ -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")
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needed this so the E2E test could use it after converting internal/e2e into a submodule.


flags.StringVar(&opts.tags, "tags", ".", "build tags to be passed when parsing the packages")

flags.BoolVar(&opts.ignorePackageLoadErrors, "ignore-package-load-errors", false,
Expand All @@ -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)
}
Expand All @@ -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)
}
Expand Down
Loading