用go-ioc实现基于文件解析的IOC

2021-08-31  本文已影响0人  EasyNetCN

go-ioc是本人写的一个基于go的开源项目,实现了基于go源文件解析,模板生成container代码的IOC功能。可以根据自己的需求自行扩展,来满足自己逻辑。
下面的例子使用go-ioc来生成简单的repository container,controller container和自定义比较复杂的service container。

configuration.go

package main

import (
    "io/ioutil"

    "gopkg.in/yaml.v3"
)

type Configuration struct {
    Project      string `yaml:"project"`
    GenerateType string `yaml:"generate-type"`
}

func NewConfiguration() (*Configuration, error) {
    conf := new(Configuration)

    data, err := ioutil.ReadFile("config.yml")

    if err != nil {
        return conf, err
    }

    err = yaml.Unmarshal(data, &conf)

    return conf, err
}

config.yml

project: 
generate-type: 

service.go

package main

import (
    "strings"

    "github.com/easynet-cn/go-ioc"
)

type Service struct {
    ioc.StructInfo
}

func (s Service) NewFuncString() string {
    sb := new(strings.Builder)

    for _, arg := range s.Args {
        if strings.HasPrefix(arg.TypeName, "repository.") {
            sb.WriteString("repositoryContainer.")
            sb.WriteString(arg.TypeName[len("repository."):])
            sb.WriteString(",\n")
        } else {
            dotIndex := strings.LastIndex(arg.TypeName, ".")

            if dotIndex > -1 {
                str := arg.TypeName[strings.LastIndex(arg.TypeName, ".")+1:]

                sb.WriteString(strings.ToLower(str[0:1]))
                sb.WriteString(str[1:])

                if strings.Contains(arg.TypeName, "[") && strings.Contains(arg.TypeName, "]") {
                    sb.WriteString("s")
                }

                sb.WriteString(",\n")
            } else if _, ok := baseTypes[arg.TypeName]; ok {
                sb.WriteString(arg.Name)
                sb.WriteString(",\n")
            } else {
                sb.WriteString(strings.ToLower(arg.TypeName[0:1]))
                sb.WriteString(arg.TypeName[1:])
                sb.WriteString(",\n")
            }

        }
    }

    return sb.String()
}

service_context.go

package main

import (
    "sort"
    "strings"
)

type ServiceContext struct {
    AllServices   []Service
    CitedServices []Service
    Services      []Service
}

func (c *ServiceContext) NewFuncString() string {
    typeNames := make([]string, 0)
    typeNameMap := make(map[string]string)
    typeNameArgNameMap := make(map[string]string)

    for _, s := range c.AllServices {
        for _, arg := range s.Args {
            typeName := arg.TypeName

            if strings.HasPrefix(typeName, "repository.") {
                typeName = "repository.RepositoryContainer"
            }

            if _, ok := typeNameMap[typeName]; !ok && strings.Contains(typeName, ".") {
                typeNameMap[typeName] = typeName
                typeNames = append(typeNames, typeName)
            } else if _, ok := baseTypes[typeName]; ok {
                if _, ok := typeNameMap[typeName]; !ok {
                    typeNameMap[typeName] = typeName
                    typeNames = append(typeNames, typeName)
                    typeNameArgNameMap[typeName] = arg.Name
                }
            }
        }
    }

    sort.Strings(typeNames)

    sb := new(strings.Builder)

    for _, typeName := range typeNames {
        if strings.HasPrefix(typeName, "repository.") {
            sb.WriteString("repositoryContainer ")
            sb.WriteString(typeName)
            sb.WriteString(",\n")
        } else {
            dotIndex := strings.LastIndex(typeName, ".")

            if dotIndex > -1 {
                str := typeName[strings.LastIndex(typeName, ".")+1:]

                sb.WriteString(strings.ToLower(str[0:1]))
                sb.WriteString(str[1:])

                if strings.Contains(typeName, "[") && strings.Contains(typeName, "]") {
                    sb.WriteString("s")
                }

                sb.WriteString(" ")
                sb.WriteString(typeName)
                sb.WriteString(",\n")
            } else if argName, ok := typeNameArgNameMap[typeName]; ok {
                sb.WriteString(argName)
                sb.WriteString(" ")
                sb.WriteString(typeName)
                sb.WriteString(",\n")
            } else {
                sb.WriteString(strings.ToLower(typeName[0:1]))
                sb.WriteString(typeName[1:])
                sb.WriteString(" ")
                sb.WriteString(typeName)
                sb.WriteString(",\n")
            }
        }

    }

    return sb.String()
}

main.go

package main

import (
    "fmt"
    "text/template"

    "github.com/easynet-cn/go-ioc"
)

