对于Repository具有固定参数的构造函数,但是对于Service的构造函数参数是不固定的。开始的时候,尝试用反射的方法,感觉非常麻烦,走不通,突发奇想,直接用文件解析的方法,解析Service的New构造函数。
type Service struct {
Name string
Args []ServiceArg
}
func (s Service) NewFuncString() string {
sb := new(strings.Builder)
hasEngine := false
hasLogger := false
repositoryArgs := make([]ServiceArg, 0)
for _, arg := range s.Args {
if strings.HasPrefix(arg.TypeName, "*xorm.Engine") && hasEngine == false {
hasEngine = true
} else if strings.HasPrefix(arg.TypeName, "*golog.Logger") && hasLogger == false {
hasLogger = true
} else if strings.HasPrefix(arg.TypeName, "repository.") {
repositoryArgs = append(repositoryArgs, arg)
}
}
if hasEngine {
sb.WriteString("engine,\n")
}
if hasLogger {
sb.WriteString("logger,\n")
}
for _, arg := range repositoryArgs {
sb.WriteString("repositoryContainer.")
sb.WriteString(arg.TypeName[len("repository."):])
sb.WriteString(",\n")
}
return sb.String()
}
type ServiceArg struct {
Name string
TypeName string
}
type ServiceContext struct {
Services []Service
}
func Test_GenerateServiceContainer(t *testing.T) {
files, err1 := os.ReadDir("../service")
if err1 != nil {
fmt.Println("parse file err:", err1)
return
}
services := make([]Service, 0)
for _, file := range files {
filename := file.Name()
if strings.HasSuffix(filename, "_service.go") {
serviceName := UpperCamelCase(filename[:len(filename)-3])
args, err2 := ServiceArgs(fmt.Sprintf("../service/%s", filename), serviceName)
if err2 != nil {
fmt.Println(err2)
return
}
services = append(services, Service{Name: serviceName, Args: args})
}
}
if f, err := os.Stat("../service/service_container.go"); f != nil && err == nil {
if err := os.Remove("../service/service_container.go"); err != nil {
fmt.Println("remove file err:", err)
return
}
}
f, err2 := os.OpenFile("../service/service_container.go", os.O_CREATE|os.O_WRONLY, 0666)
defer f.Close()
if err2 != nil {
fmt.Println("can not create output file,err:", err2)
return
}
tpl, err3 := template.New("service-container.tpl").ParseFiles("./service-container.tpl")
if err3 != nil {
fmt.Println("parse file err:", err3)
return
}
if err := tpl.Execute(f, &ServiceContext{Services: services}); err != nil {
fmt.Println("There was an error:", err.Error())
}
}
func UpperCamelCase(txt string) string {
sb := new(strings.Builder)
strs := strings.Split(txt, "_")
for _, str := range strs {
sb.WriteString(strings.ToUpper(string(str[0])))
sb.WriteString(str[1:])
}
return sb.String()
}
func ServiceArgs(filename, serviceName string) ([]ServiceArg, error) {
content, err1 := ReadGoFile(filename)
if err1 != nil {
return nil, err1
}
args := make([]ServiceArg, 0)
startTxt := fmt.Sprintf("New%s(", serviceName)
firstIndex := strings.Index(content, startTxt) + len(startTxt)
lastIndex := firstIndex + strings.Index(content[firstIndex:], ")")
reg := regexp.MustCompile(`\s+`)
strs := strings.Split(reg.ReplaceAllString(content[firstIndex:lastIndex], " "), ",")
for _, str := range strs {
txt := strings.TrimSpace(str)
if len(txt) > 0 {
args = append(args, Arg(txt))
}
}
return args, nil
}
func Arg(str string) ServiceArg {
strs := strings.Split(str, " ")
return ServiceArg{Name: strings.TrimSpace(strs[0]), TypeName: strings.TrimSpace(strs[1])}
}
func ReadGoFile(fileName string) (string, error) {
f, err := os.OpenFile(fileName, os.O_RDONLY, 0600)
defer f.Close()
if err != nil {
return "", err
} else {
if bytes, err := ioutil.ReadAll(f); err != nil {
return "", err
} else {
return string(bytes), nil
}
}
}
模板
package service
import (
"github.com/kataras/golog"
"xorm.io/xorm"
)
type ServiceContainer struct{
{{range $service := .Services -}}
{{.Name}} {{.Name}}
{{end -}}
}
func NewServiceContainer(engine *xorm.Engine, logger *golog.Logger, repositoryContainer repository.RepositoryContainer) *ServiceContainer{
return &ServiceContainer{
{{range $service := .Services -}}
{{.Name}}: New{{.Name}}({{.NewFuncString}}),
{{end -}}
}
}
网友评论