@@ -6,16 +6,15 @@ package yamlgen
6
6
import (
7
7
"bytes"
8
8
"context"
9
- "fmt"
10
9
"go/ast"
11
10
"go/parser"
12
11
"go/token"
13
12
"go/types"
14
- "io/ioutil"
15
13
"os"
16
14
"os/exec"
17
15
"path/filepath"
18
16
"regexp"
17
+ "strings"
19
18
20
19
"github.com/dave/jennifer/jen"
21
20
"github.com/pkg/errors"
@@ -31,18 +30,15 @@ func GenGoCode(src []byte) (string, error) {
31
30
// Create new main file.
32
31
fset := token .NewFileSet ()
33
32
generatedCode := jen .NewFile ("main" )
33
+ // Don't really need to format here, saves time.
34
+ // generatedCode.NoFormat = true
34
35
35
36
// Parse source file.
36
37
f , err := parser .ParseFile (fset , "" , src , parser .AllErrors )
37
38
if err != nil {
38
39
return "" , err
39
40
}
40
41
41
- // Add imports if needed(will not be used if not required in code).
42
- for _ , s := range f .Imports {
43
- generatedCode .ImportName (s .Path .Value [1 :len (s .Path .Value )- 1 ], "" )
44
- }
45
-
46
42
// Init statements for structs.
47
43
var init []jen.Code
48
44
// Declare config map, i.e, `configs := map[string]interface{}{}`.
@@ -54,33 +50,70 @@ func GenGoCode(src []byte) (string, error) {
54
50
if genericDecl , ok := decl .(* ast.GenDecl ); ok {
55
51
// Check if declaration spec is `type`.
56
52
if typeDecl , ok := genericDecl .Specs [0 ].(* ast.TypeSpec ); ok {
57
- var structFields []jen.Code
58
53
// Cast to `type struct`.
59
54
structDecl , ok := typeDecl .Type .(* ast.StructType )
60
55
if ! ok {
61
56
generatedCode .Type ().Id (typeDecl .Name .Name ).Id (string (src [typeDecl .Type .Pos ()- 1 : typeDecl .Type .End ()- 1 ]))
62
57
continue
63
58
}
64
- fields := structDecl .Fields .List
59
+
60
+ var structFields []jen.Code
65
61
arrayInit := make (jen.Dict )
66
62
67
63
// Loop and generate fields for each field.
64
+ fields := structDecl .Fields .List
68
65
for _ , field := range fields {
69
66
// Each field might have multiple names.
70
67
names := field .Names
71
68
for _ , n := range names {
72
69
if n .IsExported () {
73
70
pos := n .Obj .Decl .(* ast.Field )
74
71
75
- // Check if field is a slice type.
76
- sliceRe := regexp .MustCompile (`.*\[.*\].*` )
77
- if sliceRe .MatchString (types .ExprString (field .Type )) {
78
- arrayInit [jen .Id (n .Name )] = jen .Id (string (src [pos .Type .Pos ()- 1 : pos .Type .End ()- 1 ])).Values (jen .Id (string (src [pos .Type .Pos ()+ 1 : pos .Type .End ()- 1 ])).Values ())
72
+ // Copy struct field to generated code, with imports in case of other package imported field.
73
+ if pos .Tag != nil {
74
+ typeStr := string (src [pos .Type .Pos ()- 1 : pos .Type .End ()- 1 ])
75
+ // Check if field is imported from other package.
76
+ if strings .Contains (typeStr , "." ) {
77
+ typeArr := strings .SplitN (typeStr , "." , 2 )
78
+ // Match the import name with the import statement.
79
+ for _ , s := range f .Imports {
80
+ moduleName := ""
81
+ // Choose to copy same alias as in source file or use package name.
82
+ if s .Name == nil {
83
+ _ , moduleName = filepath .Split (s .Path .Value [1 : len (s .Path .Value )- 1 ])
84
+ generatedCode .ImportName (s .Path .Value [1 :len (s .Path .Value )- 1 ], moduleName )
85
+ } else {
86
+ moduleName = s .Name .String ()
87
+ generatedCode .ImportAlias (s .Path .Value [1 :len (s .Path .Value )- 1 ], moduleName )
88
+ }
89
+ // Add field to struct only if import name matches.
90
+ if moduleName == typeArr [0 ] {
91
+ structFields = append (structFields , jen .Id (n .Name ).Qual (s .Path .Value [1 :len (s .Path .Value )- 1 ], typeArr [1 ]).Id (pos .Tag .Value ))
92
+ }
93
+ }
94
+ } else {
95
+ structFields = append (structFields , jen .Id (n .Name ).Id (string (src [pos .Type .Pos ()- 1 :pos .Type .End ()- 1 ])).Id (pos .Tag .Value ))
96
+ }
79
97
}
80
98
81
- // Copy struct field to generated code.
82
- if pos .Tag != nil {
83
- structFields = append (structFields , jen .Id (n .Name ).Id (string (src [pos .Type .Pos ()- 1 :pos .Type .End ()- 1 ])).Id (pos .Tag .Value ))
99
+ // Check if field is a slice type.
100
+ sliceRe := regexp .MustCompile (`^\[.*\].*$` )
101
+ typeStr := types .ExprString (field .Type )
102
+ if sliceRe .MatchString (typeStr ) {
103
+ iArr := "[]int"
104
+ fArr := "[]float"
105
+ cArr := "[]complex"
106
+ uArr := "[]uint"
107
+ switch typeStr {
108
+ case "[]bool" , "[]string" , "[]byte" , "[]rune" ,
109
+ iArr , iArr + "8" , iArr + "16" , iArr + "32" , iArr + "64" ,
110
+ fArr + "32" , fArr + "64" ,
111
+ cArr + "64" , cArr + "128" ,
112
+ uArr , uArr + "8" , uArr + "16" , uArr + "32" , uArr + "64" , uArr + "ptr" :
113
+ arrayInit [jen .Id (n .Name )] = jen .Id (typeStr + "{}" )
114
+ default :
115
+ arrayInit [jen .Id (n .Name )] = jen .Id (string (src [pos .Type .Pos ()- 1 : pos .Type .End ()- 1 ])).Values (jen .Id (string (src [pos .Type .Pos ()+ 1 : pos .Type .End ()- 1 ])).Values ())
116
+ }
84
117
}
85
118
}
86
119
}
@@ -109,12 +142,12 @@ func GenGoCode(src []byte) (string, error) {
109
142
110
143
// Generate main function in new module.
111
144
generatedCode .Func ().Id ("main" ).Params ().Block (init ... )
112
- return fmt . Sprintf ( "%#v" , generatedCode ), nil
145
+ return generatedCode . GoString ( ), nil
113
146
}
114
147
115
148
// execGoCode executes and returns output from generated Go code.
116
149
func ExecGoCode (ctx context.Context , mainGo string ) ([]byte , error ) {
117
- tmpDir , err := ioutil . TempDir ("" , "structgen" )
150
+ tmpDir , err := os . MkdirTemp ("" , "structgen" )
118
151
if err != nil {
119
152
return nil , err
120
153
}
0 commit comments