func main() {
    config, err := NewConfiguration()

    if err != nil {
        fmt.Println("can not read configuration file,err:", err)

        return
    }

    generateTypes := make([]string, 0)

    if config.GenerateType == "all" {
        generateTypes = append(generateTypes, "controller", "repository", "service")
    } else {
        generateTypes = append(generateTypes, config.GenerateType)
    }

    for _, generateType := range generateTypes {
        name, templateFile, outputFilename := containerNameAndOutputFilename(generateType)

        containerConfig := ioc.ContainerConfig{
            Name:            name,
            TemplateFile:    templateFile,
            InputDirectory:  fmt.Sprintf("../%s/%s", config.Project, generateType),
            OutputDirectory: fmt.Sprintf("../%s/%s", config.Project, generateType),
            OutputFilename:  outputFilename,
        }

        generator := ioc.NewGeneralContainerGenerator(containerConfig)
        funcMap := template.FuncMap{"unescaped": ioc.Unescaped, "littleCamelCase": ioc.LittleCamelCase}

        c := convert

        if generateType == "service" {
            c = serviceContextConvert
        }

        result, err := generator.Generate(c, funcMap)

        fmt.Println(result, err)
    }
}

func containerNameAndOutputFilename(generateType string) (string, string, string) {
    if generateType == "controller" {
        return "ControllerContainer", "controller-container.tpl", "controller_container.go"
    } else if generateType == "repository" {
        return "RepositoryContainer", "repository-container.tpl", "repository_container.go"
    } else if generateType == "service" {
        return "ServiceContainer", "service-container.tpl", "service_container.go"
    }

    return "", "", ""
}

var baseTypes = map[string]string{
    "string":  "string",
    "*string": "*string",
    "int":     "int",
    "*int":    "*int",
    "int64":   "int64",
    "*int64":  "*int64",
}

func convert(structContext ioc.StructContext) interface{} {
    return structContext
}

func serviceContextConvert(structContext ioc.StructContext) interface{} {
    allServices := make([]Service, len(structContext.StructInfoes))

    for i, structInfo := range structContext.StructInfoes {
        allServices[i].StructInfo = structInfo
    }

    citedServiceMap := make(map[string]Service)
    citedServices := make([]Service, 0)

    for _, s := range allServices {
        for _, s1 := range allServices {
            if s.Name != s1.Name {
                for _, arg := range s1.Args {
                    if _, ok := citedServiceMap[s.Name]; !ok && s.Name == arg.TypeName {
                        citedServiceMap[s.Name] = s
                        citedServices = append(citedServices, s)
                    }
                }
            }
        }
    }

    processCitedService(&citedServices)

    services := make([]Service, 0)

    for _, s := range allServices {
        if _, ok := citedServiceMap[s.Name]; !ok {
            services = append(services, s)
        }
    }

    return &ServiceContext{AllServices: allServices, CitedServices: citedServices, Services: services}
}

func processCitedService(citedServices *[]Service) {
    for i, s1 := range *citedServices {
        for j, s2 := range *citedServices {
            if s1.Name != s2.Name {
                for _, arg := range s2.Args {
                    if arg.TypeName == s1.Name {
                        if i > j {
                            (*citedServices)[i], (*citedServices)[j] = (*citedServices)[j], (*citedServices)[i]

                            processCitedService(citedServices)
                        }
                    }
                }
            }
        }
    }
}

controller-container.tpl

package controller

import (
    "github.com/kataras/iris/v12/mvc"
)

// Count: {{len .StructInfoes}}
type ControllerContainer struct {
    mvcApp           *mvc.Application
    serviceContainer *service.ServiceContainer
}

func NewControllerContainer(mvcApp *mvc.Application, serviceContainer *service.ServiceContainer) *ControllerContainer {
    return &ControllerContainer{mvcApp: mvcApp, serviceContainer: serviceContainer}
}

func (c *ControllerContainer) InitMvc() {
    {{range $controller := .StructInfoes -}}
    c.mvcApp.Party({{.RequestMapping | unescaped}}).Configure(func(app *mvc.Application) {
        {{range $prop := .Properties -}}
        app.Register(c.serviceContainer.{{.Name}})
        {{end -}}
        app.Handle(new({{.Name}}))
    })
    
    {{end -}}
}

repository-container.tpl

package repository

import (
    "github.com/kataras/golog"
    "xorm.io/xorm"
)

// Count: {{len .StructInfoes}}
type RepositoryContainer struct{
    {{range $repository := .StructInfoes -}}
    {{.Name}} {{.Name}} 
    {{end -}}
}

func NewRepositoryContainer(engine *xorm.Engine, logger *golog.Logger) *RepositoryContainer{
    return &RepositoryContainer{
        {{range $repository := .StructInfoes -}}
        {{.Name}}: New{{.Name}}(engine, logger),
        {{end -}}
    }
}

service-container.tpl

package service

import (
    "github.com/kataras/golog"
    "xorm.io/xorm"
)

// Count: {{len .AllServices}}
type ServiceContainer struct{
    {{range $service := .CitedServices -}}
    {{.Name}} {{.Name}} 
    {{end -}}
    {{range $service := .Services -}}
    {{.Name}} {{.Name}} 
    {{end -}}
}

func NewServiceContainer({{.NewFuncString}}) *ServiceContainer{
    {{range $service := .CitedServices -}}
    {{.Name | littleCamelCase}} := New{{.Name}}({{.NewFuncString}})
    {{end -}}
    return &ServiceContainer{
        {{range $service := .CitedServices -}}
        {{.Name}}: {{.Name | littleCamelCase}},
        {{end -}}
        {{range $service := .Services -}}
        {{.Name}}: New{{.Name}}({{.NewFuncString}}),
        {{end -}}
    }
}
上一篇 下一篇

猜你喜欢

热点阅读