diff --git a/Makefile b/Makefile index 3e4579f..633e717 100644 --- a/Makefile +++ b/Makefile @@ -1,2 +1,2 @@ build: - go build ./cmd/tuugen \ No newline at end of file + go build -o tuugen . \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..06e5d6d --- /dev/null +++ b/README.md @@ -0,0 +1,46 @@ +# Tuugen + +Tuugen takes a tuugen.yml and a grpc proto definition to generate common boiler plate logic. Some of the common boiler plate it generates is http routes, grpc service, data model structs and a main file with some default plumbing. + +Tuugen adds an interactor package which the http and grpc transport layers use for business logic. + +Feel free to pull the repo and play around with the example_project. + +## Using Tuugen. + +``` +go install github.com/cubixle/tuugen +``` + +And the run `tuugen` in the root dir of your project or wherever you have your tuugen and proto file. + +## Example tuugen file +```yaml +project: tuugen_test_project +import_path: github.com/cubixle/tuugen/example_project +proto_file: service.proto +grpc_file: internal/pb/service/service_grpc.pb.go +service_name: Service +data_models: + - name: User + properties: + - name: id + type: varchar + autoinc: true + - name: team_id + type: varchar + - name: name + type: varchar + - name: email + type: varchar + - name: created_at + type: timestamp + - name: updated_at + type: timestamp + - name: Team + properties: + - name: id + type: varchar + - name: name + type: varchar +``` \ No newline at end of file diff --git a/app.go b/app.go index d27c951..140e4c3 100644 --- a/app.go +++ b/app.go @@ -1,4 +1,4 @@ -package tuugen +package main import ( "embed" diff --git a/cmd.go b/cmd.go index c2b73fa..4b0e571 100644 --- a/cmd.go +++ b/cmd.go @@ -1,4 +1,4 @@ -package tuugen +package main import "os/exec" diff --git a/config.go b/config.go index c348bfe..1f2f702 100644 --- a/config.go +++ b/config.go @@ -1,4 +1,4 @@ -package tuugen +package main import ( "path/filepath" diff --git a/db.go b/db.go index 4889a2c..7843f6d 100644 --- a/db.go +++ b/db.go @@ -1,4 +1,4 @@ -package tuugen +package main import ( "embed" diff --git a/example_project/cmd/app/main.go b/example_project/cmd/app/main.go index 071ba08..deeb52e 100644 --- a/example_project/cmd/app/main.go +++ b/example_project/cmd/app/main.go @@ -18,7 +18,7 @@ func main() { } s := grpc.NewServer() - pb.RegisterServiceServer(s, service.New(store)) + pb.RegisterServiceServer(s, service.New()) if err := s.Serve(lis); err != nil { log.Fatalf("failed to serve: %v", err) } diff --git a/cmd/tuugen/main.go b/main.go similarity index 73% rename from cmd/tuugen/main.go rename to main.go index 495fd15..8b2c0bc 100644 --- a/cmd/tuugen/main.go +++ b/main.go @@ -4,8 +4,6 @@ import ( "embed" "io/ioutil" "log" - - "github.com/cubixle/tuugen" ) //go:embed templates/* @@ -17,47 +15,47 @@ func main() { if err != nil { log.Fatalf("failed to find tuugen.yaml, %v", err) } - cfg, err := tuugen.YamlToConfig(d) + cfg, err := YamlToConfig(d) if err != nil { log.Fatalf("failed to load config, %v", err) } log.Printf("Generating protos from %s\n", cfg.ProtoFile) - err = tuugen.GenerateProtos(cfg.ProtoFile) + err = GenerateProtos(cfg.ProtoFile) if err != nil { log.Fatal(err) } log.Println("Generating service implementation") - err = tuugen.GenerateService(FS, cfg) + err = GenerateService(FS, cfg) if err != nil { log.Fatalf("failed to generate service: %v", err) } log.Println("Generating interactor stubs") - err = tuugen.GenerateInteractor(FS, cfg) + err = GenerateInteractor(FS, cfg) if err != nil { log.Fatalf("failed to generate interactor: %v", err) } log.Println("Generating data models") - if err := tuugen.GenerateDataModels(FS, cfg); err != nil { + if err := GenerateDataModels(FS, cfg); err != nil { log.Fatalf("failed to generate data models: %v", err) } log.Println("Generate main.go") - if err := tuugen.GenerateMain(FS, cfg); err != nil { + if err := GenerateMain(FS, cfg); err != nil { log.Fatalf("failed to generate main file: %v", err) } log.Printf("Running 'go mod init %s' \n", cfg.ImportPath) - if err := tuugen.GoModInit(cfg.ImportPath); err != nil { + if err := GoModInit(cfg.ImportPath); err != nil { log.Fatalf("failed to run 'go mod init': %v", err) } log.Println("Running clean up") - if err := tuugen.GoFmt(); err != nil { + if err := GoFmt(); err != nil { log.Fatalf("failed to run gofmt: %v", err) } - if err := tuugen.GoImports(); err != nil { + if err := GoImports(); err != nil { log.Fatalf("failed to run 'goimports': %v", err) } log.Println("------") diff --git a/service.go b/service.go index cf5a970..8337733 100644 --- a/service.go +++ b/service.go @@ -1,4 +1,4 @@ -package tuugen +package main import ( "embed" @@ -67,20 +67,24 @@ func createFileFromProto(FS embed.FS, cfg Config, templateFile, outputFile strin if err != nil { return nil } - funcs, err := methodsFromProto(cfg) + def, err := parseProto(cfg) if err != nil { return err } - if err := t.Execute(f, funcs); err != nil { + // dirty check to see if we need to import the interactors. + if strings.Contains(templateFile, "service") { + def.Imports = append(def.Imports, cfg.ImportPath+"/internal/interactors") + } + + if err := t.Execute(f, def); err != nil { return err } return f.Close() } -func methodsFromProto(cfg Config) (serviceDef, error) { - funcs := []serviceFuncDef{} +func parseProto(cfg Config) (serviceDef, error) { fset := token.NewFileSet() f, err := parser.ParseFile(fset, cfg.GRPCFile, nil, 0) if err != nil { @@ -90,10 +94,17 @@ func methodsFromProto(cfg Config) (serviceDef, error) { importStrs := []string{} servicePath := filepath.Dir(cfg.GRPCFile) for _, i := range f.Imports { - importStrs = append(importStrs, i.Name.Name) + importStrs = append(importStrs, i.Path.Value) } - importStrs = append(importStrs, strings.Join([]string{cfg.ImportPath, strings.TrimLeft(servicePath, "/")}, "/"), cfg.ImportPath+"/internal/interactors") + importStrs = append(importStrs, strings.Join([]string{cfg.ImportPath, strings.TrimLeft(servicePath, "/")}, "/")) + funcs := parseFuncs(cfg, f) + + return serviceDef{Funcs: funcs, Imports: importStrs}, nil +} + +func parseFuncs(cfg Config, f *ast.File) []serviceFuncDef { + funcs := []serviceFuncDef{} for _, m := range f.Scope.Objects { if m.Name != cfg.ServiceName+"Server" { continue @@ -118,7 +129,7 @@ func methodsFromProto(cfg Config) (serviceDef, error) { funcs = append(funcs, f) } } - return serviceDef{Funcs: funcs, Imports: importStrs}, nil + return funcs } func parseFieldList(fl *ast.FieldList) []string { diff --git a/cmd/tuugen/templates/data_model.go.tmpl b/templates/data_model.go.tmpl similarity index 100% rename from cmd/tuugen/templates/data_model.go.tmpl rename to templates/data_model.go.tmpl diff --git a/cmd/tuugen/templates/interactors.go.tmpl b/templates/interactors.go.tmpl similarity index 100% rename from cmd/tuugen/templates/interactors.go.tmpl rename to templates/interactors.go.tmpl diff --git a/cmd/tuugen/templates/main.go.tmpl b/templates/main.go.tmpl similarity index 90% rename from cmd/tuugen/templates/main.go.tmpl rename to templates/main.go.tmpl index 43d7dcb..ca0fb7e 100644 --- a/cmd/tuugen/templates/main.go.tmpl +++ b/templates/main.go.tmpl @@ -18,7 +18,7 @@ func main() { } s := grpc.NewServer() - pb.Register{{.ServiceName}}Server(s, service.New(store)) + pb.Register{{.ServiceName}}Server(s, service.New()) if err := s.Serve(lis); err != nil { log.Fatalf("failed to serve: %v", err) } diff --git a/cmd/tuugen/templates/service.go.tmpl b/templates/service.go.tmpl similarity index 90% rename from cmd/tuugen/templates/service.go.tmpl rename to templates/service.go.tmpl index 3c8d765..ad0dc0e 100644 --- a/cmd/tuugen/templates/service.go.tmpl +++ b/templates/service.go.tmpl @@ -4,6 +4,10 @@ import ({{ range .Imports }} "{{ . }}"{{ end }} ) +func New() *Service { + return &Service{} +} + type Service struct { interactor *interactors.Interactor }