diff --git a/cmd.go b/cmd.go index 933d8ed..ebea317 100644 --- a/cmd.go +++ b/cmd.go @@ -30,6 +30,9 @@ func GoImports() error { if err := runCommand("goimports", []string{"-w", "internal/service/service.go"}); err != nil { return err } + if err := runCommand("goimports", []string{"-w", "internal/storage/"}); err != nil { + return err + } return runCommand("goimports", []string{"-w", "internal/interactors/interactors.go"}) } diff --git a/cmd/tuugen/main.go b/cmd/tuugen/main.go index 280c19e..816240f 100644 --- a/cmd/tuugen/main.go +++ b/cmd/tuugen/main.go @@ -21,27 +21,41 @@ func main() { if err != nil { log.Fatalf("failed to load config, %v", err) } - // idea is to read a yaml file and create the project. + + log.Println("Generating protos") err = tuugen.GenerateProtos() if err != nil { log.Fatal(err) } + log.Println("Generating service implementation") err = tuugen.GenerateService(FS, cfg) if err != nil { log.Fatalf("failed to generate service: %v", err) } + log.Println("Generating interactor stubs") err = tuugen.GenerateInteractor(FS, cfg) if err != nil { log.Fatalf("failed to generate interactor: %v", err) } - if err := tuugen.GoFmt(); err != nil { - log.Fatalf("failed to run gofmt: %v", err) + + log.Println("Generating data models") + if err := tuugen.GenerateDataModels(FS, cfg); err != nil { + log.Fatalf("failed to generate data models: %v", err) } + + log.Printf("Running 'go mod init %s' \n", cfg.ImportPath) if err := tuugen.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 { + log.Fatalf("failed to run gofmt: %v", err) + } if err := tuugen.GoImports(); err != nil { log.Fatalf("failed to run 'goimports': %v", err) } + log.Println("------") + log.Println("🎉 All setup has been complete enjoy working on your business logic.") + log.Println("------") } diff --git a/cmd/tuugen/templates/data_model.go.tmpl b/cmd/tuugen/templates/data_model.go.tmpl index ec4f8bb..fbd2cb4 100644 --- a/cmd/tuugen/templates/data_model.go.tmpl +++ b/cmd/tuugen/templates/data_model.go.tmpl @@ -1,7 +1,7 @@ package storage -type {{ .Name }} struct { - {{ range .Properties }} - {{ .Name }} {{ .Type }} - {{ end }} +import "time" + +type {{ .Name }} struct { {{ range .Properties }} + {{ .NameCorrected }} {{ .DBTypeToGoType }}{{ end }} } \ No newline at end of file diff --git a/config.go b/config.go index 39494f4..2292ee1 100644 --- a/config.go +++ b/config.go @@ -1,6 +1,8 @@ package tuugen -import "gopkg.in/yaml.v2" +import ( + "gopkg.in/yaml.v2" +) type Config struct { Project string `yaml:"project"` diff --git a/db.go b/db.go index cb58707..4889a2c 100644 --- a/db.go +++ b/db.go @@ -1,9 +1,11 @@ package tuugen import ( + "embed" "html/template" - "path/filepath" "os" + "path/filepath" + "strings" ) type DataModel struct { @@ -17,14 +19,39 @@ type DataProperty struct { AutoIncrement bool `yaml:"autoinc"` } -func GenerateDataModels(cfg Config) error { +func (p DataProperty) NameCorrected() string { + if strings.Contains(p.Name, "_") { + parts := strings.Split(p.Name, "_") + newParts := []string{} + for _, p := range parts { + newParts = append(newParts, strings.Title(p)) + } + return strings.Join(newParts, "") + } + return strings.Title(p.Name) +} + +func (p DataProperty) DBTypeToGoType() string { + switch p.Type { + case "varchar": + return "string" + case "timestamp": + return "time.Time" + case "int": + return "int32" + default: + return "string" + } +} + +func GenerateDataModels(FS embed.FS, cfg Config) error { for _, dm := range cfg.DataModels { t, err := template.ParseFS(FS, "templates/data_model.go.tmpl") if err != nil { return err } - outputFile := "internal/data/models.go" + outputFile := "internal/storage/" + strings.ToLower(dm.Name) + ".go" fp := filepath.Dir(outputFile) if err := os.MkdirAll(fp, 0777); err != nil { return err @@ -39,4 +66,5 @@ func GenerateDataModels(cfg Config) error { return err } } + return nil } diff --git a/example_project/internal/pb/service/service.pb.go b/example_project/internal/pb/service/service.pb.go index c5b8f6c..62fa7a1 100644 --- a/example_project/internal/pb/service/service.pb.go +++ b/example_project/internal/pb/service/service.pb.go @@ -83,6 +83,53 @@ func (x *User) GetEmail() string { return "" } +type IdRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` +} + +func (x *IdRequest) Reset() { + *x = IdRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_service_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *IdRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*IdRequest) ProtoMessage() {} + +func (x *IdRequest) ProtoReflect() protoreflect.Message { + mi := &file_service_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use IdRequest.ProtoReflect.Descriptor instead. +func (*IdRequest) Descriptor() ([]byte, []int) { + return file_service_proto_rawDescGZIP(), []int{1} +} + +func (x *IdRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + var File_service_proto protoreflect.FileDescriptor var file_service_proto_rawDesc = []byte{ @@ -92,12 +139,17 @@ var file_service_proto_rawDesc = []byte{ 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x61, 0x6d, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6d, - 0x61, 0x69, 0x6c, 0x32, 0x37, 0x0a, 0x07, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x2c, - 0x0a, 0x0a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x12, 0x0d, 0x2e, 0x73, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x1a, 0x0d, 0x2e, 0x73, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x22, 0x00, 0x42, 0x15, 0x5a, 0x13, - 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x70, 0x62, 0x2f, 0x73, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x61, 0x69, 0x6c, 0x22, 0x1b, 0x0a, 0x09, 0x49, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, + 0x32, 0x67, 0x0a, 0x07, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x2c, 0x0a, 0x0a, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x12, 0x0d, 0x2e, 0x73, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x1a, 0x0d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x22, 0x00, 0x12, 0x2e, 0x0a, 0x07, 0x47, 0x65, 0x74, + 0x55, 0x73, 0x65, 0x72, 0x12, 0x12, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x49, + 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0d, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x22, 0x00, 0x42, 0x15, 0x5a, 0x13, 0x69, 0x6e, 0x74, + 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x70, 0x62, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -112,15 +164,18 @@ func file_service_proto_rawDescGZIP() []byte { return file_service_proto_rawDescData } -var file_service_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_service_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_service_proto_goTypes = []interface{}{ - (*User)(nil), // 0: service.User + (*User)(nil), // 0: service.User + (*IdRequest)(nil), // 1: service.IdRequest } var file_service_proto_depIdxs = []int32{ 0, // 0: service.Service.CreateUser:input_type -> service.User - 0, // 1: service.Service.CreateUser:output_type -> service.User - 1, // [1:2] is the sub-list for method output_type - 0, // [0:1] is the sub-list for method input_type + 1, // 1: service.Service.GetUser:input_type -> service.IdRequest + 0, // 2: service.Service.CreateUser:output_type -> service.User + 0, // 3: service.Service.GetUser:output_type -> service.User + 2, // [2:4] is the sub-list for method output_type + 0, // [0:2] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name @@ -144,6 +199,18 @@ func file_service_proto_init() { return nil } } + file_service_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*IdRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -151,7 +218,7 @@ func file_service_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_service_proto_rawDesc, NumEnums: 0, - NumMessages: 1, + NumMessages: 2, NumExtensions: 0, NumServices: 1, }, diff --git a/example_project/internal/pb/service/service_grpc.pb.go b/example_project/internal/pb/service/service_grpc.pb.go index 686b73e..9fc35e7 100644 --- a/example_project/internal/pb/service/service_grpc.pb.go +++ b/example_project/internal/pb/service/service_grpc.pb.go @@ -19,6 +19,7 @@ const _ = grpc.SupportPackageIsVersion7 // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. type ServiceClient interface { CreateUser(ctx context.Context, in *User, opts ...grpc.CallOption) (*User, error) + GetUser(ctx context.Context, in *IdRequest, opts ...grpc.CallOption) (*User, error) } type serviceClient struct { @@ -38,11 +39,21 @@ func (c *serviceClient) CreateUser(ctx context.Context, in *User, opts ...grpc.C return out, nil } +func (c *serviceClient) GetUser(ctx context.Context, in *IdRequest, opts ...grpc.CallOption) (*User, error) { + out := new(User) + err := c.cc.Invoke(ctx, "/service.Service/GetUser", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // ServiceServer is the server API for Service service. // All implementations must embed UnimplementedServiceServer // for forward compatibility type ServiceServer interface { CreateUser(context.Context, *User) (*User, error) + GetUser(context.Context, *IdRequest) (*User, error) mustEmbedUnimplementedServiceServer() } @@ -53,6 +64,9 @@ type UnimplementedServiceServer struct { func (UnimplementedServiceServer) CreateUser(context.Context, *User) (*User, error) { return nil, status.Errorf(codes.Unimplemented, "method CreateUser not implemented") } +func (UnimplementedServiceServer) GetUser(context.Context, *IdRequest) (*User, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetUser not implemented") +} func (UnimplementedServiceServer) mustEmbedUnimplementedServiceServer() {} // UnsafeServiceServer may be embedded to opt out of forward compatibility for this service. @@ -84,6 +98,24 @@ func _Service_CreateUser_Handler(srv interface{}, ctx context.Context, dec func( return interceptor(ctx, in, info, handler) } +func _Service_GetUser_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(IdRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ServiceServer).GetUser(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/service.Service/GetUser", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ServiceServer).GetUser(ctx, req.(*IdRequest)) + } + return interceptor(ctx, in, info, handler) +} + // Service_ServiceDesc is the grpc.ServiceDesc for Service service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -95,6 +127,10 @@ var Service_ServiceDesc = grpc.ServiceDesc{ MethodName: "CreateUser", Handler: _Service_CreateUser_Handler, }, + { + MethodName: "GetUser", + Handler: _Service_GetUser_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "service.proto", diff --git a/example_project/service.proto b/example_project/service.proto index b25bfc5..135f31b 100644 --- a/example_project/service.proto +++ b/example_project/service.proto @@ -6,6 +6,7 @@ option go_package = 'internal/pb/service'; service Service { rpc CreateUser(User) returns (User) {}; + rpc GetUser(IdRequest) returns (User) {}; } message User { @@ -13,3 +14,7 @@ message User { string team_id = 2; string email = 3; } + +message IdRequest { + string id = 1; +} \ No newline at end of file diff --git a/example_project/tuugen.yml b/example_project/tuugen.yml index 66d2aeb..36c3aa0 100644 --- a/example_project/tuugen.yml +++ b/example_project/tuugen.yml @@ -2,9 +2,9 @@ project: tuugen_test_project import_path: github.com/cubixle/tuugen/example_project proto: service.proto service_name: Service -db_models: +data_models: - name: User - - properties: + properties: - name: id type: varchar autoinc: true @@ -19,7 +19,7 @@ db_models: - name: updated_at type: timestamp - name: Team - - properties: + properties: - name: id type: varchar - name: name