diff --git a/cli/actions/api.go b/cli/actions/api.go index 1337e4f..efeb046 100644 --- a/cli/actions/api.go +++ b/cli/actions/api.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/fatih/color" "github.com/gozelus/zelus_rest/cli/codegen" + "github.com/iancoleman/strcase" "github.com/urfave/cli" "io" "os" @@ -54,8 +55,87 @@ func GenApis(ctx *cli.Context) error { return err } - if err := codegen.NewControllerGenner(apiFileMergeCopy, dir, "api").GenCode(); err != nil { + // 先解析 apiFile 以此生成 controllers + controllerGen := codegen.NewControllerGenner() + groupControllers, err := controllerGen.ParseApiFile(apiFileMergeCopy, "api") + if err != nil { return err } + // 生成 service 代码,用于服务于 controllers + for groupName, controllersMap := range groupControllers { + path := filepath.Join(dir, "services", groupName) + // 因为 service 层可能会有些业务代码,所以这个地方不再强制生成 + if _, err := mkdirIfNotExist(path); err != nil { + return err + } + // 遍历 controller 准备生成对应的 service 文件 + for _, c := range controllersMap { + filename := filepath.Join(path, strcase.ToSnake(c.Name+"_service.go")) + createFile, err := createIfNotExist(filename) + if err != nil { + return err + } + if createFile == nil { + fmt.Println(color.HiRedString("%s exist, will ignore ...", filename)) + continue + } + + fmt.Println(color.HiGreenString("%s created", filename)) + // 交给 genner + if err := codegen.NewServiceGener(c).GenCode(createFile); err != nil { + return err + } + if err := logFinishAndFmt(createFile.Name()); err != nil { + return err + } + } + } + + // 生成 controllers 的代码,用于服务 routes + for groupName, controllersMap := range groupControllers { + // 查看是否存在 dir/controllers/$groupName 这个文件夹 + // 如果存在,则强制删除,然后创建新的文件夹 + path := filepath.Join(dir, "controllers", groupName) + if err := forceCreateDir(path); err != nil { + return err + } + + // 遍历 controller 准备生成对应的 controller 文件 + for _, c := range controllersMap { + filename := filepath.Join(path, strcase.ToSnake(c.Name+"_controller.go")) + w, err := os.Create(filename) + if err != nil { + return err + } + fmt.Println(color.HiGreenString("%s created", filename)) + // 交给 genner + if err := controllerGen.GenCode(w, c); err != nil { + return err + } + if err := logFinishAndFmt(w.Name()); err != nil { + return err + } + } + } + return nil +} + +func forceCreateDir(path string) error { + _, err := os.Stat(path) + if err == nil { + fmt.Println(color.HiRedString("%s exist, will remove and recreate", path)) + if err := os.RemoveAll(path); err != nil { + return err + } + if err := os.MkdirAll(path, os.ModePerm); err != nil { + return err + } + } + if os.IsNotExist(err) { + fmt.Println(color.HiGreenString("%s not exist, will recreate", path)) + if err := os.MkdirAll(path, os.ModePerm); err != nil { + return err + } + } return nil } diff --git a/cli/actions/base.go b/cli/actions/base.go index 0798883..56d3438 100644 --- a/cli/actions/base.go +++ b/cli/actions/base.go @@ -4,12 +4,15 @@ import ( "fmt" "github.com/fatih/color" "os/exec" + "time" ) func logFinishAndFmt(dirPath string) error { + now := time.Now() + fmt.Println(color.HiGreenString("the file : %s will be fmt ...", dirPath)) if err := exec.Command("goimports", "-w", dirPath).Run(); err != nil { return err } - fmt.Println(color.HiGreenString("the file : %s done \n", dirPath)) + fmt.Println(color.HiGreenString("the file : %s fmt done ---> %dms", dirPath, time.Now().Sub(now).Milliseconds())) return nil } diff --git a/cli/actions/file.go b/cli/actions/file.go index 07fc8b1..c453d00 100644 --- a/cli/actions/file.go +++ b/cli/actions/file.go @@ -5,13 +5,25 @@ import ( "path/filepath" ) +func createIfNotExist(path string) (*os.File, error) { + var file *os.File + var err error + if _, err = os.Stat(path); os.IsNotExist(err) { + if file, err = os.Create(path); err != nil { + return nil, err + } + return file, nil + } + return nil, nil +} + func mkdirIfNotExist(dir string) (string, error) { dirAbs, err := filepath.Abs(dir) if err != nil { return "", err } if _, err := os.Stat(dirAbs); os.IsNotExist(err) { - if err := os.Mkdir(dirAbs, os.ModePerm); err != nil { + if err := os.MkdirAll(dirAbs, os.ModePerm); err != nil { return "", err } } diff --git a/cli/codegen/controller_gen.go b/cli/codegen/controller_gen.go index e091084..c3e21ae 100644 --- a/cli/codegen/controller_gen.go +++ b/cli/codegen/controller_gen.go @@ -9,16 +9,17 @@ import ( "github.com/iancoleman/strcase" "html/template" "io" - "os" - "path/filepath" "strings" ) type controller struct { - Name string - Handlers []*handler - PkgName string + Name string + Handlers []*handler + PkgName string + // 依赖的类型包名 TypesPkgName string + // 依赖的服务包名 + ServicesPkgName string } type handler struct { Method string @@ -29,63 +30,44 @@ type handler struct { Comments []string } type ControllerGenner struct { - readerFiler io.Reader - reader io.Reader - writer io.Writer - genPath string - typesPkgName string + // reader api 文件的读取入口抽象 + reader io.Reader + // 依赖的类型包名 + TypesPkgName string + // 依赖的服务包名 + ServicesPkgName string + + // key1 一级path -> 文件夹名 + // key2 二级path -> 文件名 + // 如 /v1/user/create -> { "v1" : { "user" : $controller } } + // controller 下的函数名,将会被 @handler 后的字符串映射 Group map[string]map[string]*controller } -func NewControllerGenner(file io.Reader, genPath, typesPkgName string) *ControllerGenner { - return &ControllerGenner{typesPkgName: typesPkgName, genPath: genPath, reader: file, Group: map[string]map[string]*controller{}} +func NewControllerGenner() *ControllerGenner { + return &ControllerGenner{Group: map[string]map[string]*controller{}} } -func (c *ControllerGenner) GenCode() error { - if err := c.initHandlers(); err != nil { - return err - } - if err := c.initDir(); err != nil { +// 将 controller 结构体转为代码写入文件 +func (c ControllerGenner) GenCode(w io.Writer, controller *controller) error { + if err := c.execTemplate(w, controller); err != nil { return err } return nil } -func (c *ControllerGenner) initDir() error { - for group, controllerMap := range c.Group { - path := filepath.Join(c.genPath, group) - _, err := os.Stat(path) - if err == nil { - fmt.Println(color.GreenString("%s exist, will remove and recreate", path)) - if err := os.RemoveAll(path); err != nil { - return err - } - if err := os.MkdirAll(path, os.ModePerm); err != nil { - return err - } - } - if os.IsNotExist(err) { - fmt.Println(color.GreenString("%s not exist, will recreate", path)) - if err := os.MkdirAll(path, os.ModePerm); err != nil { - return err - } - } - for _, controller := range controllerMap { - filename := filepath.Join(path, strcase.ToSnake(controller.Name+"_controller.go")) - w, err := os.Create(filename) - if err != nil { - return err - } - fmt.Println(color.GreenString("%s created", filename)) - if err := c.execTemplate(w, controller, group); err != nil { - return err - } - } +// 通过 api 文件生成 controller 定义的结构体 +func (c *ControllerGenner) ParseApiFile(file io.Reader, typesPkgName string) (map[string]map[string]*controller, error) { + c.reader = file + c.TypesPkgName = typesPkgName + if err := c.initHandlers(); err != nil { + return nil, err } - return nil + return c.Group, nil } -func (c *ControllerGenner) execTemplate(w io.Writer, controller *controller, groupName string) error { + +func (c *ControllerGenner) execTemplate(w io.Writer, controller *controller) error { var t *template.Template var err error if t, err = template.New("controller new").Parse(tpls.ControllerTpl); err != nil { @@ -93,6 +75,7 @@ func (c *ControllerGenner) execTemplate(w io.Writer, controller *controller, gro } return t.Execute(w, controller) } + func (c *ControllerGenner) initHandlers() error { reader := bufio.NewReader(c.reader) var lineNum int @@ -178,19 +161,21 @@ func (c *ControllerGenner) handleHandlerLine(lines []string) error { excontroller.Handlers = append(excontroller.Handlers, h) } else { c.Group[group][controllerName] = &controller{ - Name: strcase.ToCamel(controllerName), - Handlers: []*handler{h}, - PkgName: group, - TypesPkgName: c.typesPkgName, + Name: strcase.ToCamel(controllerName), + Handlers: []*handler{h}, + PkgName: group + "_controllers", + TypesPkgName: c.TypesPkgName, + ServicesPkgName: group + "_services", } } } else { c.Group[group] = map[string]*controller{ controllerName: { - Name: strcase.ToCamel(controllerName), - Handlers: []*handler{h}, - PkgName: group, - TypesPkgName: c.typesPkgName, + Name: strcase.ToCamel(controllerName), + Handlers: []*handler{h}, + PkgName: group + "_controllers", + TypesPkgName: c.TypesPkgName, + ServicesPkgName: group + "_services", }, } } diff --git a/cli/codegen/service_gen.go b/cli/codegen/service_gen.go new file mode 100644 index 0000000..197c3f6 --- /dev/null +++ b/cli/codegen/service_gen.go @@ -0,0 +1,51 @@ +package codegen + +import ( + "github.com/gozelus/zelus_rest/cli/tpls" + "html/template" + "io" + "strings" +) + +type serviceInfo struct { + Name string + Handlers []*handler + PkgName string + TypesPkgName string +} + +type ServiceGenner struct { + controller *controller + serviceInfo *serviceInfo +} + +func NewServiceGener(c *controller) *ServiceGenner { + s := &ServiceGenner{ + controller: c, + serviceInfo: initServiceInfo(c), + } + return s +} + +// file 要写入的文件 +// controller 要服务的 controller +func (s *ServiceGenner) GenCode(file io.Writer) error { + var t *template.Template + var err error + if t, err = template.New("service new").Parse(tpls.ServiceTpl); err != nil { + return err + } + if err := t.Execute(file, s.serviceInfo); err != nil { + return err + } + return nil +} + +func initServiceInfo(controller *controller) *serviceInfo { + return &serviceInfo{ + Name: controller.Name, + Handlers: controller.Handlers, + PkgName: strings.Split(controller.PkgName, "_")[0] + "_services", + TypesPkgName: controller.TypesPkgName, + } +} diff --git a/cli/tpls/controller.tpl.go b/cli/tpls/controller.tpl.go index 8e86495..9ff9c8b 100644 --- a/cli/tpls/controller.tpl.go +++ b/cli/tpls/controller.tpl.go @@ -1,18 +1,16 @@ package tpls -var ControllerTpl = `package {{ .PkgName }} +var ControllerTpl = `// Code generated by ZelusCtl. DO NOT EDIT. +package {{ .PkgName }} import ( "github.com/gozelus/zelus_rest" ) -type {{ .Name }}Service interface { {{ range .Handlers }} - {{ .Name }}(ctx rest.Context, req *{{ $.TypesPkgName }}.{{ .RequestType }}) (*{{ $.TypesPkgName }}.{{ .ResponseType }}, error) {{ end }} -} type {{ .Name }}Controller struct { - service {{ .Name }}Service + service *{{ $.ServicesPkgName }}.{{ .Name }}Service } -func New{{ .Name }}Controller(service {{ .Name }}Service) *{{ .Name }}Controller { +func New{{ .Name }}Controller(service *{{ $.ServicesPkgName }}.{{ .Name }}Service) *{{ .Name }}Controller { return &{{ .Name }}Controller{service : service} } diff --git a/cli/tpls/service.tpl.go b/cli/tpls/service.tpl.go new file mode 100644 index 0000000..cf4b1cd --- /dev/null +++ b/cli/tpls/service.tpl.go @@ -0,0 +1,15 @@ +package tpls + +var ServiceTpl = `package {{ .PkgName }} +type {{ .Name }}Service struct { + // 以后放入要依赖度的对象 +} +func New{{ .Name }}Service() *{{ .Name }}Service { + return &{{ .Name }}Service{} +} +{{ range .Handlers }} +func (s *{{ $.Name }}Service) {{ .Name }}(ctx rest.Context, request *{{ $.TypesPkgName }}.{{ .RequestType }}) (*{{ $.TypesPkgName }}.{{ .ResponseType }}, error) { + return nil, errors.New("no imp") +} +{{ end }} +`