-
Notifications
You must be signed in to change notification settings - Fork 1
/
seeder.go
190 lines (177 loc) · 4.92 KB
/
seeder.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
package gomvc
import (
"errors"
"fmt"
"go/ast"
"go/parser"
"go/token"
"log"
"os"
"path/filepath"
"strings"
"github.com/iancoleman/strcase"
"github.com/spf13/cobra"
"github.com/Fanatics/toast/collector"
)
var seed = &cobra.Command{
Use: "seed",
Short: "Generate seed files",
Run: func(cmd *cobra.Command, args []string) {
dest, _ := cmd.LocalFlags().GetString("dest")
if dest == "" {
path, err := os.Getwd()
if err != nil {
panic(err)
}
dest = path
}
cmdDir := filepath.Join(dest, "cmd")
createDirIfNotExists(cmdDir)
if err := CreateSeedCommand(dest); err != nil {
panic(err)
}
},
}
// Seed is the cli command that creates new seeders based on structs in your
// application's "models" directory
func Seed() *cobra.Command {
return seed
}
// CreateSeedCommand creates a single cmd that inserts records for all models
func CreateSeedCommand(dest string) error {
modelDir := filepath.Join(dest, "models")
data, err := collectFileData(modelDir)
if err != nil {
log.Println("filepath walk error", err)
return err
}
var modelNames []string
iterateModels(data, func(s collector.Struct) error {
modelNames = append(modelNames, s.Name)
return nil
})
if len(modelNames) == 0 {
return errors.New("No models found")
}
content := createContentsFromTemplate("sqlboiler/seeder.tmpl", struct {
Models []string
}{Models: modelNames})
commandDir := filepath.Join(dest, "cmd/seed")
createDirIfNotExists(commandDir)
fileName := filepath.Join(commandDir, "main.go")
return CreateFileFromString(fileName, content)
}
// CreateSeederWithName creates a file with a single function. This function
// connects to the DB and inserts records for each model using each the model
// factories.
func CreateSeederWithName(structName string, destDir string) error {
// default to current dir if none given
if destDir == "" {
destDir = "./cmd/seed"
}
createDirIfNotExists(destDir)
fileName := fmt.Sprintf("%s.go", strcase.ToSnake(structName))
dest := filepath.Join(destDir, fileName)
name := strcase.ToCamel(structName)
data := map[string]string{"Name": name}
contents := createContentsFromTemplate("sqlboiler/seed.tmpl", data)
return CreateFileFromString(dest, contents)
}
// CreateSeedersFromModels iterates through the given directory
// finds models the creates seed files in the given destination
func CreateSeedersFromModels(dir string, dest string) error {
data, err := collectFileData(dir)
if err != nil {
log.Println("filepath walk error", err)
return err
}
if len(data.Packages) == 0 {
log.Println("No model data found. Exiting.")
return nil
}
iterateModels(data, func(s collector.Struct) error {
if err := CreateSeederWithName(s.Name, dest); err != nil {
return err
}
return nil
})
return nil
}
func iterateModels(data *collector.Data, lambda func(s collector.Struct) error) {
for _, p := range data.Packages {
for _, f := range p.Files {
for _, s := range f.Structs {
var hasAnyTags bool // hack
for _, field := range s.Fields {
if len(field.Tag) > 0 {
hasAnyTags = true
tag := strings.Trim(field.Tag, "`")
parts := strings.Split(tag, " ")
for _, p := range parts {
tag := strings.Split(p, ":")
name := tag[0]
// some db tools use a 'db' struct tag to help with marshalling
// so we use that convention here as a way to determine what the sql column is
if name == "db" {
values := strings.Trim(tag[1], "\"")
valueParts := strings.Split(values, ",")
log.Println(valueParts)
}
}
}
}
if !hasAnyTags {
continue
}
if err := lambda(s); err != nil {
panic(err)
}
}
}
}
}
func collectFileData(dir string) (*collector.Data, error) {
fset := token.NewFileSet()
data := &collector.Data{}
err := filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error {
if err != nil {
log.Fatal("recursive walk error:", err)
}
// skip over files, only continue into directories for parser to enter
if !fi.IsDir() {
return nil
}
pkgs, err := parser.ParseDir(fset, path, nil, parser.ParseComments)
if err != nil {
log.Fatalf("parse dir error: %v\n", err)
}
for _, pkg := range pkgs {
p := collector.Package{
Name: pkg.Name,
}
for _, file := range pkg.Files {
c := &collector.FileCollector{}
ast.Walk(c, file)
f := collector.File{
Name: fset.Position(file.Pos()).Filename,
Package: pkg.Name,
Imports: c.Imports,
BuildTags: c.BuildTags,
Comments: c.Comments,
MagicComments: c.MagicComments,
GenerateComments: c.GenerateComments,
Consts: c.Consts,
Vars: c.Vars,
Structs: c.Structs,
TypeDefs: c.TypeDefs,
Interfaces: c.Interfaces,
Funcs: c.Funcs,
}
p.Files = append(p.Files, f)
}
data.Packages = append(data.Packages, p)
}
return nil
})
return data, err
}