mirror of
https://github.com/go-home-admin/toolset.git
synced 2025-12-24 13:37:52 +08:00
bean + mysql orm
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/.idea
|
||||
17
Makefile
Normal file
17
Makefile
Normal file
@@ -0,0 +1,17 @@
|
||||
GOBIN := $(shell go env GOBIN)
|
||||
ATDIR := $(shell pwd)
|
||||
|
||||
# 只维护 protoc
|
||||
protoc:
|
||||
go run main.go make:protoc
|
||||
|
||||
make-route:
|
||||
go run main.go make:route
|
||||
|
||||
make-bean:
|
||||
go run main.go make:bean
|
||||
|
||||
# 调试启动
|
||||
dev:protoc make-route make-bean
|
||||
|
||||
|
||||
278
console/commands/bean.go
Normal file
278
console/commands/bean.go
Normal file
@@ -0,0 +1,278 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"github.com/ctfang/command"
|
||||
"github.com/go-home-admin/toolset/parser"
|
||||
"log"
|
||||
"os"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// BeanCommand @Bean
|
||||
type BeanCommand struct{}
|
||||
|
||||
func (BeanCommand) Configure() command.Configure {
|
||||
return command.Configure{
|
||||
Name: "make:bean",
|
||||
Description: "生成依赖注入的声明源代码文件",
|
||||
Input: command.Argument{
|
||||
Has: []command.ArgParam{
|
||||
{
|
||||
Name: "-f",
|
||||
Description: "强制更新",
|
||||
},
|
||||
},
|
||||
Option: []command.ArgParam{
|
||||
{
|
||||
Name: "skip",
|
||||
Description: "跳过目录",
|
||||
Default: "@root/generate",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (BeanCommand) Execute(input command.Input) {
|
||||
root := getRootPath()
|
||||
|
||||
//if input.GetHas("-f") == true {
|
||||
// for alias, _ := range parser.NewGoParserForDir(path) {
|
||||
// if
|
||||
// }
|
||||
//}
|
||||
|
||||
skip := make(map[string]bool)
|
||||
for _, s := range input.GetOptions("proto_path") {
|
||||
s = strings.Replace(s, "@root", root, 1)
|
||||
skip[s] = true
|
||||
}
|
||||
|
||||
for dir, fileParsers := range parser.NewGoParserForDir(root) {
|
||||
if _, ok := skip[dir]; ok {
|
||||
break
|
||||
}
|
||||
|
||||
bc := newBeanCache()
|
||||
for _, fileParser := range fileParsers {
|
||||
bc.name = fileParser.PackageName
|
||||
for _, goType := range fileParser.Types {
|
||||
for _, attr := range goType.Attrs {
|
||||
if attr.HasTag("inject") {
|
||||
for _, impStr := range fileParser.Imports {
|
||||
bc.imports[impStr] = impStr
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if goType.Doc.HasAnnotation("@Bean") {
|
||||
bc.structList = append(bc.structList, goType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
genBean(dir, bc)
|
||||
}
|
||||
}
|
||||
|
||||
type beanCache struct {
|
||||
name string
|
||||
imports map[string]string
|
||||
structList []parser.GoType
|
||||
}
|
||||
|
||||
func newBeanCache() beanCache {
|
||||
return beanCache{
|
||||
imports: map[string]string{
|
||||
"github.com/go-home-admin/home/bootstrap/services/app": "github.com/go-home-admin/home/bootstrap/services/app",
|
||||
},
|
||||
structList: make([]parser.GoType, 0),
|
||||
}
|
||||
}
|
||||
|
||||
func genBean(dir string, bc beanCache) {
|
||||
if len(bc.structList) == 0 {
|
||||
return
|
||||
}
|
||||
context := make([]string, 0)
|
||||
context = append(context, "package "+bc.name)
|
||||
|
||||
// import
|
||||
importAlias := parser.GenImportAlias(bc.imports)
|
||||
if len(importAlias) != 0 {
|
||||
context = append(context, "\nimport ("+getImportStr(bc, importAlias)+"\n)")
|
||||
}
|
||||
|
||||
// Single
|
||||
context = append(context, genSingle(bc))
|
||||
// Provider
|
||||
context = append(context, genProvider(bc, importAlias))
|
||||
str := "// gen for home toolset"
|
||||
for _, s2 := range context {
|
||||
str = str + "\n" + s2
|
||||
}
|
||||
|
||||
err := os.WriteFile(dir+"/z_inject_gen.go", []byte(str), 0766)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func genSingle(bc beanCache) string {
|
||||
str := ""
|
||||
allProviderStr := "\n\treturn []interface{}{"
|
||||
for _, goType := range bc.structList {
|
||||
if goType.Doc.HasAnnotation("@Bean") {
|
||||
str = str + "\nvar " + genSingleName(goType.Name) + " *" + goType.Name
|
||||
allProviderStr += "\n\t\t" + genInitializeNewStr(goType.Name) + "(),"
|
||||
}
|
||||
}
|
||||
// 返回全部的提供商
|
||||
str += "\n\nfunc GetAllProvider() []interface{} {" + allProviderStr + "\n\t}\n}"
|
||||
return str
|
||||
}
|
||||
|
||||
func genSingleName(s string) string {
|
||||
return "_" + s + "Single"
|
||||
}
|
||||
|
||||
func genProvider(bc beanCache, m map[string]string) string {
|
||||
str := ""
|
||||
for _, goType := range bc.structList {
|
||||
sVar := genSingleName(goType.Name)
|
||||
if goType.Doc.HasAnnotation("@Bean") {
|
||||
str = str + "\nfunc " + genInitializeNewStr(goType.Name) + "() *" + goType.Name + " {" +
|
||||
"\n\tif " + sVar + " == nil {" + // if _provider == nil {
|
||||
"\n\t\t" + sVar + " = " + "&" + goType.Name + "{}" // provider := provider{}
|
||||
|
||||
for attrName, attr := range goType.Attrs {
|
||||
pointer := ""
|
||||
if !attr.IsPointer() {
|
||||
pointer = "*"
|
||||
}
|
||||
|
||||
for tagName, _ := range attr.Tag {
|
||||
if tagName == "inject" {
|
||||
str = str + "\n\t\t" +
|
||||
sVar + "." + attrName + " = " + pointer + getInitializeNewFunName(attr, m)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constraint := m["github.com/go-home-admin/home/bootstrap/services/app"]
|
||||
str = str +
|
||||
"\n\t\t" + constraint + ".AfterProvider(" + sVar + ", \"" + goType.Doc.GetAlias() + "\")" +
|
||||
"\n\t}" +
|
||||
"\n\treturn " + sVar +
|
||||
"\n}"
|
||||
}
|
||||
}
|
||||
|
||||
return str
|
||||
}
|
||||
|
||||
func getInitializeNewFunName(k parser.GoTypeAttr, m map[string]string) string {
|
||||
alias := ""
|
||||
name := k.TypeName
|
||||
|
||||
if !k.InPackage {
|
||||
a := m[k.TypeImport]
|
||||
alias = a + "."
|
||||
arr := strings.Split(k.TypeName, ".")
|
||||
name = arr[len(arr)-1]
|
||||
} else if name[0:1] == "*" {
|
||||
name = name[1:]
|
||||
}
|
||||
tag := k.Tag["inject"]
|
||||
if tag.Count() < 2 {
|
||||
return alias + genInitializeNewStr(name) + "()"
|
||||
} else {
|
||||
beanAlias := tag.Get(0)
|
||||
beanValue := tag.Get(1)
|
||||
|
||||
constraint := m["github.com/go-home-admin/home/bootstrap/services/app"]
|
||||
|
||||
return constraint + ".GetBean(\"" + beanAlias + "\").(" + constraint + ".Bean)" +
|
||||
".GetBean(\"" + beanValue + "\").(*" + alias + name + ")"
|
||||
}
|
||||
}
|
||||
|
||||
// 控制对完函数名称
|
||||
func genInitializeNewStr(name string) string {
|
||||
if name[0:1] == "*" {
|
||||
name = name[1:]
|
||||
}
|
||||
|
||||
return "New" + name
|
||||
}
|
||||
|
||||
// 生成 import => alias
|
||||
func genImportAlias(m map[string]string) map[string]string {
|
||||
aliasMapImport := make(map[string]string)
|
||||
importMapAlias := make(map[string]string)
|
||||
for _, imp := range m {
|
||||
temp := strings.Split(imp, "/")
|
||||
key := temp[len(temp)-1]
|
||||
|
||||
if _, ok := aliasMapImport[key]; ok {
|
||||
for i := 1; i < 1000; i++ {
|
||||
newKey := key + strconv.Itoa(i)
|
||||
if _, ok2 := aliasMapImport[newKey]; !ok2 {
|
||||
key = newKey
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
aliasMapImport[key] = imp
|
||||
}
|
||||
for s, s2 := range aliasMapImport {
|
||||
importMapAlias[s2] = s
|
||||
}
|
||||
|
||||
return importMapAlias
|
||||
}
|
||||
|
||||
// m = import => alias
|
||||
func getImportStr(bc beanCache, m map[string]string) string {
|
||||
has := map[string]bool{
|
||||
"github.com/go-home-admin/home/bootstrap/services/app": true,
|
||||
}
|
||||
for _, goType := range bc.structList {
|
||||
if goType.Doc.HasAnnotation("@Bean") {
|
||||
for _, attr := range goType.Attrs {
|
||||
if !attr.InPackage {
|
||||
has[attr.TypeImport] = true
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
// 删除未使用的import
|
||||
nm := make(map[string]string)
|
||||
for s, vv := range m {
|
||||
if _, ok := has[s]; ok {
|
||||
nm[s] = vv
|
||||
}
|
||||
}
|
||||
|
||||
sk := sortMap(nm)
|
||||
got := ""
|
||||
for _, k := range sk {
|
||||
got += "\n\t" + nm[k] + " \"" + k + "\""
|
||||
}
|
||||
|
||||
return got
|
||||
}
|
||||
|
||||
func sortMap(m map[string]string) []string {
|
||||
var keys []string
|
||||
for k := range m {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
return keys
|
||||
}
|
||||
128
console/commands/orm.go
Normal file
128
console/commands/orm.go
Normal file
@@ -0,0 +1,128 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/ctfang/command"
|
||||
"github.com/go-home-admin/toolset/console/commands/orm"
|
||||
"github.com/joho/godotenv"
|
||||
"gopkg.in/yaml.v2"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// OrmCommand @Bean
|
||||
type OrmCommand struct{}
|
||||
|
||||
func (OrmCommand) Configure() command.Configure {
|
||||
return command.Configure{
|
||||
Name: "make:orm",
|
||||
Description: "根据配置文件连接数据库, 生成orm源码",
|
||||
Input: command.Argument{
|
||||
Option: []command.ArgParam{
|
||||
{
|
||||
Name: "config",
|
||||
Description: "配置文件",
|
||||
Default: "@root/config/database.yaml",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (OrmCommand) Execute(input command.Input) {
|
||||
root := getRootPath()
|
||||
file := input.GetOption("config")
|
||||
file = strings.Replace(file, "@root", root, 1)
|
||||
|
||||
err := godotenv.Load(root + "/.env")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fileContext, _ := os.ReadFile(file)
|
||||
fileContext = SetEnv(fileContext)
|
||||
m := make(map[string]interface{})
|
||||
err = yaml.Unmarshal(fileContext, &m)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
connections := m["connections"].(map[interface{}]interface{})
|
||||
for s, confT := range connections {
|
||||
conf := confT.(map[interface{}]interface{})
|
||||
driver := conf["driver"]
|
||||
out := root + "/app/entity/" + s.(string)
|
||||
switch driver {
|
||||
case "mysql":
|
||||
orm.GenMysql(s.(string), conf, out)
|
||||
case "postgresql":
|
||||
|
||||
}
|
||||
|
||||
cmd := exec.Command("go", []string{"fmt", out}...)
|
||||
var outBuffer bytes.Buffer
|
||||
cmd.Stdout = &outBuffer
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Dir = out
|
||||
_ = cmd.Run()
|
||||
}
|
||||
}
|
||||
|
||||
// SetEnv 对字符串内容进行替换环境变量
|
||||
func SetEnv(fileContext []byte) []byte {
|
||||
str := string(fileContext)
|
||||
arr := strings.Split(str, "\n")
|
||||
|
||||
for _, s := range arr {
|
||||
if strings.Index(s, " env(\"") != -1 {
|
||||
arr2 := strings.Split(s, ": ")
|
||||
if len(arr2) != 2 {
|
||||
continue
|
||||
}
|
||||
nS := arr2[1]
|
||||
st, et := GetBrackets(nS, '"', '"')
|
||||
key := nS[st : et+1]
|
||||
nS = nS[et+1:]
|
||||
st, et = GetBrackets(nS, '"', '"')
|
||||
val := nS[st : et+1]
|
||||
key = strings.Trim(key, "\"")
|
||||
val = strings.Trim(val, "\"")
|
||||
|
||||
envVal := os.Getenv(key)
|
||||
if envVal != "" {
|
||||
val = envVal
|
||||
}
|
||||
|
||||
str = strings.Replace(str, s, arr2[0]+": "+val, 1)
|
||||
}
|
||||
}
|
||||
|
||||
return []byte(str)
|
||||
}
|
||||
|
||||
func GetBrackets(str string, start, end int32) (int, int) {
|
||||
var startInt, endInt int
|
||||
|
||||
bCount := 0
|
||||
for i, w := range str {
|
||||
if bCount == 0 {
|
||||
if w == start {
|
||||
startInt = i
|
||||
bCount++
|
||||
}
|
||||
} else {
|
||||
switch w {
|
||||
case end:
|
||||
bCount--
|
||||
if bCount <= 0 {
|
||||
endInt = i
|
||||
return startInt, endInt
|
||||
}
|
||||
case start:
|
||||
bCount++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return startInt, endInt
|
||||
}
|
||||
343
console/commands/orm/mysql.go
Normal file
343
console/commands/orm/mysql.go
Normal file
@@ -0,0 +1,343 @@
|
||||
package orm
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"github.com/go-home-admin/home/bootstrap/services"
|
||||
"github.com/go-home-admin/toolset/parser"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// IsExist checks whether a file or directory exists.
|
||||
// It returns false when the file or directory does not exist.
|
||||
func IsExist(f string) bool {
|
||||
_, err := os.Stat(f)
|
||||
return err == nil || os.IsExist(err)
|
||||
}
|
||||
|
||||
func GenMysql(name string, conf map[interface{}]interface{}, out string) {
|
||||
if !IsExist(out) {
|
||||
os.MkdirAll(out, 0766)
|
||||
}
|
||||
|
||||
db := NewDb(conf)
|
||||
tableColumns := db.tableColumns()
|
||||
|
||||
// 计算import
|
||||
imports := getImports(tableColumns)
|
||||
for table, columns := range tableColumns {
|
||||
mysqlTableName := parser.StringToSnake(table)
|
||||
file := out + "/" + mysqlTableName
|
||||
|
||||
str := "package " + name
|
||||
str += "\nimport (" + imports[table] + "\n)"
|
||||
str += "\n" + genOrmStruct(table, columns)
|
||||
|
||||
baseFunStr := baseMysqlFuncStr
|
||||
for old, new := range map[string]string{
|
||||
"MysqlTableName": parser.StringToHump(table),
|
||||
} {
|
||||
baseFunStr = strings.ReplaceAll(baseFunStr, old, new)
|
||||
}
|
||||
|
||||
str += baseFunStr
|
||||
str += genFieldFunc(table, columns)
|
||||
str += genListFunc(table, columns)
|
||||
err := os.WriteFile(file+"_gen.go", []byte(str), 0766)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func genListFunc(table string, columns []tableColumn) string {
|
||||
TableName := parser.StringToHump(table)
|
||||
str := "\ntype " + TableName + "List []*" + TableName
|
||||
for _, column := range columns {
|
||||
// 索引,或者枚举字段
|
||||
if strInStr(column.Field, []string{"id", "code"}) {
|
||||
str += "\nfunc (l " + TableName + "List) Get" + column.ColumnName + "List() []" + column.GoaType + " {" +
|
||||
"\n\tgot := make([]" + column.GoaType + ", 0)\n\tfor _, val := range l {" +
|
||||
"\n\t\tgot = append(got, val." + column.ColumnName + ")" +
|
||||
"\n\t}" +
|
||||
"\n\treturn got" +
|
||||
"\n}"
|
||||
|
||||
str += "\nfunc (l " + TableName + "List) Get" + column.ColumnName + "Map() map[" + column.GoaType + "]*" + TableName + " {" +
|
||||
"\n\tgot := make(map[" + column.GoaType + "]*" + TableName + ")\n\tfor _, val := range l {" +
|
||||
"\n\t\tgot[val." + column.ColumnName + "] = val" +
|
||||
"\n\t}" +
|
||||
"\n\treturn got" +
|
||||
"\n}"
|
||||
}
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
func genFieldFunc(table string, columns []tableColumn) string {
|
||||
TableName := parser.StringToHump(table)
|
||||
|
||||
str := ""
|
||||
for _, column := range columns {
|
||||
// 等于函数
|
||||
str += "\nfunc (orm *Orm" + TableName + ") Where" + column.ColumnName + "(val " + column.GoaType + ") *Orm" + TableName + " {" +
|
||||
"\n\torm.db.Where(\"`" + column.Field + "` = ?\", val)" +
|
||||
"\n\treturn orm" +
|
||||
"\n}"
|
||||
|
||||
if column.PrimaryKey != "" {
|
||||
// if 主键, 生成In, > <
|
||||
str += "\nfunc (orm *Orm" + TableName + ") Where" + column.ColumnName + "In(val []" + column.GoaType + ") *Orm" + TableName + " {" +
|
||||
"\n\torm.db.Where(\"`" + column.Field + "` IN ?\", val)" +
|
||||
"\n\treturn orm" +
|
||||
"\n}"
|
||||
|
||||
str += "\nfunc (orm *Orm" + TableName + ") Where" + column.ColumnName + "Gt(val " + column.GoaType + ") *Orm" + TableName + " {" +
|
||||
"\n\torm.db.Where(\"`" + column.Field + "` > ?\", val)" +
|
||||
"\n\treturn orm" +
|
||||
"\n}"
|
||||
str += "\nfunc (orm *Orm" + TableName + ") Where" + column.ColumnName + "Gte(val " + column.GoaType + ") *Orm" + TableName + " {" +
|
||||
"\n\torm.db.Where(\"`" + column.Field + "` >= ?\", val)" +
|
||||
"\n\treturn orm" +
|
||||
"\n}"
|
||||
|
||||
str += "\nfunc (orm *Orm" + TableName + ") Where" + column.ColumnName + "Lt(val " + column.GoaType + ") *Orm" + TableName + " {" +
|
||||
"\n\torm.db.Where(\"`" + column.Field + "` < ?\", val)" +
|
||||
"\n\treturn orm" +
|
||||
"\n}"
|
||||
str += "\nfunc (orm *Orm" + TableName + ") Where" + column.ColumnName + "Lte(val " + column.GoaType + ") *Orm" + TableName + " {" +
|
||||
"\n\torm.db.Where(\"`" + column.Field + "` <= ?\", val)" +
|
||||
"\n\treturn orm" +
|
||||
"\n}"
|
||||
} else {
|
||||
// 索引,或者枚举字段
|
||||
if strInStr(column.Field, []string{"id", "code", "status", "state"}) {
|
||||
// else if 名称存在 id, code, status 生成in操作
|
||||
str += "\nfunc (orm *Orm" + TableName + ") Where" + column.ColumnName + "In(val []" + column.GoaType + ") *Orm" + TableName + " {" +
|
||||
"\n\torm.db.Where(\"`" + column.Field + "` IN ?\", val)" +
|
||||
"\n\treturn orm" +
|
||||
"\n}"
|
||||
|
||||
str += "\nfunc (orm *Orm" + TableName + ") Where" + column.ColumnName + "Ne(val " + column.GoaType + ") *Orm" + TableName + " {" +
|
||||
"\n\torm.db.Where(\"`" + column.Field + "` <> ?\", val)" +
|
||||
"\n\treturn orm" +
|
||||
"\n}"
|
||||
}
|
||||
// 时间字段
|
||||
if strInStr(column.Field, []string{"created", "updated", "time", "_at"}) || (column.GoaType == "database.Time") {
|
||||
str += "\nfunc (orm *Orm" + TableName + ") Where" + column.ColumnName + "Between(begin " + column.GoaType + ", end " + column.GoaType + ") *Orm" + TableName + " {" +
|
||||
"\n\torm.db.Where(\"`" + column.Field + "` BETWEEN ? AND ?\", begin, end)" +
|
||||
"\n\treturn orm" +
|
||||
"\n}"
|
||||
|
||||
str += "\nfunc (orm *Orm" + TableName + ") Where" + column.ColumnName + "Lte(val " + column.GoaType + ") *Orm" + TableName + " {" +
|
||||
"\n\torm.db.Where(\"`" + column.Field + "` <= ?\", val)" +
|
||||
"\n\treturn orm" +
|
||||
"\n}"
|
||||
|
||||
str += "\nfunc (orm *Orm" + TableName + ") Where" + column.ColumnName + "Gte(val " + column.GoaType + ") *Orm" + TableName + " {" +
|
||||
"\n\torm.db.Where(\"`" + column.Field + "` >= ?\", val)" +
|
||||
"\n\treturn orm" +
|
||||
"\n}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return str
|
||||
}
|
||||
|
||||
func strInStr(s string, in []string) bool {
|
||||
for _, sub := range in {
|
||||
if strings.Index(s, sub) != -1 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
//go:embed mysql.go.text
|
||||
var baseMysqlFuncStr string
|
||||
|
||||
// 字段类型引入
|
||||
var alias = map[string]string{
|
||||
"database": "github.com/go-home-admin/home/bootstrap/services/database",
|
||||
}
|
||||
|
||||
// 获得 table => map{alias => github.com/*}
|
||||
func getImports(tableColumns map[string][]tableColumn) map[string]string {
|
||||
got := make(map[string]string)
|
||||
for table, columns := range tableColumns {
|
||||
// 初始引入
|
||||
tm := map[string]string{
|
||||
"gorm.io/gorm": "gorm",
|
||||
"github.com/go-home-admin/home/bootstrap/providers": "providers",
|
||||
"github.com/sirupsen/logrus": "logrus",
|
||||
"database/sql": "sql",
|
||||
}
|
||||
for _, column := range columns {
|
||||
index := strings.Index(column.GoaType, ".")
|
||||
if index != -1 {
|
||||
as := column.GoaType[:index]
|
||||
importStr := alias[as]
|
||||
tm[importStr] = as
|
||||
}
|
||||
}
|
||||
got[table] = parser.GetImportStrForMap(tm)
|
||||
}
|
||||
|
||||
return got
|
||||
}
|
||||
|
||||
func genOrmStruct(table string, columns []tableColumn) string {
|
||||
TableName := parser.StringToHump(table)
|
||||
|
||||
str := `type {TableName} struct {`
|
||||
for _, column := range columns {
|
||||
str += "\n\t" + parser.StringToHump(column.Field) + " " + column.GoaType +
|
||||
"`" + genGormTag(column) + "` // " +
|
||||
strings.ReplaceAll(column.ColumnComment, "\n", " ")
|
||||
}
|
||||
|
||||
str = strings.ReplaceAll(str, "{TableName}", TableName)
|
||||
return "\n" + str + "\n}"
|
||||
}
|
||||
|
||||
func genGormTag(column tableColumn) string {
|
||||
var arr []string
|
||||
if column.PrimaryKey != "" {
|
||||
arr = append(arr, "primaryKey")
|
||||
}
|
||||
|
||||
arr = append(arr, "column:"+column.Field)
|
||||
str := ""
|
||||
for i := 0; i < len(arr)-1; i++ {
|
||||
str += arr[i] + ";"
|
||||
}
|
||||
str += "" + arr[len(arr)-1]
|
||||
return "gorm:\"" + str + "\""
|
||||
}
|
||||
|
||||
type DB struct {
|
||||
db *sql.DB
|
||||
}
|
||||
|
||||
func (d *DB) tableColumns() map[string][]tableColumn {
|
||||
var sqlStr = `SELECT
|
||||
COLUMN_NAME,
|
||||
DATA_TYPE,
|
||||
IS_NULLABLE,
|
||||
TABLE_NAME,
|
||||
COLUMN_COMMENT,
|
||||
COLUMN_KEY
|
||||
FROM
|
||||
information_schema.COLUMNS
|
||||
WHERE
|
||||
table_schema = DATABASE ()
|
||||
ORDER BY
|
||||
TABLE_NAME ASC,
|
||||
ORDINAL_POSITION ASC`
|
||||
|
||||
rows, err := d.db.Query(sqlStr)
|
||||
if err != nil {
|
||||
log.Println("Error reading table information: ", err.Error())
|
||||
return nil
|
||||
}
|
||||
|
||||
defer rows.Close()
|
||||
tableColumns := make(map[string][]tableColumn)
|
||||
for rows.Next() {
|
||||
col := tableColumn{}
|
||||
err = rows.Scan(&col.ColumnName, &col.MysqlType, &col.Nullable, &col.TableName, &col.ColumnComment, &col.PrimaryKey)
|
||||
if err != nil {
|
||||
log.Println(err.Error())
|
||||
return nil
|
||||
}
|
||||
|
||||
col.Field = col.ColumnName
|
||||
col.ColumnName = parser.StringToHump(col.ColumnName)
|
||||
col.GoaType = typeForMysqlToGo[col.MysqlType]
|
||||
|
||||
if _, ok := tableColumns[col.TableName]; !ok {
|
||||
tableColumns[col.TableName] = []tableColumn{}
|
||||
}
|
||||
tableColumns[col.TableName] = append(tableColumns[col.TableName], col)
|
||||
}
|
||||
return tableColumns
|
||||
}
|
||||
|
||||
type tableColumn struct {
|
||||
PrimaryKey string
|
||||
ColumnName string
|
||||
GoaType string
|
||||
MysqlType string
|
||||
Nullable string
|
||||
TableName string
|
||||
ColumnComment string
|
||||
Field string
|
||||
}
|
||||
|
||||
var typeForMysqlToGo = map[string]string{
|
||||
"int": "int64",
|
||||
"integer": "int64",
|
||||
"tinyint": "int32",
|
||||
"smallint": "int32",
|
||||
"mediumint": "int64",
|
||||
"bigint": "int64",
|
||||
"int unsigned": "uint64",
|
||||
"integer unsigned": "uint64",
|
||||
"tinyint unsigned": "uint32",
|
||||
"smallint unsigned": "uint64",
|
||||
"mediumint unsigned": "uint64",
|
||||
"bigint unsigned": "uint64",
|
||||
"bit": "int64",
|
||||
"bool": "bool",
|
||||
"enum": "string",
|
||||
"set": "string",
|
||||
"varchar": "string",
|
||||
"char": "string",
|
||||
"tinytext": "string",
|
||||
"mediumtext": "string",
|
||||
"text": "string",
|
||||
"longtext": "string",
|
||||
"blob": "string",
|
||||
"tinyblob": "string",
|
||||
"mediumblob": "string",
|
||||
"longblob": "string",
|
||||
"date": "database.Time", // time.Time or string
|
||||
"datetime": "database.Time", // time.Time or string
|
||||
"timestamp": "database.Time", // time.Time or string
|
||||
"time": "database.Time", // time.Time or string
|
||||
"float": "float64",
|
||||
"double": "float64",
|
||||
"decimal": "float64",
|
||||
"binary": "string",
|
||||
"varbinary": "string",
|
||||
"json": "database.Json",
|
||||
}
|
||||
|
||||
func NewDb(conf map[interface{}]interface{}) *DB {
|
||||
config := services.NewConfig(conf)
|
||||
db, err := sql.Open("mysql", fmt.Sprintf(
|
||||
"%s:%s@tcp(%s)/%s",
|
||||
config.GetString("username", "root"),
|
||||
config.GetString("password", "123456"),
|
||||
config.GetString("host", "localhost:"+config.GetString("port", "3306")),
|
||||
config.GetString("database", "demo"),
|
||||
))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// See "Important settings" section.
|
||||
db.SetConnMaxLifetime(time.Minute * 3)
|
||||
db.SetMaxOpenConns(10)
|
||||
db.SetMaxIdleConns(10)
|
||||
|
||||
return &DB{
|
||||
db: db,
|
||||
}
|
||||
}
|
||||
212
console/commands/orm/mysql.go.text
Normal file
212
console/commands/orm/mysql.go.text
Normal file
@@ -0,0 +1,212 @@
|
||||
|
||||
func (receiver *MysqlTableName) TableName() string {
|
||||
return "action_logs"
|
||||
}
|
||||
|
||||
type OrmMysqlTableName struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
func NewOrmMysqlTableName() *OrmMysqlTableName {
|
||||
orm := &OrmMysqlTableName{}
|
||||
orm.db = providers.NewMysqlProvider().GetBean("mysql").(*gorm.DB)
|
||||
return orm
|
||||
}
|
||||
|
||||
func (orm *OrmMysqlTableName) GetDB() *gorm.DB {
|
||||
return orm.db
|
||||
}
|
||||
|
||||
// Create insert the value into database
|
||||
func (orm *OrmMysqlTableName) Create(value interface{}) *gorm.DB {
|
||||
return orm.db.Create(value)
|
||||
}
|
||||
|
||||
// CreateInBatches insert the value in batches into database
|
||||
func (orm *OrmMysqlTableName) CreateInBatches(value interface{}, batchSize int) *gorm.DB {
|
||||
return orm.db.CreateInBatches(value, batchSize)
|
||||
}
|
||||
|
||||
// Save update value in database, if the value doesn't have primary key, will insert it
|
||||
func (orm *OrmMysqlTableName) Save(value interface{}) *gorm.DB {
|
||||
return orm.db.Save(value)
|
||||
}
|
||||
|
||||
func (orm *OrmMysqlTableName) Row() *sql.Row {
|
||||
return orm.db.Row()
|
||||
}
|
||||
|
||||
func (orm *OrmMysqlTableName) Rows() (*sql.Rows, error) {
|
||||
return orm.db.Rows()
|
||||
}
|
||||
|
||||
// Scan scan value to a struct
|
||||
func (orm *OrmMysqlTableName) Scan(dest interface{}) *gorm.DB {
|
||||
return orm.db.Scan(dest)
|
||||
}
|
||||
|
||||
func (orm *OrmMysqlTableName) ScanRows(rows *sql.Rows, dest interface{}) error {
|
||||
return orm.db.ScanRows(rows, dest)
|
||||
}
|
||||
|
||||
// Connection use a db conn to execute Multiple commands,this conn will put conn pool after it is executed.
|
||||
func (orm *OrmMysqlTableName) Connection(fc func(tx *gorm.DB) error) (err error) {
|
||||
return orm.db.Connection(fc)
|
||||
}
|
||||
|
||||
// Transaction start a transaction as a block, return error will rollback, otherwise to commit.
|
||||
func (orm *OrmMysqlTableName) Transaction(fc func(tx *gorm.DB) error, opts ...*sql.TxOptions) (err error) {
|
||||
return orm.db.Transaction(fc, opts...)
|
||||
}
|
||||
|
||||
// Begin begins a transaction
|
||||
func (orm *OrmMysqlTableName) Begin(opts ...*sql.TxOptions) *gorm.DB {
|
||||
return orm.db.Begin(opts...)
|
||||
}
|
||||
|
||||
// Commit commit a transaction
|
||||
func (orm *OrmMysqlTableName) Commit() *gorm.DB {
|
||||
return orm.db.Commit()
|
||||
}
|
||||
|
||||
// Rollback rollback a transaction
|
||||
func (orm *OrmMysqlTableName) Rollback() *gorm.DB {
|
||||
return orm.db.Rollback()
|
||||
}
|
||||
|
||||
func (orm *OrmMysqlTableName) SavePoint(name string) *gorm.DB {
|
||||
return orm.db.SavePoint(name)
|
||||
}
|
||||
|
||||
func (orm *OrmMysqlTableName) RollbackTo(name string) *gorm.DB {
|
||||
return orm.db.RollbackTo(name)
|
||||
}
|
||||
|
||||
// Exec execute raw sql
|
||||
func (orm *OrmMysqlTableName) Exec(sql string, values ...interface{}) *gorm.DB {
|
||||
return orm.db.Exec(sql, values...)
|
||||
}
|
||||
|
||||
// ------------ 以下是单表独有的函数, 便捷字段条件, Laravel风格操作 ---------
|
||||
|
||||
func (orm *OrmMysqlTableName) Insert(row *MysqlTableName) *gorm.DB {
|
||||
return orm.db.Create(row)
|
||||
}
|
||||
|
||||
func (orm *OrmMysqlTableName) Inserts(rows []*MysqlTableName) *gorm.DB {
|
||||
return orm.db.Create(rows)
|
||||
}
|
||||
|
||||
func (orm *OrmMysqlTableName) Limit(limit int) *OrmMysqlTableName {
|
||||
orm.db.Limit(limit)
|
||||
return orm
|
||||
}
|
||||
|
||||
func (orm *OrmMysqlTableName) Offset(offset int) *OrmMysqlTableName {
|
||||
orm.db.Offset(offset)
|
||||
return orm
|
||||
}
|
||||
|
||||
func (orm *OrmMysqlTableName) Get() (MysqlTableNameList, int64) {
|
||||
return orm.Find()
|
||||
}
|
||||
|
||||
// Pluck used to query single column from a model as a map
|
||||
// var ages []int64
|
||||
// db.Model(&users).Pluck("age", &ages)
|
||||
func (orm *OrmMysqlTableName) Pluck(column string, dest interface{}) *gorm.DB {
|
||||
return orm.db.Model(&MysqlTableName{}).Pluck(column, dest)
|
||||
}
|
||||
|
||||
// Delete delete value match given conditions, if the value has primary key, then will including the primary key as condition
|
||||
func (orm *OrmMysqlTableName) Delete(value interface{}, conds ...interface{}) *gorm.DB {
|
||||
return orm.db.Model(&MysqlTableName{}).Delete(value, conds...)
|
||||
}
|
||||
|
||||
func (orm *OrmMysqlTableName) Count() int64 {
|
||||
var count int64
|
||||
orm.db.Model(&MysqlTableName{}).Count(&count)
|
||||
return count
|
||||
}
|
||||
|
||||
// First 检索单个对象
|
||||
func (orm *OrmMysqlTableName) First(conds ...interface{}) (*MysqlTableName, int64) {
|
||||
dest := &MysqlTableName{}
|
||||
db := orm.db.Limit(1).Find(dest, conds...)
|
||||
return dest, db.RowsAffected
|
||||
}
|
||||
|
||||
// Take return a record that match given conditions, the order will depend on the database implementation
|
||||
func (orm *OrmMysqlTableName) Take(conds ...interface{}) (*MysqlTableName, int64) {
|
||||
dest := &MysqlTableName{}
|
||||
db := orm.db.Take(dest, conds...)
|
||||
return dest, db.RowsAffected
|
||||
}
|
||||
|
||||
// Last find last record that match given conditions, order by primary key
|
||||
func (orm *OrmMysqlTableName) Last(conds ...interface{}) (*MysqlTableName, int64) {
|
||||
dest := &MysqlTableName{}
|
||||
db := orm.db.Last(dest, conds...)
|
||||
return dest, db.RowsAffected
|
||||
}
|
||||
|
||||
func (orm *OrmMysqlTableName) Find(conds ...interface{}) (MysqlTableNameList, int64) {
|
||||
list := make([]*MysqlTableName, 0)
|
||||
tx := orm.db.Model(&MysqlTableName{}).Find(list, conds...)
|
||||
if tx.Error != nil {
|
||||
logrus.Error(tx.Error)
|
||||
}
|
||||
return list, tx.RowsAffected
|
||||
}
|
||||
|
||||
// FindInBatches find records in batches
|
||||
func (orm *OrmMysqlTableName) FindInBatches(dest interface{}, batchSize int, fc func(tx *gorm.DB, batch int) error) *gorm.DB {
|
||||
return orm.db.FindInBatches(dest, batchSize, fc)
|
||||
}
|
||||
|
||||
// FirstOrInit gets the first matched record or initialize a new instance with given conditions (only works with struct or map conditions)
|
||||
func (orm *OrmMysqlTableName) FirstOrInit(dest *MysqlTableName, conds ...interface{}) (*MysqlTableName, *gorm.DB) {
|
||||
return dest, orm.db.FirstOrInit(dest, conds...)
|
||||
}
|
||||
|
||||
// FirstOrCreate gets the first matched record or create a new one with given conditions (only works with struct, map conditions)
|
||||
func (orm *OrmMysqlTableName) FirstOrCreate(dest interface{}, conds ...interface{}) *gorm.DB {
|
||||
return orm.db.FirstOrCreate(dest, conds...)
|
||||
}
|
||||
|
||||
// Update update attributes with callbacks, refer: https://gorm.io/docs/update.html#Update-Changed-Fields
|
||||
func (orm *OrmMysqlTableName) Update(column string, value interface{}) *gorm.DB {
|
||||
return orm.db.Model(&MysqlTableName{}).Update(column, value)
|
||||
}
|
||||
|
||||
// Updates update attributes with callbacks, refer: https://gorm.io/docs/update.html#Update-Changed-Fields
|
||||
func (orm *OrmMysqlTableName) Updates(values interface{}) *gorm.DB {
|
||||
return orm.db.Model(&MysqlTableName{}).Updates(values)
|
||||
}
|
||||
|
||||
func (orm *OrmMysqlTableName) UpdateColumn(column string, value interface{}) *gorm.DB {
|
||||
return orm.db.Model(&MysqlTableName{}).UpdateColumn(column, value)
|
||||
}
|
||||
|
||||
func (orm *OrmMysqlTableName) UpdateColumns(values interface{}) *gorm.DB {
|
||||
return orm.db.Model(&MysqlTableName{}).UpdateColumns(values)
|
||||
}
|
||||
|
||||
func (orm *OrmMysqlTableName) Where(query interface{}, args ...interface{}) *OrmMysqlTableName {
|
||||
orm.db.Where(query, args...)
|
||||
return orm
|
||||
}
|
||||
|
||||
func (orm *OrmMysqlTableName) And(fuc func(orm *OrmMysqlTableName)) *OrmMysqlTableName {
|
||||
ormAnd := NewOrmMysqlTableName()
|
||||
fuc(ormAnd)
|
||||
orm.db.Where(ormAnd.db)
|
||||
return orm
|
||||
}
|
||||
|
||||
func (orm *OrmMysqlTableName) Or(fuc func(orm *OrmMysqlTableName)) *OrmMysqlTableName {
|
||||
ormOr := NewOrmMysqlTableName()
|
||||
fuc(ormOr)
|
||||
orm.db.Or(ormOr.db)
|
||||
return orm
|
||||
}
|
||||
291
console/commands/protoc.go
Normal file
291
console/commands/protoc.go
Normal file
@@ -0,0 +1,291 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/ctfang/command"
|
||||
"github.com/go-home-admin/toolset/parser"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ProtocCommand @Bean
|
||||
type ProtocCommand struct{}
|
||||
|
||||
func (ProtocCommand) Configure() command.Configure {
|
||||
return command.Configure{
|
||||
Name: "make:protoc",
|
||||
Description: "组装和执行protoc命令",
|
||||
Input: command.Argument{
|
||||
Option: []command.ArgParam{
|
||||
{
|
||||
Name: "proto",
|
||||
Description: "proto文件存放的目录",
|
||||
Default: "@root/protobuf",
|
||||
},
|
||||
{
|
||||
Name: "proto_path",
|
||||
Description: "protoc后面拼接的proto_path, 可以传入多个",
|
||||
Default: "@root/protobuf/common/http",
|
||||
},
|
||||
{
|
||||
Name: "go_out",
|
||||
Description: "生成文件到指定目录",
|
||||
Default: "@root/generate/proto",
|
||||
},
|
||||
{
|
||||
Name: "show",
|
||||
Description: "是否打印protoc命令",
|
||||
Default: "false",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
var show = false
|
||||
|
||||
func (ProtocCommand) Execute(input command.Input) {
|
||||
show = input.GetOption("false") == "true"
|
||||
root := getRootPath()
|
||||
_, err := exec.LookPath("protoc")
|
||||
if err != nil {
|
||||
log.Printf("'protoc' 未安装; brew install protobuf")
|
||||
return
|
||||
}
|
||||
out := input.GetOption("go_out")
|
||||
out = strings.Replace(out, "@root", root, 1)
|
||||
outTemp, _ := filepath.Abs(out + "/../temp")
|
||||
_ = os.MkdirAll(outTemp, 0766)
|
||||
|
||||
path := input.GetOption("proto")
|
||||
path = strings.Replace(path, "@root", root, 1)
|
||||
|
||||
pps := make([]string, 0)
|
||||
for _, s := range input.GetOptions("proto_path") {
|
||||
s = strings.Replace(s, "@root", root, 1)
|
||||
pps = append(pps, "--proto_path="+s)
|
||||
}
|
||||
// path/*.proto 不是protoc命令提供的, 如果这里执行需要每一个文件一个命令
|
||||
for _, dir := range parser.GetChildrenDir(path) {
|
||||
for _, info := range dir.GetFiles(".proto") {
|
||||
cods := []string{"--proto_path=" + dir.Path}
|
||||
cods = append(cods, pps...)
|
||||
cods = append(cods, "--go_out="+outTemp)
|
||||
cods = append(cods, info.Path)
|
||||
|
||||
Cmd("protoc", cods)
|
||||
}
|
||||
}
|
||||
|
||||
// 生成后, 从temp目录拷贝到out
|
||||
_ = os.RemoveAll(out)
|
||||
rootAlias := strings.Replace(out, root+"/", "", 1)
|
||||
module := getModModule()
|
||||
|
||||
for _, dir := range parser.GetChildrenDir(outTemp) {
|
||||
dir2 := strings.Replace(dir.Path, outTemp+"/", "", 1)
|
||||
dir3 := strings.Replace(dir2, module+"/", "", 1)
|
||||
if dir2 == dir3 {
|
||||
continue
|
||||
}
|
||||
|
||||
if dir3 == rootAlias {
|
||||
_ = os.Rename(dir.Path, out)
|
||||
_ = os.RemoveAll(outTemp)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 基础proto生成后, 生成Tag
|
||||
genProtoTag(out)
|
||||
}
|
||||
|
||||
func genProtoTag(out string) {
|
||||
byteLineDoc := []byte("//")
|
||||
bytePackage := []byte("package")
|
||||
byteT := []byte("\t")
|
||||
byteProtobuf := []byte("`protobuf:\"")
|
||||
byteProtobufOneof := []byte("`protobuf_oneof:\"")
|
||||
byteEq := []byte("=")
|
||||
|
||||
for _, dir := range parser.GetChildrenDir(out) {
|
||||
for _, file := range dir.GetFiles(".go") {
|
||||
fd, err := os.Open(file.Path)
|
||||
defer fd.Close()
|
||||
if err != nil {
|
||||
fmt.Println("read error:", err)
|
||||
}
|
||||
|
||||
fileNewStr := make([]byte, 0)
|
||||
buff := bufio.NewReader(fd)
|
||||
packageStart := false
|
||||
// 初始化文件Tag
|
||||
fileTags := make([]tag, 0)
|
||||
fileMapTags := map[string]tag{
|
||||
"form": {name: "Tag", key: "form", val: "{name}"},
|
||||
}
|
||||
for _, t := range fileMapTags {
|
||||
fileTags = append(fileTags, t)
|
||||
}
|
||||
// 开始替换文件内容
|
||||
lineTags := make([]tag, 0)
|
||||
lineMapTags := make(map[string]tag)
|
||||
for {
|
||||
data, _, eof := buff.ReadLine()
|
||||
if eof == io.EOF {
|
||||
break
|
||||
}
|
||||
newStr := append([]byte("\n"), data...)
|
||||
if bytes.Index(data, byteLineDoc) == -1 {
|
||||
if !packageStart {
|
||||
if bytes.Index(data, bytePackage) == 0 {
|
||||
packageStart = true
|
||||
fileTags = append(fileTags, lineTags...)
|
||||
for s, t := range lineMapTags {
|
||||
fileMapTags[s] = t
|
||||
}
|
||||
}
|
||||
} else if bytes.HasPrefix(data, byteT) {
|
||||
var tagValue []byte
|
||||
end := 0
|
||||
if start := bytes.Index(data, byteProtobuf); start != -1 {
|
||||
nData := data[start:]
|
||||
begin := bytes.Index(nData, []byte("name="))
|
||||
end = bytes.Index(nData[begin:], []byte(","))
|
||||
nData = nData[begin : begin+end]
|
||||
tagValue = nData[bytes.Index(nData, byteEq)+1:]
|
||||
end = bytes.Index(data[start+begin:], []byte("`")) + start + begin
|
||||
} else if start := bytes.Index(data, byteProtobufOneof); start != -1 {
|
||||
nData := data[start:]
|
||||
begin := bytes.Index(nData, []byte("\"")) + 1
|
||||
end = bytes.Index(nData[begin:], []byte("\""))
|
||||
nData = nData[begin : begin+end]
|
||||
tagValue = nData[bytes.Index(nData, byteEq)+1:]
|
||||
end = bytes.Index(data[start+begin:], []byte("`")) + start + begin
|
||||
}
|
||||
if len(tagValue) != 0 {
|
||||
for s, t := range fileMapTags {
|
||||
if _, ok := lineMapTags[s]; !ok {
|
||||
lineMapTags[s] = t
|
||||
lineTags = append(lineTags, t)
|
||||
}
|
||||
}
|
||||
endStr := data[end:]
|
||||
newStr = append([]byte("\n"), data[:end]...)
|
||||
for _, lineTag := range lineTags {
|
||||
switch lineTag.key {
|
||||
case "json":
|
||||
newStr = []byte(strings.ReplaceAll(string(newStr), "json:\""+string(tagValue)+",omitempty\"", ""))
|
||||
newStr = append(newStr, []byte(lineTag.key+":\""+
|
||||
strings.ReplaceAll(lineTag.val, "{name}", string(tagValue))+
|
||||
"\"")...)
|
||||
default:
|
||||
newStr = append(newStr, []byte(" "+lineTag.key+":\""+
|
||||
strings.ReplaceAll(lineTag.val, "{name}", string(tagValue))+
|
||||
"\"")...)
|
||||
}
|
||||
}
|
||||
newStr = append(newStr, endStr...)
|
||||
}
|
||||
}
|
||||
|
||||
lineTags = make([]tag, 0)
|
||||
lineMapTags = make(map[string]tag)
|
||||
} else {
|
||||
str := strings.Trim(string(data), "")
|
||||
tags, mTags := getDocTag(str)
|
||||
lineTags = append(lineTags, tags...)
|
||||
for s, t := range mTags {
|
||||
lineMapTags[s] = t
|
||||
}
|
||||
}
|
||||
|
||||
fileNewStr = append(fileNewStr, newStr...)
|
||||
}
|
||||
|
||||
defer os.WriteFile(file.Path, fileNewStr, 0760)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type tag struct {
|
||||
name string
|
||||
|
||||
key string
|
||||
val string
|
||||
}
|
||||
|
||||
func getDocTag(doc string) ([]tag, map[string]tag) {
|
||||
got := make([]tag, 0)
|
||||
mapt := make(map[string]tag)
|
||||
|
||||
arr := parser.GetWords(strings.ReplaceAll(doc, "//", " "))
|
||||
arrLet := len(arr)
|
||||
for i := 0; i < arrLet; i++ {
|
||||
w := arr[i]
|
||||
if w.Str == "@" {
|
||||
if arrLet < (i + 1) {
|
||||
continue
|
||||
}
|
||||
tag := tag{}
|
||||
tag.name = arr[i+1].Str
|
||||
if tag.name != "Tag" {
|
||||
continue
|
||||
}
|
||||
nl := arr[i+1:]
|
||||
i = i + 1
|
||||
st, et, has := parser.GetBracketsOrLn(nl, "(", ")")
|
||||
if !has {
|
||||
continue
|
||||
}
|
||||
i = i + et
|
||||
nl = nl[st+1 : et]
|
||||
for _, word := range nl {
|
||||
if word.Ty == 0 {
|
||||
if tag.key == "" {
|
||||
tag.key = word.Str[1 : len(word.Str)-1]
|
||||
tag.val = "{name}"
|
||||
} else {
|
||||
tag.val = word.Str[1 : len(word.Str)-1]
|
||||
}
|
||||
}
|
||||
}
|
||||
if tag.key != "" {
|
||||
got = append(got, tag)
|
||||
mapt[tag.key] = tag
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return got, mapt
|
||||
}
|
||||
|
||||
func Cmd(commandName string, params []string) {
|
||||
// 打印真实命令
|
||||
if show {
|
||||
PrintCmd(commandName, params)
|
||||
}
|
||||
|
||||
cmd := exec.Command(commandName, params...)
|
||||
var out bytes.Buffer
|
||||
cmd.Stdout = &out
|
||||
cmd.Stderr = os.Stderr
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
}
|
||||
|
||||
func PrintCmd(commandName string, params []string) {
|
||||
str := "\n" + commandName + " "
|
||||
for _, param := range params {
|
||||
str += param + " "
|
||||
}
|
||||
fmt.Print(str + "\n")
|
||||
}
|
||||
279
console/commands/route.go
Normal file
279
console/commands/route.go
Normal file
@@ -0,0 +1,279 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/ctfang/command"
|
||||
"github.com/go-home-admin/toolset/parser"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// RouteCommand @Bean
|
||||
type RouteCommand struct{}
|
||||
|
||||
func (RouteCommand) Configure() command.Configure {
|
||||
return command.Configure{
|
||||
Name: "make:route",
|
||||
Description: "根据protoc文件定义, 生成路由信息和控制器文件",
|
||||
Input: command.Argument{
|
||||
Option: []command.ArgParam{
|
||||
{
|
||||
Name: "path",
|
||||
Description: "只解析指定目录",
|
||||
Default: "@root/protobuf",
|
||||
},
|
||||
{
|
||||
Name: "out_route",
|
||||
Description: "生成文件到指定目录",
|
||||
Default: "@root/routes",
|
||||
},
|
||||
{
|
||||
Name: "out_actions",
|
||||
Description: "生成文件到指定目录",
|
||||
Default: "@root/app/http",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (RouteCommand) Execute(input command.Input) {
|
||||
root := getRootPath()
|
||||
module := getModModule()
|
||||
out := input.GetOption("out_route")
|
||||
out = strings.Replace(out, "@root", root, 1)
|
||||
|
||||
outHttp := input.GetOption("out_actions")
|
||||
outHttp = strings.Replace(outHttp, "@root", root, 1)
|
||||
|
||||
path := input.GetOption("path")
|
||||
path = strings.Replace(path, "@root", root, 1)
|
||||
|
||||
agl := map[string]*ApiGroups{}
|
||||
|
||||
for _, parsers := range parser.NewProtocParserForDir(path) {
|
||||
for _, fileParser := range parsers {
|
||||
for _, service := range fileParser.Services {
|
||||
group := ""
|
||||
|
||||
for _, option := range service.Opt {
|
||||
if option.Key == "http.RouteGroup" {
|
||||
group = option.Val
|
||||
if _, ok := agl[group]; !ok {
|
||||
agl[group] = &ApiGroups{
|
||||
name: group,
|
||||
imports: map[string]string{
|
||||
"home_api_1": "github.com/go-home-admin/home/bootstrap/http/api",
|
||||
"home_gin_1": "github.com/gin-gonic/gin",
|
||||
},
|
||||
controllers: make([]Controller, 0),
|
||||
servers: make([]parser.Service, 0),
|
||||
}
|
||||
}
|
||||
genController(service, outHttp)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if group != "" {
|
||||
g := agl[group]
|
||||
imports := module + "/app/http/" + fileParser.PackageName + "/" + parser.StringToSnake(service.Name)
|
||||
g.imports[imports] = imports
|
||||
|
||||
g.controllers = append(g.controllers, Controller{
|
||||
name: service.Name,
|
||||
alias: imports,
|
||||
})
|
||||
|
||||
g.servers = append(g.servers, service)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, g := range agl {
|
||||
genRoute(g, out)
|
||||
}
|
||||
cmd := exec.Command("go", []string{"fmt", out}...)
|
||||
var outBuffer bytes.Buffer
|
||||
cmd.Stdout = &outBuffer
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Dir = out
|
||||
_ = cmd.Run()
|
||||
}
|
||||
|
||||
func genController(server parser.Service, out string) {
|
||||
page := server.Protoc.PackageName
|
||||
out += "/" + page + "/" + parser.StringToSnake(server.Name)
|
||||
|
||||
if !parser.DirIsExist(out) {
|
||||
_ = os.MkdirAll(out, 0760)
|
||||
}
|
||||
|
||||
conStr := `package {package}
|
||||
|
||||
// Controller @Bean
|
||||
type Controller struct {
|
||||
}`
|
||||
conStr = strings.ReplaceAll(conStr, "{package}", parser.StringToSnake(server.Name))
|
||||
err := os.WriteFile(out+"/controller.go", []byte(conStr), 0760)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
methodStr := `package {package}
|
||||
|
||||
import ({import})
|
||||
|
||||
// {action} {doc}
|
||||
func (receiver *Controller) {action}(req *{controllerAlias}.{param}, ctx *gin.Context) (*{controllerAlias}.{return}, error) {
|
||||
// TODO 这里写业务
|
||||
return &{controllerAlias}.{return}{}, nil
|
||||
}
|
||||
|
||||
// GinHandle{action} gin原始路由处理
|
||||
// http.{method}({url})
|
||||
func (receiver *Controller) GinHandle{action}(ctx *gin.Context) {
|
||||
req := &{controllerAlias}.{param}{}
|
||||
err := ctx.ShouldBind(req)
|
||||
|
||||
if err != nil {
|
||||
{providers}.ErrorRequest(ctx, err)
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := receiver.{action}(req, ctx)
|
||||
if err != nil {
|
||||
{providers}.ErrorResponse(ctx, err)
|
||||
return
|
||||
}
|
||||
|
||||
{providers}.SuccessResponse(ctx, resp)
|
||||
}
|
||||
`
|
||||
gin := "github.com/gin-gonic/gin"
|
||||
providers := "github.com/go-home-admin/home/app/providers"
|
||||
imports := map[string]string{gin: gin, providers: providers}
|
||||
goMod := getModModule()
|
||||
|
||||
for rName, rpc := range server.Rpc {
|
||||
for _, option := range rpc.Opt {
|
||||
if strings.Index(option.Key, "http.") == 0 {
|
||||
serPage := goMod + "/generate/proto/" + server.Protoc.PackageName
|
||||
imports[serPage] = serPage
|
||||
importsStr := ""
|
||||
m := genImportAlias(imports)
|
||||
sk := sortMap(m)
|
||||
for _, s := range sk {
|
||||
importsStr += "\n\t" + m[s] + " \"" + s + "\""
|
||||
}
|
||||
|
||||
controllerAlias := m[serPage]
|
||||
|
||||
str := methodStr
|
||||
actionName := parser.StringToHump(rName)
|
||||
|
||||
i := strings.Index(option.Key, ".")
|
||||
method := option.Key[i+1:]
|
||||
url := option.Val
|
||||
|
||||
for s, O := range map[string]string{
|
||||
"{package}": parser.StringToSnake(server.Name),
|
||||
"{import}": importsStr + "\n",
|
||||
"{doc}": rpc.Doc,
|
||||
"{method}": method,
|
||||
"{url}": url,
|
||||
"{action}": actionName,
|
||||
"{controllerAlias}": controllerAlias,
|
||||
"{providers}": m[providers],
|
||||
"{param}": rpc.Param,
|
||||
"{return}": rpc.Return,
|
||||
} {
|
||||
str = strings.ReplaceAll(str, s, O)
|
||||
}
|
||||
|
||||
err = os.WriteFile(out+"/"+parser.StringToSnake(actionName)+"_action.go", []byte(str), 0766)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func genRoute(g *ApiGroups, out string) {
|
||||
context := make([]string, 0)
|
||||
context = append(context, "package routes")
|
||||
|
||||
// import
|
||||
importAlias := genImportAlias(g.imports)
|
||||
if len(importAlias) != 0 {
|
||||
context = append(context, "\nimport ("+parser.GetImportStrForMap(importAlias)+"\n)")
|
||||
}
|
||||
// Routes struct
|
||||
context = append(context, genRoutesStruct(g, importAlias))
|
||||
// Routes struct func GetRoutes
|
||||
context = append(context, genRoutesFunc(g, importAlias))
|
||||
|
||||
str := "// gen for home toolset"
|
||||
for _, s2 := range context {
|
||||
str = str + "\n" + s2
|
||||
}
|
||||
err := os.WriteFile(out+"/"+parser.StringToSnake(g.name)+"_gen.go", []byte(str), 0766)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func genRoutesFunc(g *ApiGroups, m map[string]string) string {
|
||||
homeGin := m["github.com/gin-gonic/gin"]
|
||||
homeApi := m["github.com/go-home-admin/home/bootstrap/http/api"]
|
||||
|
||||
str := "func (c *" + parser.StringToHump(g.name) + "Routes) GetGroup() string {" +
|
||||
"\n\treturn \"" + g.name + "\"" +
|
||||
"\n}"
|
||||
|
||||
str += "\nfunc (c *" + parser.StringToHump(g.name) + "Routes) GetRoutes() map[*" + homeApi + ".Config]func(c *" + homeGin + ".Context) {" +
|
||||
"\n\treturn map[*" + homeApi + ".Config]func(c *" + homeGin + ".Context){"
|
||||
|
||||
for _, server := range g.servers {
|
||||
for rName, rpc := range server.Rpc {
|
||||
for _, option := range rpc.Opt {
|
||||
if strings.Index(option.Key, "http.") == 0 {
|
||||
i := strings.Index(option.Key, ".")
|
||||
method := option.Key[i+1:]
|
||||
str += "\n\t\t" + homeApi + "." + method + "(\"" + option.Val + "\"):" +
|
||||
"c." + parser.StringToSnake(server.Name) + ".GinHandle" + parser.StringToHump(rName) + ","
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return str + "\n\t}\n}"
|
||||
}
|
||||
|
||||
func genRoutesStruct(g *ApiGroups, m map[string]string) string {
|
||||
str := "\n// @Bean" +
|
||||
"\ntype " + parser.StringToHump(g.name) + "Routes struct {\n"
|
||||
for _, controller := range g.controllers {
|
||||
alias := m[controller.alias]
|
||||
str += "\t" + parser.StringToSnake(controller.name) + " *" + alias + ".Controller" + " `inject:\"\"`\n"
|
||||
}
|
||||
|
||||
return str + "}\n"
|
||||
}
|
||||
|
||||
type ApiGroups struct {
|
||||
name string
|
||||
imports map[string]string
|
||||
controllers []Controller
|
||||
servers []parser.Service
|
||||
}
|
||||
|
||||
type Controller struct {
|
||||
name string
|
||||
alias string
|
||||
ty string // *alias.Controller
|
||||
}
|
||||
47
console/commands/z_help.go
Normal file
47
console/commands/z_help.go
Normal file
@@ -0,0 +1,47 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var rootPath string
|
||||
|
||||
func SetRootPath(root string) {
|
||||
rootPath = root
|
||||
}
|
||||
|
||||
// 获取项目跟目录
|
||||
func getRootPath() string {
|
||||
return rootPath
|
||||
}
|
||||
|
||||
// 获取module
|
||||
func getModModule() string {
|
||||
root := getRootPath()
|
||||
path := root + "/go.mod"
|
||||
|
||||
fin, err := os.Stat(path)
|
||||
if err != nil {
|
||||
// no such file or dir
|
||||
panic("根目录必须存在go.mod")
|
||||
}
|
||||
if fin.IsDir() {
|
||||
panic("根目录必须存在go.mod文件")
|
||||
}
|
||||
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer file.Close()
|
||||
scanner := bufio.NewScanner(file)
|
||||
text := ""
|
||||
for scanner.Scan() {
|
||||
text = scanner.Text()
|
||||
break
|
||||
}
|
||||
text = strings.Replace(text, "module ", "", 1)
|
||||
return text
|
||||
}
|
||||
49
console/commands/z_inject_gen.go
Normal file
49
console/commands/z_inject_gen.go
Normal file
@@ -0,0 +1,49 @@
|
||||
// gen for home toolset
|
||||
package commands
|
||||
|
||||
import (
|
||||
app "github.com/go-home-admin/home/bootstrap/services/app"
|
||||
)
|
||||
|
||||
var _BeanCommandSingle *BeanCommand
|
||||
var _OrmCommandSingle *OrmCommand
|
||||
var _ProtocCommandSingle *ProtocCommand
|
||||
var _RouteCommandSingle *RouteCommand
|
||||
|
||||
func GetAllProvider() []interface{} {
|
||||
return []interface{}{
|
||||
NewBeanCommand(),
|
||||
NewOrmCommand(),
|
||||
NewProtocCommand(),
|
||||
NewRouteCommand(),
|
||||
}
|
||||
}
|
||||
|
||||
func NewBeanCommand() *BeanCommand {
|
||||
if _BeanCommandSingle == nil {
|
||||
_BeanCommandSingle = &BeanCommand{}
|
||||
app.AfterProvider(_BeanCommandSingle, "")
|
||||
}
|
||||
return _BeanCommandSingle
|
||||
}
|
||||
func NewOrmCommand() *OrmCommand {
|
||||
if _OrmCommandSingle == nil {
|
||||
_OrmCommandSingle = &OrmCommand{}
|
||||
app.AfterProvider(_OrmCommandSingle, "")
|
||||
}
|
||||
return _OrmCommandSingle
|
||||
}
|
||||
func NewProtocCommand() *ProtocCommand {
|
||||
if _ProtocCommandSingle == nil {
|
||||
_ProtocCommandSingle = &ProtocCommand{}
|
||||
app.AfterProvider(_ProtocCommandSingle, "")
|
||||
}
|
||||
return _ProtocCommandSingle
|
||||
}
|
||||
func NewRouteCommand() *RouteCommand {
|
||||
if _RouteCommandSingle == nil {
|
||||
_RouteCommandSingle = &RouteCommand{}
|
||||
app.AfterProvider(_RouteCommandSingle, "")
|
||||
}
|
||||
return _RouteCommandSingle
|
||||
}
|
||||
39
console/kernel.go
Normal file
39
console/kernel.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package console
|
||||
|
||||
import (
|
||||
"github.com/ctfang/command"
|
||||
"github.com/go-home-admin/toolset/console/commands"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// @Bean
|
||||
type Kernel struct{}
|
||||
|
||||
func (k *Kernel) Run() {
|
||||
app := command.New()
|
||||
app.AddBaseOption(command.ArgParam{
|
||||
Name: "root",
|
||||
Description: "获取项目跟路径",
|
||||
Call: func(val string, c *command.Console) (string, bool) {
|
||||
if val == "" {
|
||||
val, _ = os.Getwd()
|
||||
}
|
||||
|
||||
val, _ = filepath.Abs(val)
|
||||
commands.SetRootPath(val)
|
||||
return val, true
|
||||
},
|
||||
})
|
||||
|
||||
for _, provider := range commands.GetAllProvider() {
|
||||
if v, ok := provider.(command.Command); ok {
|
||||
app.AddCommand(v)
|
||||
}
|
||||
}
|
||||
app.Run()
|
||||
}
|
||||
|
||||
func (k *Kernel) Exit() {
|
||||
|
||||
}
|
||||
22
console/z_inject_gen.go
Executable file
22
console/z_inject_gen.go
Executable file
@@ -0,0 +1,22 @@
|
||||
// gen for home toolset
|
||||
package console
|
||||
|
||||
import (
|
||||
app "github.com/go-home-admin/home/bootstrap/services/app"
|
||||
)
|
||||
|
||||
var _KernelSingle *Kernel
|
||||
|
||||
func GetAllProvider() []interface{} {
|
||||
return []interface{}{
|
||||
NewKernel(),
|
||||
}
|
||||
}
|
||||
|
||||
func NewKernel() *Kernel {
|
||||
if _KernelSingle == nil {
|
||||
_KernelSingle = &Kernel{}
|
||||
app.AfterProvider(_KernelSingle, "")
|
||||
}
|
||||
return _KernelSingle
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
package bean
|
||||
|
||||
import (
|
||||
f "fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
// @Bean fdsaf
|
||||
type con struct {
|
||||
orm orm `inject:"" json:"orm"`
|
||||
orm2 *orm `inject:""`
|
||||
orm3 []orm `inject:""`
|
||||
area int `protobuf:"bytes,1,opt,name=area,proto3" form:"area" json:"area,omitempty"`
|
||||
orm4 func(i int, f map[string]string)
|
||||
}
|
||||
|
||||
// test api
|
||||
func (c con) api() {
|
||||
f.Println("test")
|
||||
|
||||
f.Println(testInt(1))
|
||||
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
//
|
||||
type testInt int
|
||||
|
||||
func test() {
|
||||
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package bean
|
||||
|
||||
import (
|
||||
f "fmt"
|
||||
)
|
||||
|
||||
// @Bean
|
||||
type orm struct {
|
||||
}
|
||||
|
||||
// test api
|
||||
func (c orm) api() {
|
||||
f.Println("test")
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
package ults
|
||||
16
go.mod
16
go.mod
@@ -1,3 +1,19 @@
|
||||
module github.com/go-home-admin/toolset
|
||||
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/ctfang/command v1.0.0
|
||||
github.com/go-home-admin/home v0.0.0-20220329042758-02a34d8e19bc
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/go-redis/redis/v8 v8.11.5 // indirect
|
||||
github.com/go-sql-driver/mysql v1.6.0
|
||||
github.com/joho/godotenv v1.4.0
|
||||
golang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064 // indirect
|
||||
golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886 // indirect
|
||||
google.golang.org/protobuf v1.28.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
)
|
||||
|
||||
201
go.sum
Normal file
201
go.sum
Normal file
@@ -0,0 +1,201 @@
|
||||
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
|
||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/ctfang/command v1.0.0 h1:CurDflG0o7GntcRWWzbFlKdR3CMZUF/627Rv/Ii8HNE=
|
||||
github.com/ctfang/command v1.0.0/go.mod h1:iWJcUCwZReswQ7T5IaRE6ZvGzZ/m9v53Z1na20JttV8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.7.7 h1:3DoBmSbJbZAWqXJC3SLjAPfutPJJRN1U5pALB7EeTTs=
|
||||
github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U=
|
||||
github.com/go-home-admin/home v0.0.0-20220329042758-02a34d8e19bc h1:ozZX6t4p/WfKGdvjVrpfxqs4GyX6igh6uDLWZ5vw308=
|
||||
github.com/go-home-admin/home v0.0.0-20220329042758-02a34d8e19bc/go.mod h1:xdiTISDwUhNaIQlvP+Xh67MasSXHuZH1gUCG0VfuHnc=
|
||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||
github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
|
||||
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
|
||||
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
||||
github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=
|
||||
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
|
||||
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
|
||||
github.com/go-playground/validator/v10 v10.10.1 h1:uA0+amWMiglNZKZ9FJRKUAe9U3RX91eVn1JYXMWt7ig=
|
||||
github.com/go-playground/validator/v10 v10.10.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU=
|
||||
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
|
||||
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
|
||||
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
|
||||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg=
|
||||
github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
|
||||
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
|
||||
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
||||
github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
|
||||
github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
||||
github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
|
||||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||
github.com/ugorji/go v1.2.7 h1:qYhyWUUd6WbiM+C6JZAUkIJt/1WrjzNHY9+KCIjVqTo=
|
||||
github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
|
||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||
github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
|
||||
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064 h1:S25/rfnfsMVgORT4/J61MJ7rdyseOZOyvLIrZEZ7s6s=
|
||||
golang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886 h1:eJv7u3ksNXoLbGSKuv2s/SIO4tJVxc/A+MTpzxDgz/Q=
|
||||
golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
|
||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/driver/mysql v1.3.2/go.mod h1:ChK6AHbHgDCFZyJp0F+BmVGb06PSIoh9uVYKAlRbb2U=
|
||||
gorm.io/gorm v1.23.1/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
|
||||
gorm.io/gorm v1.23.3/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
|
||||
6
main.go
6
main.go
@@ -1,7 +1,9 @@
|
||||
package main
|
||||
|
||||
import "github.com/go-home-admin/toolset/parser"
|
||||
import (
|
||||
"github.com/go-home-admin/toolset/console"
|
||||
)
|
||||
|
||||
func main() {
|
||||
parser.NewGoParserForDir("./examples/bean")
|
||||
console.NewKernel().Run()
|
||||
}
|
||||
|
||||
337
parser/go.go
337
parser/go.go
@@ -5,9 +5,7 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
/**
|
||||
golang parser 非完整token实现
|
||||
*/
|
||||
// GoFileParser 非完整token实现
|
||||
type GoFileParser struct {
|
||||
PackageName string
|
||||
PackageDoc string
|
||||
@@ -16,21 +14,51 @@ type GoFileParser struct {
|
||||
Funds map[string]GoFunc
|
||||
}
|
||||
|
||||
func NewGoParserForDir(path string) []GoFileParser {
|
||||
var got []GoFileParser
|
||||
for _, file := range loadGoFiles(path) {
|
||||
gof, _ := GetFileParser(file)
|
||||
got = append(got, gof)
|
||||
func NewGoParserForDir(path string) map[string][]GoFileParser {
|
||||
got := make(map[string][]GoFileParser)
|
||||
for _, dir := range GetChildrenDir(path) {
|
||||
arr := make([]GoFileParser, 0)
|
||||
for _, file := range dir.GetFiles(".go") {
|
||||
gof, _ := GetFileParser(file.Path)
|
||||
arr = append(arr, gof)
|
||||
}
|
||||
got[dir.Path] = arr
|
||||
}
|
||||
|
||||
return got
|
||||
}
|
||||
|
||||
func loadGoFiles(path string) []FileInfo {
|
||||
return loadFiles(path, ".go")
|
||||
// go 关键字语法块
|
||||
// map\[]\interface{}
|
||||
func getWordsWitchGo(l *GoWords) GoWords {
|
||||
var got = GoWords{
|
||||
list: make([]*word, 0),
|
||||
}
|
||||
|
||||
for offset := 0; offset < len(l.list); offset++ {
|
||||
work := l.list[offset]
|
||||
switch work.Ty {
|
||||
case wordT_word:
|
||||
switch work.Str {
|
||||
case "interface":
|
||||
if len(l.list) >= (offset+2) && l.list[offset+1].Str == "{" && l.list[offset+2].Str == "}" {
|
||||
offset = offset + 2
|
||||
work.Str = work.Str + "{}"
|
||||
got.list = append(got.list, work)
|
||||
} else {
|
||||
got.list = append(got.list, work)
|
||||
}
|
||||
default:
|
||||
got.list = append(got.list, work)
|
||||
}
|
||||
default:
|
||||
got.list = append(got.list, work)
|
||||
}
|
||||
}
|
||||
return got
|
||||
}
|
||||
|
||||
func GetFileParser(info FileInfo) (GoFileParser, error) {
|
||||
func GetFileParser(path string) (GoFileParser, error) {
|
||||
d := GoFileParser{
|
||||
PackageName: "",
|
||||
PackageDoc: "",
|
||||
@@ -39,42 +67,55 @@ func GetFileParser(info FileInfo) (GoFileParser, error) {
|
||||
Funds: make(map[string]GoFunc),
|
||||
}
|
||||
|
||||
l := getWordsWitchFile(info.path)
|
||||
l := getWordsWitchFile(path)
|
||||
l = getWordsWitchGo(&l)
|
||||
lastDoc := ""
|
||||
for offset := 0; offset < len(l.list); offset++ {
|
||||
work := l.list[offset]
|
||||
// 原则上, 每个块级别的作用域必须自己处理完, 返回的偏移必须是下一个块的开始
|
||||
switch work.t {
|
||||
switch work.Ty {
|
||||
case wordT_line:
|
||||
lastDoc += work.Str
|
||||
case wordT_division:
|
||||
case wordT_doc:
|
||||
lastDoc = work.str
|
||||
lastDoc += work.Str
|
||||
case wordT_word:
|
||||
switch work.str {
|
||||
switch work.Str {
|
||||
case "package":
|
||||
d.PackageDoc = lastDoc
|
||||
d.PackageName, offset = handlePackageName(l.list, offset)
|
||||
lastDoc = ""
|
||||
case "import":
|
||||
var imap map[string]string
|
||||
imap, offset = handleImports(l.list, offset)
|
||||
for k, v := range imap {
|
||||
d.Imports[k] = v
|
||||
}
|
||||
lastDoc = ""
|
||||
case "type":
|
||||
var imap GoType
|
||||
imap, offset = handleTypes(l.list, offset)
|
||||
imap.Doc = lastDoc
|
||||
imap, offset = handleTypes(l.list, offset, d)
|
||||
imap.Doc = GoDoc(lastDoc)
|
||||
d.Types[imap.Name] = imap
|
||||
lastDoc = ""
|
||||
case "func":
|
||||
var gf GoFunc
|
||||
gf, offset = handleFunds(l.list, offset)
|
||||
d.Funds[gf.Name] = gf
|
||||
lastDoc = ""
|
||||
case "const":
|
||||
_, offset = handleCosts(l.list, offset)
|
||||
lastDoc = ""
|
||||
case "var":
|
||||
_, offset = handleVars(l.list, offset)
|
||||
lastDoc = ""
|
||||
default:
|
||||
fmt.Println("文件块作用域似乎解析有错误", info.path, work.str, offset)
|
||||
nl := l.list[offset:]
|
||||
str := ""
|
||||
for _, w := range nl {
|
||||
str += w.Str
|
||||
}
|
||||
fmt.Println("文件块作用域似乎解析有错误", path, offset, str)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -83,64 +124,65 @@ func GetFileParser(info FileInfo) (GoFileParser, error) {
|
||||
}
|
||||
|
||||
func handlePackageName(l []*word, offset int) (string, int) {
|
||||
newOffset := offset
|
||||
name := ""
|
||||
for i, w := range l[offset:] {
|
||||
if w.t == wordT_line {
|
||||
name = l[i-1].str
|
||||
newOffset = i
|
||||
break
|
||||
}
|
||||
name, i := GetFistWordBehindStr(l[offset:], "package")
|
||||
return name, offset + i
|
||||
}
|
||||
|
||||
func getImport(sl []string) (string, string) {
|
||||
if len(sl) == 2 {
|
||||
return sl[0], sl[1][1 : len(sl[1])-1]
|
||||
}
|
||||
|
||||
return name, newOffset
|
||||
str := sl[0][1 : len(sl[0])-1]
|
||||
temp := strings.Split(str, "/")
|
||||
key := temp[len(temp)-1]
|
||||
return key, str
|
||||
}
|
||||
|
||||
func handleImports(l []*word, offset int) (map[string]string, int) {
|
||||
newOffset := offset
|
||||
imap := make(map[string]string)
|
||||
var key, val string
|
||||
start := 0
|
||||
|
||||
gofer:
|
||||
for i, w := range l[offset+1:] {
|
||||
switch w.t {
|
||||
case wordT_line:
|
||||
newOffset = i + offset + 1
|
||||
switch start {
|
||||
case 0:
|
||||
break
|
||||
case 1:
|
||||
if l[newOffset+1].str == ")" {
|
||||
i = newOffset + 1
|
||||
break gofer
|
||||
}
|
||||
key, val = "", ""
|
||||
}
|
||||
case wordT_word:
|
||||
if w.str[0:1] == "\"" {
|
||||
val = w.str[1 : len(w.str)-1]
|
||||
|
||||
if key == "" {
|
||||
temp := strings.Split(val, "/")
|
||||
key = temp[len(temp)-1]
|
||||
}
|
||||
ft, fti := GetFistStr(l[offset+1:])
|
||||
if ft != "(" {
|
||||
arr := make([]string, 0)
|
||||
for i, w := range l[offset+fti:] {
|
||||
if wordT_line == w.Ty {
|
||||
newOffset = offset + fti + i
|
||||
key, val = getImport(arr)
|
||||
imap[key] = val
|
||||
} else {
|
||||
key = w.str
|
||||
return imap, newOffset
|
||||
}
|
||||
case wordT_division:
|
||||
if w.str == "(" {
|
||||
start = 1
|
||||
|
||||
if w.Ty == wordT_word {
|
||||
arr = append(arr, w.Str)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
st, et := GetBrackets(l[offset+1:], "(", ")")
|
||||
st = st + offset + 1
|
||||
et = et + offset + 1
|
||||
newOffset = et
|
||||
|
||||
arr := make([]string, 0)
|
||||
for _, w := range l[st : et+1] {
|
||||
if wordT_line == w.Ty && len(arr) != 0 {
|
||||
key, val = getImport(arr)
|
||||
imap[key] = val
|
||||
arr = make([]string, 0)
|
||||
}
|
||||
|
||||
if w.Ty == wordT_word {
|
||||
arr = append(arr, w.Str)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return imap, newOffset + 1
|
||||
return imap, newOffset
|
||||
}
|
||||
|
||||
type GoType struct {
|
||||
Doc string
|
||||
Doc GoDoc
|
||||
Name string
|
||||
Attrs map[string]GoTypeAttr
|
||||
}
|
||||
@@ -149,28 +191,76 @@ type GoTypeAttr struct {
|
||||
TypeName string
|
||||
TypeAlias string
|
||||
TypeImport string
|
||||
Tag map[string]string
|
||||
InPackage bool // 是否本包的引用
|
||||
Tag map[string]TagDoc
|
||||
}
|
||||
|
||||
// 普通指针
|
||||
type TagDoc string
|
||||
|
||||
func (t TagDoc) Get(num int) string {
|
||||
s := string(t)
|
||||
sr := strings.Split(s, ",")
|
||||
return strings.Trim(sr[num], " ")
|
||||
}
|
||||
|
||||
func (t TagDoc) Count() int {
|
||||
s := string(t)
|
||||
sr := strings.Split(s, ",")
|
||||
return len(sr)
|
||||
}
|
||||
|
||||
type GoDoc string
|
||||
|
||||
// HasAnnotation 是否存在某个注解
|
||||
func (d GoDoc) HasAnnotation(check string) bool {
|
||||
return strings.Index(string(d), check) != -1
|
||||
}
|
||||
|
||||
func (d GoDoc) GetAlias() string {
|
||||
l := GetWords(string(d)[2:])
|
||||
num := 0
|
||||
for i, w := range l {
|
||||
if w.Ty == wordT_word {
|
||||
if w.Str == "Bean" {
|
||||
if l[i-1].Str == "@" {
|
||||
num = 1
|
||||
}
|
||||
} else if num == 1 {
|
||||
return w.Str[1 : len(w.Str)-1]
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// IsPointer 普通指针
|
||||
func (receiver GoTypeAttr) IsPointer() bool {
|
||||
return receiver.TypeName[0:1] == "*"
|
||||
}
|
||||
|
||||
func (receiver GoTypeAttr) HasTag(name string) bool {
|
||||
for s := range receiver.Tag {
|
||||
if s == name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 组装成数组, 只限name type other\n结构
|
||||
func getArrGoWord(l []*word) [][]string {
|
||||
got := make([][]string, 0)
|
||||
arr := GetArrWord(l)
|
||||
for _, i := range arr {
|
||||
lis := i[len(i)-1].str
|
||||
lis := i[len(i)-1].Str
|
||||
if lis[0:1] == "`" && len(i) >= 3 {
|
||||
ty := ""
|
||||
for in := 1; in < len(i)-1; in++ {
|
||||
if i[in].t != wordT_doc {
|
||||
ty = ty + i[in].str
|
||||
if i[in].Ty != wordT_doc {
|
||||
ty = ty + i[in].Str
|
||||
}
|
||||
}
|
||||
got = append(got, []string{i[0].str, ty, lis})
|
||||
got = append(got, []string{i[0].Str, ty, lis})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,8 +276,8 @@ func getArrGoTag(source string) [][]string {
|
||||
got := make([][]string, 0)
|
||||
arr := make([]string, 0)
|
||||
for _, w := range wl {
|
||||
if w.t == wordT_word {
|
||||
arr = append(arr, w.str)
|
||||
if w.Ty == wordT_word {
|
||||
arr = append(arr, w.Str)
|
||||
i++
|
||||
if i >= 2 {
|
||||
i = 0
|
||||
@@ -199,7 +289,7 @@ func getArrGoTag(source string) [][]string {
|
||||
|
||||
return got
|
||||
}
|
||||
func handleTypes(l []*word, offset int) (GoType, int) {
|
||||
func handleTypes(l []*word, offset int, d GoFileParser) (GoType, int) {
|
||||
newOffset := offset
|
||||
nl := l[offset:]
|
||||
got := GoType{
|
||||
@@ -222,17 +312,17 @@ func handleTypes(l []*word, offset int) (GoType, int) {
|
||||
// TODO 当前仅支持有tag的
|
||||
if len(wordAttrs) == 3 && strings.Index(wordAttrs[2], "`") == 0 {
|
||||
attr := GoTypeAttr{
|
||||
Name: wordAttrs[0],
|
||||
TypeName: wordAttrs[1],
|
||||
TypeAlias: "",
|
||||
TypeImport: "",
|
||||
Tag: map[string]string{},
|
||||
Name: wordAttrs[0],
|
||||
TypeName: wordAttrs[1],
|
||||
Tag: map[string]TagDoc{},
|
||||
}
|
||||
getTypeAlias(wordAttrs[1], d, &attr)
|
||||
// 解析 go tag
|
||||
tagArr := getArrGoTag(wordAttrs[2])
|
||||
|
||||
for _, tagStrArr := range tagArr {
|
||||
attr.Tag[tagStrArr[0]] = tagStrArr[1]
|
||||
td := tagStrArr[1]
|
||||
attr.Tag[tagStrArr[0]] = TagDoc(td[1 : len(td)-1])
|
||||
}
|
||||
got.Attrs[attr.Name] = attr
|
||||
}
|
||||
@@ -246,53 +336,94 @@ func handleTypes(l []*word, offset int) (GoType, int) {
|
||||
return got, newOffset
|
||||
}
|
||||
|
||||
// 根据属性声明类型或者类型的引入名称
|
||||
func getTypeAlias(str string, d GoFileParser, attr *GoTypeAttr) {
|
||||
wArr := GetWords(str)
|
||||
wf := wArr[0]
|
||||
|
||||
if wf.Ty == wordT_word || wf.Str == "*" {
|
||||
if (wf.Str == "*" && len(wArr) >= 3) || (wf.Str != "*" && len(wArr) >= 2) {
|
||||
attr.TypeAlias, _ = GetFistWord(wArr)
|
||||
attr.TypeImport = d.Imports[attr.TypeAlias]
|
||||
return
|
||||
}
|
||||
}
|
||||
// 本包
|
||||
attr.TypeAlias = d.PackageName
|
||||
attr.TypeImport = "" // TODO
|
||||
attr.InPackage = true
|
||||
}
|
||||
|
||||
type GoFunc struct {
|
||||
Name string
|
||||
Stu string
|
||||
}
|
||||
|
||||
func handleFunds(l []*word, offset int) (GoFunc, int) {
|
||||
ft := 0
|
||||
for _, w := range l[offset+1:] {
|
||||
if w.t == wordT_division && w.str == "(" {
|
||||
ft = 1
|
||||
break
|
||||
} else if w.t == wordT_word {
|
||||
break
|
||||
}
|
||||
}
|
||||
if ft == 0 {
|
||||
ft, _ := GetFistStr(l[offset+1:])
|
||||
name := ""
|
||||
if ft != "(" {
|
||||
// 普通函数
|
||||
name, i := GetFistWordBehindStr(l[offset:], "func")
|
||||
|
||||
_, et := GetBrackets(l[offset+i:], "(", ")")
|
||||
_, et = GetBrackets(l[offset+et+i:], "{", "}")
|
||||
return GoFunc{Name: name}, offset + et + i
|
||||
var i int
|
||||
name, i = GetFistWordBehindStr(l[offset:], "func")
|
||||
offset = offset + i
|
||||
_, et := GetBrackets(l[offset:], "(", ")")
|
||||
offset = offset + et
|
||||
} else {
|
||||
// 结构函数
|
||||
_, et := GetBrackets(l[offset:], "(", ")")
|
||||
offset = offset + et
|
||||
name, _ := GetFistWord(l[offset:])
|
||||
name, _ = GetFistWord(l[offset:])
|
||||
_, et = GetBrackets(l[offset:], "(", ")")
|
||||
offset = offset + et
|
||||
_, et = GetBrackets(l[offset:], "{", "}")
|
||||
return GoFunc{Name: name}, offset + et
|
||||
}
|
||||
// 排除返回值的interface{}
|
||||
st, et := GetBrackets(l[offset:], "{", "}")
|
||||
interCount := 0
|
||||
for _, w := range l[offset : offset+st] {
|
||||
if w.Str == "interface" {
|
||||
interCount++
|
||||
}
|
||||
}
|
||||
if interCount != 0 {
|
||||
for i := 0; i <= interCount; i++ {
|
||||
_, et := GetBrackets(l[offset:], "{", "}")
|
||||
offset = offset + et
|
||||
}
|
||||
} else {
|
||||
offset = offset + et
|
||||
}
|
||||
return GoFunc{Name: name}, offset
|
||||
}
|
||||
func handleCosts(l []*word, offset int) (map[string]string, int) {
|
||||
ok, off := GetLastIsIdentifier(l[offset:], "(")
|
||||
if ok {
|
||||
return nil, off + offset
|
||||
}
|
||||
_, et := GetBrackets(l[offset:], "(", ")")
|
||||
return nil, offset + et
|
||||
return handleVars(l, offset)
|
||||
}
|
||||
|
||||
func handleVars(l []*word, offset int) (map[string]string, int) {
|
||||
ok, off := GetLastIsIdentifier(l[offset:], "(")
|
||||
if ok {
|
||||
return nil, off + offset
|
||||
ft, _ := GetFistStr(l[offset+1:])
|
||||
if ft != "(" {
|
||||
ok, _ := GetLastIsIdentifier(l[offset:], "{")
|
||||
if ok {
|
||||
last := NextLine(l[offset:])
|
||||
ss := l[offset+last-1]
|
||||
if ss.Str == "{" {
|
||||
nl := l[offset+last-1:]
|
||||
offset = offset + last - 1
|
||||
_, et := GetBrackets(nl, "{", "}")
|
||||
return nil, offset + et + 1
|
||||
} else {
|
||||
nl := l[offset:]
|
||||
_, et := GetBrackets(nl, "{", "}")
|
||||
return nil, offset + et + 1
|
||||
}
|
||||
} else {
|
||||
last := NextLine(l[offset:])
|
||||
offset = offset + last
|
||||
return nil, offset
|
||||
}
|
||||
} else {
|
||||
nl := l[offset:]
|
||||
_, et := GetBrackets(nl, "(", ")")
|
||||
return nil, offset + et + 1
|
||||
}
|
||||
_, et := GetBrackets(l[offset:], "(", ")")
|
||||
return nil, offset + et
|
||||
}
|
||||
|
||||
@@ -1,210 +0,0 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
type DirParser struct {
|
||||
PackageName string
|
||||
Imports map[string]string
|
||||
Types map[string]string
|
||||
Funcs map[string]string
|
||||
}
|
||||
|
||||
func NewGoParser(path string) DirParser {
|
||||
d := DirParser{}
|
||||
for _, file := range loadGoFiles(path) {
|
||||
gof, _ := parserGoFile(file)
|
||||
fmt.Println(gof)
|
||||
}
|
||||
|
||||
return d
|
||||
}
|
||||
|
||||
func loadGoFiles(path string) []FileInfo {
|
||||
return loadFiles(path, ".go")
|
||||
}
|
||||
|
||||
func parserGoFile(info FileInfo) (string, error) {
|
||||
l := words(info.path)
|
||||
for _, i := range l.list {
|
||||
fmt.Print(i.str)
|
||||
}
|
||||
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// 分词后的结构
|
||||
type word struct {
|
||||
str string
|
||||
t wordT
|
||||
}
|
||||
type GoWords struct {
|
||||
list []*word
|
||||
}
|
||||
type wordT int
|
||||
|
||||
const (
|
||||
// 单词
|
||||
wordT_word wordT = 0
|
||||
// 分隔符
|
||||
wordT_division wordT = 1
|
||||
// 换行符
|
||||
wordT_line wordT = 2
|
||||
wordT_doc wordT = 3
|
||||
)
|
||||
|
||||
// 分词过程状态
|
||||
type scannerStatus int
|
||||
|
||||
const (
|
||||
// 新的一行
|
||||
scannerStatus_NewLine scannerStatus = 0
|
||||
// 准备注释中
|
||||
scannerStatus_DocWait scannerStatus = 1
|
||||
// 注释中, 单行, 多行
|
||||
scannerStatus_Doc scannerStatus = 2
|
||||
scannerStatus_Doc2 scannerStatus = 3
|
||||
// 遇到间隔符号
|
||||
scannerStatus_NewWork scannerStatus = 4
|
||||
// 单词中
|
||||
scannerStatus_Work scannerStatus = 5
|
||||
)
|
||||
|
||||
func words(path string) GoWords {
|
||||
var got = GoWords{
|
||||
list: make([]*word, 0),
|
||||
}
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
return got
|
||||
}
|
||||
defer file.Close()
|
||||
scanner := bufio.NewScanner(file)
|
||||
|
||||
status := scannerStatus_NewLine
|
||||
work := ""
|
||||
lastIsSpe := false
|
||||
for scanner.Scan() {
|
||||
for _, s := range scanner.Text() {
|
||||
str := string(s)
|
||||
stop := false
|
||||
switch status {
|
||||
case scannerStatus_Doc:
|
||||
work = work + str
|
||||
stop = true
|
||||
case scannerStatus_Doc2:
|
||||
work = work + str
|
||||
stop = true
|
||||
// 检查是否*/结束了
|
||||
if str == "/" && HasSuffix(work, "*/") {
|
||||
got.list = append(got.list, &word{
|
||||
str: work,
|
||||
t: wordT_doc,
|
||||
})
|
||||
// 分割后从新开始
|
||||
work = ""
|
||||
status = scannerStatus_NewWork
|
||||
}
|
||||
case scannerStatus_DocWait:
|
||||
switch str {
|
||||
case "/":
|
||||
work = work + str
|
||||
status = scannerStatus_Doc
|
||||
stop = true
|
||||
case "*":
|
||||
work = work + str
|
||||
status = scannerStatus_Doc2
|
||||
stop = true
|
||||
default:
|
||||
// 没有进入文档模式, 那么上一个就是分割符号
|
||||
got.list = append(got.list, &word{
|
||||
str: work,
|
||||
t: wordT_division,
|
||||
})
|
||||
// 分割后从新开始
|
||||
work = ""
|
||||
status = scannerStatus_NewWork
|
||||
}
|
||||
case scannerStatus_NewLine, scannerStatus_NewWork:
|
||||
if str == "/" {
|
||||
work = work + str
|
||||
status = scannerStatus_DocWait
|
||||
stop = true
|
||||
}
|
||||
}
|
||||
if !stop {
|
||||
if IsIdentifier(s) {
|
||||
// 标识符: 字母, 数字, _
|
||||
work = work + str
|
||||
status = scannerStatus_Work
|
||||
lastIsSpe = false
|
||||
} else if InArrString(str,[]string{" ","\t"}) {
|
||||
// 合并多余的空格
|
||||
if !lastIsSpe {
|
||||
got.list = append(got.list, &word{
|
||||
str: work,
|
||||
t: wordT_division,
|
||||
}, &word{
|
||||
str: str,
|
||||
t: wordT_division,
|
||||
})
|
||||
work = ""
|
||||
status = scannerStatus_NewWork
|
||||
lastIsSpe = true
|
||||
}
|
||||
}else {
|
||||
got.list = append(got.list, &word{
|
||||
str: work,
|
||||
t: wordT_word,
|
||||
}, &word{
|
||||
str: str,
|
||||
t: wordT_division,
|
||||
})
|
||||
work = ""
|
||||
status = scannerStatus_NewWork
|
||||
lastIsSpe = false
|
||||
}
|
||||
} else {
|
||||
lastIsSpe = false
|
||||
}
|
||||
}
|
||||
switch status {
|
||||
case scannerStatus_Work:
|
||||
got.list = append(got.list, &word{
|
||||
str: work,
|
||||
t: wordT_word,
|
||||
}, &word{
|
||||
str: "\n",
|
||||
t: wordT_line,
|
||||
})
|
||||
status = scannerStatus_NewLine
|
||||
work = ""
|
||||
case scannerStatus_Doc:
|
||||
got.list = append(got.list, &word{
|
||||
str: work,
|
||||
t: wordT_doc,
|
||||
}, &word{
|
||||
str: "\n",
|
||||
t: wordT_line,
|
||||
})
|
||||
status = scannerStatus_NewLine
|
||||
work = ""
|
||||
case scannerStatus_Doc2:
|
||||
// 多行注释未结束
|
||||
work = work + "\n"
|
||||
default:
|
||||
got.list = append(got.list, &word{
|
||||
str: "\n",
|
||||
t: wordT_line,
|
||||
})
|
||||
status = scannerStatus_NewLine
|
||||
work = ""
|
||||
}
|
||||
}
|
||||
|
||||
return got
|
||||
}
|
||||
@@ -6,15 +6,74 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
path2 "path"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
func DirIsExist(f string) bool {
|
||||
_, err := os.Stat(f)
|
||||
return err == nil || os.IsExist(err)
|
||||
}
|
||||
|
||||
type FileInfo struct {
|
||||
fs.FileInfo
|
||||
path string
|
||||
Path string
|
||||
}
|
||||
|
||||
// 读取目录中的所有文件包括子目录的文件
|
||||
func loadFiles(path string, ext string) []FileInfo {
|
||||
type DirInfo struct {
|
||||
Name string
|
||||
Path string
|
||||
}
|
||||
|
||||
// GetFiles 获取目录下所有文件
|
||||
func (di DirInfo) GetFiles(ext string) []FileInfo {
|
||||
got := make([]FileInfo, 0)
|
||||
|
||||
files, err := ioutil.ReadDir(di.Path)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
if path2.Ext(file.Name()) == ext {
|
||||
got = append(got, FileInfo{
|
||||
FileInfo: file,
|
||||
Path: di.Path + "/" + file.Name(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return got
|
||||
}
|
||||
|
||||
// GetChildrenDir 获取目录和所有子目录
|
||||
func GetChildrenDir(path string) []DirInfo {
|
||||
got := []DirInfo{
|
||||
{
|
||||
Name: filepath.Base(path),
|
||||
Path: path,
|
||||
},
|
||||
}
|
||||
|
||||
files, err := ioutil.ReadDir(path)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
if file.IsDir() && file.Name()[0:1] != "." {
|
||||
got = append(got, DirInfo{
|
||||
Name: file.Name(),
|
||||
Path: path + "/" + file.Name(),
|
||||
})
|
||||
got = append(got, GetChildrenDir(path+"/"+file.Name())...)
|
||||
}
|
||||
}
|
||||
|
||||
return got
|
||||
}
|
||||
|
||||
// GetDirFiles 读取目录中的所有文件包括子目录的文件
|
||||
func GetDirFiles(path string, ext string) []FileInfo {
|
||||
got := make([]FileInfo, 0)
|
||||
|
||||
files, err := ioutil.ReadDir(path)
|
||||
@@ -24,12 +83,12 @@ func loadFiles(path string, ext string) []FileInfo {
|
||||
|
||||
for _, file := range files {
|
||||
if file.IsDir() {
|
||||
t := loadFiles(path+"/"+file.Name(), ext)
|
||||
t := GetDirFiles(path+"/"+file.Name(), ext)
|
||||
got = append(got, t...)
|
||||
} else if path2.Ext(file.Name()) == ext {
|
||||
got = append(got, FileInfo{
|
||||
FileInfo: file,
|
||||
path: path + "/" + file.Name(),
|
||||
Path: path + "/" + file.Name(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -39,8 +98,8 @@ func loadFiles(path string, ext string) []FileInfo {
|
||||
|
||||
// 分词后的结构
|
||||
type word struct {
|
||||
str string
|
||||
t wordT
|
||||
Str string
|
||||
Ty wordT
|
||||
}
|
||||
type GoWords struct {
|
||||
list []*word
|
||||
@@ -108,8 +167,8 @@ func getWordsWitchFile(path string) GoWords {
|
||||
// 检查是否*/结束了
|
||||
if str == "/" && HasSuffix(work, "*/") {
|
||||
got.list = append(got.list, &word{
|
||||
str: work,
|
||||
t: wordT_doc,
|
||||
Str: work,
|
||||
Ty: wordT_doc,
|
||||
})
|
||||
// 分割后从新开始
|
||||
work = ""
|
||||
@@ -128,8 +187,8 @@ func getWordsWitchFile(path string) GoWords {
|
||||
default:
|
||||
// 没有进入文档模式, 那么上一个就是分割符号
|
||||
got.list = append(got.list, &word{
|
||||
str: work,
|
||||
t: wordT_division,
|
||||
Str: work,
|
||||
Ty: wordT_division,
|
||||
})
|
||||
// 分割后从新开始
|
||||
work = ""
|
||||
@@ -140,8 +199,8 @@ func getWordsWitchFile(path string) GoWords {
|
||||
stop = true
|
||||
if str == "\"" && !HasSuffix(work, "\\\"") {
|
||||
got.list = append(got.list, &word{
|
||||
str: work,
|
||||
t: wordT_word,
|
||||
Str: work,
|
||||
Ty: wordT_word,
|
||||
})
|
||||
// 分割后从新开始
|
||||
work = ""
|
||||
@@ -152,8 +211,8 @@ func getWordsWitchFile(path string) GoWords {
|
||||
stop = true
|
||||
if str == "'" {
|
||||
got.list = append(got.list, &word{
|
||||
str: work,
|
||||
t: wordT_word,
|
||||
Str: work,
|
||||
Ty: wordT_word,
|
||||
})
|
||||
// 分割后从新开始
|
||||
work = ""
|
||||
@@ -164,8 +223,8 @@ func getWordsWitchFile(path string) GoWords {
|
||||
stop = true
|
||||
if str == "`" {
|
||||
got.list = append(got.list, &word{
|
||||
str: work,
|
||||
t: wordT_word,
|
||||
Str: work,
|
||||
Ty: wordT_word,
|
||||
})
|
||||
// 分割后从新开始
|
||||
work = ""
|
||||
@@ -202,13 +261,13 @@ func getWordsWitchFile(path string) GoWords {
|
||||
if !lastIsSpe {
|
||||
if len(work) != 0 {
|
||||
got.list = append(got.list, &word{
|
||||
str: work,
|
||||
t: wordT_word,
|
||||
Str: work,
|
||||
Ty: wordT_word,
|
||||
})
|
||||
}
|
||||
got.list = append(got.list, &word{
|
||||
str: str,
|
||||
t: wordT_division,
|
||||
Str: str,
|
||||
Ty: wordT_division,
|
||||
})
|
||||
work = ""
|
||||
status = scannerStatus_NewWork
|
||||
@@ -217,13 +276,13 @@ func getWordsWitchFile(path string) GoWords {
|
||||
} else {
|
||||
if len(work) != 0 {
|
||||
got.list = append(got.list, &word{
|
||||
str: work,
|
||||
t: wordT_word,
|
||||
Str: work,
|
||||
Ty: wordT_word,
|
||||
})
|
||||
}
|
||||
got.list = append(got.list, &word{
|
||||
str: str,
|
||||
t: wordT_division,
|
||||
Str: str,
|
||||
Ty: wordT_division,
|
||||
})
|
||||
work = ""
|
||||
status = scannerStatus_NewWork
|
||||
@@ -236,21 +295,21 @@ func getWordsWitchFile(path string) GoWords {
|
||||
switch status {
|
||||
case scannerStatus_Work:
|
||||
got.list = append(got.list, &word{
|
||||
str: work,
|
||||
t: wordT_word,
|
||||
Str: work,
|
||||
Ty: wordT_word,
|
||||
}, &word{
|
||||
str: "\n",
|
||||
t: wordT_line,
|
||||
Str: "\n",
|
||||
Ty: wordT_line,
|
||||
})
|
||||
status = scannerStatus_NewLine
|
||||
work = ""
|
||||
case scannerStatus_Doc:
|
||||
got.list = append(got.list, &word{
|
||||
str: work,
|
||||
t: wordT_doc,
|
||||
Str: work,
|
||||
Ty: wordT_doc,
|
||||
}, &word{
|
||||
str: "\n",
|
||||
t: wordT_line,
|
||||
Str: "\n",
|
||||
Ty: wordT_line,
|
||||
})
|
||||
status = scannerStatus_NewLine
|
||||
work = ""
|
||||
@@ -258,10 +317,10 @@ func getWordsWitchFile(path string) GoWords {
|
||||
// 多行注释未结束
|
||||
work = work + "\n"
|
||||
default:
|
||||
if got.list[len(got.list)-1].t != wordT_line {
|
||||
if len(got.list) != 0 && got.list[len(got.list)-1].Ty != wordT_line {
|
||||
got.list = append(got.list, &word{
|
||||
str: "\n",
|
||||
t: wordT_line,
|
||||
Str: "\n",
|
||||
Ty: wordT_line,
|
||||
})
|
||||
}
|
||||
status = scannerStatus_NewLine
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
@@ -42,14 +45,12 @@ func GetLastIsIdentifier(l []*word, start string) (bool, int) {
|
||||
i := 0
|
||||
var w *word
|
||||
for i, w = range l {
|
||||
if w.t == wordT_line {
|
||||
if w.Ty == wordT_line {
|
||||
return ok, i
|
||||
} else if w.str == start {
|
||||
} else if w.Str == start {
|
||||
ok = true
|
||||
} else if !ok || !(w.t == wordT_doc || InArrString(w.str, []string{" ", "\t", "\n"})) {
|
||||
if ok {
|
||||
return false, i
|
||||
}
|
||||
} else if ok && !(w.Ty == wordT_doc || InArrString(w.Str, []string{" ", "\t"})) {
|
||||
ok = false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,21 +60,54 @@ func GetLastIsIdentifier(l []*word, start string) (bool, int) {
|
||||
// 获取第一个单词
|
||||
func GetFistWord(l []*word) (string, int) {
|
||||
for i, w := range l {
|
||||
if w.t == wordT_word {
|
||||
return w.str, i
|
||||
if w.Ty == wordT_word {
|
||||
return w.Str, i
|
||||
}
|
||||
}
|
||||
return "", len(l)
|
||||
}
|
||||
|
||||
// 获取第n个单词
|
||||
func GetWord(l []*word, n int) (string, int) {
|
||||
for i, w := range l {
|
||||
if w.Ty == wordT_word {
|
||||
n--
|
||||
if n <= 0 {
|
||||
return w.Str, i
|
||||
}
|
||||
}
|
||||
}
|
||||
return "", len(l)
|
||||
}
|
||||
|
||||
// 获取第一个字符(不包括空格, 换行符, 制表)
|
||||
func GetFistStr(l []*word) (string, int) {
|
||||
for i, w := range l {
|
||||
if !InArrString(w.Str, []string{" ", "\n", "\t"}) {
|
||||
return w.Str, i
|
||||
}
|
||||
}
|
||||
return "", len(l)
|
||||
}
|
||||
|
||||
// 获取下一行开始
|
||||
func NextLine(l []*word) int {
|
||||
for i, w := range l {
|
||||
if w.Ty == wordT_line {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return len(l)
|
||||
}
|
||||
|
||||
// 获取某字符串后第一个单词
|
||||
func GetFistWordBehindStr(l []*word, behind string) (string, int) {
|
||||
init := false
|
||||
for i, w := range l {
|
||||
if w.t == wordT_word {
|
||||
if w.Ty == wordT_word {
|
||||
if init {
|
||||
return w.str, i
|
||||
} else if w.str == behind {
|
||||
return w.Str, i
|
||||
} else if w.Str == behind {
|
||||
init = true
|
||||
}
|
||||
}
|
||||
@@ -84,9 +118,9 @@ func GetFistWordBehindStr(l []*word, behind string) (string, int) {
|
||||
// 括号引用起来的块, 或者第一行没有块就换行
|
||||
func GetBracketsOrLn(l []*word, start, end string) (int, int, bool) {
|
||||
for i, w := range l {
|
||||
if w.t == wordT_line {
|
||||
if w.Ty == wordT_line {
|
||||
return 0, i, false
|
||||
} else if w.t == wordT_division && w.str == start {
|
||||
} else if w.Ty == wordT_division && w.Str == start {
|
||||
startInt, endInt := GetBrackets(l, start, end)
|
||||
return startInt, endInt, true
|
||||
}
|
||||
@@ -96,27 +130,28 @@ func GetBracketsOrLn(l []*word, start, end string) (int, int, bool) {
|
||||
}
|
||||
|
||||
// 括号引用起来的块, 词性必须是分隔符
|
||||
// 返回是开始偏移和结束偏移
|
||||
func GetBrackets(l []*word, start, end string) (int, int) {
|
||||
var startInt, endInt int
|
||||
|
||||
bCount := 0
|
||||
for i, w := range l {
|
||||
if bCount == 0 {
|
||||
if w.t == wordT_division && w.str == start {
|
||||
if w.Ty == wordT_division && w.Str == start {
|
||||
startInt = i
|
||||
bCount++
|
||||
}
|
||||
} else {
|
||||
if w.t == wordT_division {
|
||||
switch w.str {
|
||||
if w.Ty == wordT_division {
|
||||
switch w.Str {
|
||||
case start:
|
||||
bCount++
|
||||
case end:
|
||||
bCount--
|
||||
if bCount <= 0 {
|
||||
endInt = i
|
||||
return startInt, endInt
|
||||
}
|
||||
case start:
|
||||
bCount++
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -130,11 +165,11 @@ func GetArrWord(l []*word) [][]*word {
|
||||
got := make([][]*word, 0)
|
||||
sl := make([]*word, 0)
|
||||
for _, w := range l {
|
||||
switch w.t {
|
||||
switch w.Ty {
|
||||
case wordT_word:
|
||||
sl = append(sl, w)
|
||||
case wordT_division:
|
||||
if !InArrString(w.str, []string{" ", "\t"}) {
|
||||
if !InArrString(w.Str, []string{" ", "\t"}) {
|
||||
sl = append(sl, w)
|
||||
}
|
||||
case wordT_line:
|
||||
@@ -169,8 +204,8 @@ func GetWords(source string) []*word {
|
||||
// 检查是否*/结束了
|
||||
if str == "/" && HasSuffix(work, "*/") {
|
||||
list = append(list, &word{
|
||||
str: work,
|
||||
t: wordT_doc,
|
||||
Str: work,
|
||||
Ty: wordT_doc,
|
||||
})
|
||||
// 分割后从新开始
|
||||
work = ""
|
||||
@@ -189,8 +224,8 @@ func GetWords(source string) []*word {
|
||||
default:
|
||||
// 没有进入文档模式, 那么上一个就是分割符号
|
||||
list = append(list, &word{
|
||||
str: work,
|
||||
t: wordT_division,
|
||||
Str: work,
|
||||
Ty: wordT_division,
|
||||
})
|
||||
// 分割后从新开始
|
||||
work = ""
|
||||
@@ -201,8 +236,8 @@ func GetWords(source string) []*word {
|
||||
stop = true
|
||||
if str == "\"" && !HasSuffix(work, "\\\"") {
|
||||
list = append(list, &word{
|
||||
str: work,
|
||||
t: wordT_word,
|
||||
Str: work,
|
||||
Ty: wordT_word,
|
||||
})
|
||||
// 分割后从新开始
|
||||
work = ""
|
||||
@@ -213,8 +248,8 @@ func GetWords(source string) []*word {
|
||||
stop = true
|
||||
if str == "'" {
|
||||
list = append(list, &word{
|
||||
str: work,
|
||||
t: wordT_word,
|
||||
Str: work,
|
||||
Ty: wordT_word,
|
||||
})
|
||||
// 分割后从新开始
|
||||
work = ""
|
||||
@@ -225,8 +260,8 @@ func GetWords(source string) []*word {
|
||||
stop = true
|
||||
if str == "`" {
|
||||
list = append(list, &word{
|
||||
str: work,
|
||||
t: wordT_word,
|
||||
Str: work,
|
||||
Ty: wordT_word,
|
||||
})
|
||||
// 分割后从新开始
|
||||
work = ""
|
||||
@@ -263,13 +298,13 @@ func GetWords(source string) []*word {
|
||||
if !lastIsSpe {
|
||||
if len(work) != 0 {
|
||||
list = append(list, &word{
|
||||
str: work,
|
||||
t: wordT_word,
|
||||
Str: work,
|
||||
Ty: wordT_word,
|
||||
})
|
||||
}
|
||||
list = append(list, &word{
|
||||
str: str,
|
||||
t: wordT_division,
|
||||
Str: str,
|
||||
Ty: wordT_division,
|
||||
})
|
||||
work = ""
|
||||
status = scannerStatus_NewWork
|
||||
@@ -278,13 +313,13 @@ func GetWords(source string) []*word {
|
||||
} else {
|
||||
if len(work) != 0 {
|
||||
list = append(list, &word{
|
||||
str: work,
|
||||
t: wordT_word,
|
||||
Str: work,
|
||||
Ty: wordT_word,
|
||||
})
|
||||
}
|
||||
list = append(list, &word{
|
||||
str: str,
|
||||
t: wordT_division,
|
||||
Str: str,
|
||||
Ty: wordT_division,
|
||||
})
|
||||
work = ""
|
||||
status = scannerStatus_NewWork
|
||||
@@ -295,5 +330,103 @@ func GetWords(source string) []*word {
|
||||
}
|
||||
}
|
||||
|
||||
if status == scannerStatus_Work && len(work) != 0 {
|
||||
list = append(list, &word{
|
||||
Str: work,
|
||||
Ty: wordT_word,
|
||||
})
|
||||
}
|
||||
|
||||
return list
|
||||
}
|
||||
|
||||
// 驼峰转蛇形
|
||||
func StringToSnake(s string) string {
|
||||
s = strings.ReplaceAll(s, "-", "_")
|
||||
data := make([]byte, 0, len(s)*2)
|
||||
j := false
|
||||
num := len(s)
|
||||
for i := 0; i < num; i++ {
|
||||
d := s[i]
|
||||
if i > 0 && d >= 'A' && d <= 'Z' && j {
|
||||
data = append(data, '_')
|
||||
}
|
||||
if d != '_' {
|
||||
j = true
|
||||
}
|
||||
data = append(data, d)
|
||||
}
|
||||
return strings.ToLower(string(data[:]))
|
||||
}
|
||||
|
||||
// 蛇形转驼峰
|
||||
func StringToHump(s string) string {
|
||||
data := make([]byte, 0, len(s))
|
||||
j := false
|
||||
k := false
|
||||
num := len(s) - 1
|
||||
for i := 0; i <= num; i++ {
|
||||
d := s[i]
|
||||
if k == false && d >= 'A' && d <= 'Z' {
|
||||
k = true
|
||||
}
|
||||
if d >= 'a' && d <= 'z' && (j || k == false) {
|
||||
d = d - 32
|
||||
j = false
|
||||
k = true
|
||||
}
|
||||
if k && (d == '_' || d == '-') && num > i && s[i+1] >= 'a' && s[i+1] <= 'z' {
|
||||
j = true
|
||||
continue
|
||||
}
|
||||
data = append(data, d)
|
||||
}
|
||||
return string(data[:])
|
||||
}
|
||||
|
||||
// SortMap 排序map
|
||||
func SortMap(m map[string]string) []string {
|
||||
var keys []string
|
||||
for k := range m {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
return keys
|
||||
}
|
||||
|
||||
// GetImportStrForMap 生成import
|
||||
func GetImportStrForMap(m map[string]string) string {
|
||||
sk := SortMap(m)
|
||||
got := ""
|
||||
for _, k := range sk {
|
||||
got += "\n\t" + m[k] + " \"" + k + "\""
|
||||
}
|
||||
|
||||
return got
|
||||
}
|
||||
|
||||
// GenImportAlias 生成 import => alias
|
||||
func GenImportAlias(m map[string]string) map[string]string {
|
||||
aliasMapImport := make(map[string]string)
|
||||
importMapAlias := make(map[string]string)
|
||||
for _, imp := range m {
|
||||
temp := strings.Split(imp, "/")
|
||||
key := temp[len(temp)-1]
|
||||
|
||||
if _, ok := aliasMapImport[key]; ok {
|
||||
for i := 1; i < 1000; i++ {
|
||||
newKey := key + strconv.Itoa(i)
|
||||
if _, ok2 := aliasMapImport[newKey]; !ok2 {
|
||||
key = newKey
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
aliasMapImport[key] = imp
|
||||
}
|
||||
for s, s2 := range aliasMapImport {
|
||||
importMapAlias[s2] = s
|
||||
}
|
||||
|
||||
return importMapAlias
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ func TestGetBrackets(t *testing.T) {
|
||||
{" ", wordT_division},
|
||||
{"TestGetBrackets", wordT_word},
|
||||
{"(", wordT_word},
|
||||
{"t", wordT_word},
|
||||
{"Ty", wordT_word},
|
||||
{" ", wordT_division},
|
||||
{")", wordT_division},
|
||||
{" ", wordT_division},
|
||||
@@ -28,7 +28,12 @@ func TestGetBrackets(t *testing.T) {
|
||||
}
|
||||
|
||||
start, end := GetBrackets(l, "{", "}")
|
||||
fmt.Println(start, end)
|
||||
if l[start].Str != "{" {
|
||||
t.Error("{解析失败")
|
||||
}
|
||||
if l[end].Str != "}" {
|
||||
t.Error("}解析失败")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLastIsIdentifier(t *testing.T) {
|
||||
@@ -37,7 +42,7 @@ func TestLastIsIdentifier(t *testing.T) {
|
||||
{" ", wordT_division},
|
||||
{"TestGetBrackets", wordT_word},
|
||||
{"(", wordT_division},
|
||||
{"t", wordT_word},
|
||||
{"Ty", wordT_word},
|
||||
{" ", wordT_division},
|
||||
{")", wordT_division},
|
||||
{"(", wordT_division},
|
||||
|
||||
327
parser/protoc.go
327
parser/protoc.go
@@ -1 +1,328 @@
|
||||
package parser
|
||||
|
||||
import "fmt"
|
||||
|
||||
/**
|
||||
golang parser 非完整token实现
|
||||
*/
|
||||
type ProtocFileParser struct {
|
||||
Doc string
|
||||
Syntax string
|
||||
PackageName string
|
||||
Imports []string
|
||||
Option map[string]Option
|
||||
Services map[string]Service
|
||||
Messages map[string]Message
|
||||
Enums map[string]Enum
|
||||
}
|
||||
|
||||
type Option struct {
|
||||
Doc string
|
||||
Key string
|
||||
Val string
|
||||
wl []*word
|
||||
alias string
|
||||
}
|
||||
|
||||
type Service struct {
|
||||
Protoc *ProtocFileParser
|
||||
Doc string
|
||||
Name string
|
||||
Opt map[string]Option
|
||||
Rpc map[string]ServiceRpc
|
||||
}
|
||||
|
||||
type ServiceRpc struct {
|
||||
Doc string
|
||||
Name string
|
||||
Param string
|
||||
Return string
|
||||
Opt map[string]Option
|
||||
}
|
||||
|
||||
type Message struct {
|
||||
Doc string
|
||||
Name string
|
||||
Attr []Attr
|
||||
Opt map[string]Option
|
||||
}
|
||||
type Attr struct {
|
||||
Doc string
|
||||
Name string
|
||||
ty string
|
||||
num int
|
||||
}
|
||||
|
||||
type Enum struct {
|
||||
Doc string
|
||||
Name string
|
||||
}
|
||||
|
||||
func NewProtocParserForDir(path string) map[string][]ProtocFileParser {
|
||||
got := make(map[string][]ProtocFileParser)
|
||||
for _, dir := range GetChildrenDir(path) {
|
||||
arr := make([]ProtocFileParser, 0)
|
||||
for _, file := range dir.GetFiles(".proto") {
|
||||
gof, _ := GetProtoFileParser(file.Path)
|
||||
arr = append(arr, gof)
|
||||
}
|
||||
got[dir.Path] = arr
|
||||
}
|
||||
|
||||
return got
|
||||
}
|
||||
|
||||
func GetProtoFileParser(path string) (ProtocFileParser, error) {
|
||||
d := ProtocFileParser{
|
||||
Imports: make([]string, 0),
|
||||
Option: make(map[string]Option),
|
||||
Services: make(map[string]Service),
|
||||
Messages: make(map[string]Message),
|
||||
Enums: make(map[string]Enum),
|
||||
}
|
||||
|
||||
l := getWordsWitchFile(path)
|
||||
lastDoc := ""
|
||||
for offset := 0; offset < len(l.list); offset++ {
|
||||
work := l.list[offset]
|
||||
// 原则上, 每个块级别的作用域必须自己处理完, 返回的偏移必须是下一个块的开始
|
||||
switch work.Ty {
|
||||
case wordT_line:
|
||||
case wordT_division:
|
||||
case wordT_doc:
|
||||
lastDoc = work.Str
|
||||
case wordT_word:
|
||||
switch work.Str {
|
||||
case "syntax":
|
||||
d.Doc = lastDoc
|
||||
d.Syntax, offset = protoSyntax(l.list, offset)
|
||||
lastDoc = ""
|
||||
case "package":
|
||||
d.PackageName, offset = protoPackageName(l.list, offset)
|
||||
lastDoc = ""
|
||||
case "import":
|
||||
var imports string
|
||||
imports, offset = protoImport(l.list, offset)
|
||||
d.Imports = append(d.Imports, imports)
|
||||
lastDoc = ""
|
||||
case "option":
|
||||
var val Option
|
||||
val.Doc = lastDoc
|
||||
val, offset = protoOption(l.list, offset)
|
||||
d.Option[val.Key] = val
|
||||
lastDoc = ""
|
||||
case "service":
|
||||
var val Service
|
||||
val, offset = protoService(l.list, offset)
|
||||
val.Protoc = &d
|
||||
val.Doc = lastDoc
|
||||
d.Services[val.Name] = val
|
||||
lastDoc = ""
|
||||
case "message":
|
||||
var val Message
|
||||
val.Doc = lastDoc
|
||||
val, offset = protoMessage(l.list, offset)
|
||||
d.Messages[val.Name] = val
|
||||
lastDoc = ""
|
||||
case "enum":
|
||||
var val Enum
|
||||
val.Doc = lastDoc
|
||||
val, offset = protoEnum(l.list, offset)
|
||||
d.Enums[val.Name] = val
|
||||
lastDoc = ""
|
||||
case "extend":
|
||||
_, offset = protoExtend(l.list, offset)
|
||||
lastDoc = ""
|
||||
default:
|
||||
fmt.Println("文件块作用域似乎解析有错误", path, work.Str, offset)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return d, nil
|
||||
}
|
||||
func protoSyntax(l []*word, offset int) (string, int) {
|
||||
name, i := GetFistWordBehindStr(l[offset:], "syntax")
|
||||
return name[1 : len(name)-1], offset + i
|
||||
}
|
||||
func protoPackageName(l []*word, offset int) (string, int) {
|
||||
name, i := GetFistWordBehindStr(l[offset:], "package")
|
||||
return name, offset + i
|
||||
}
|
||||
func protoImport(l []*word, offset int) (string, int) {
|
||||
name, i := GetFistWordBehindStr(l[offset:], "import")
|
||||
return name[1 : len(name)-1], offset + i
|
||||
}
|
||||
func protoOption(l []*word, offset int) (Option, int) {
|
||||
key, i := GetFistWordBehindStr(l[offset:], "option")
|
||||
offset = offset + i + 1
|
||||
val, i := GetFistWord(l[offset:])
|
||||
return Option{Key: key, Val: val[1 : len(val)-1]}, offset + i
|
||||
}
|
||||
func serverOption(l []*word, offset int) (Option, int) {
|
||||
st, et := GetBrackets(l[offset:], "(", ")")
|
||||
wl := l[offset+st : offset+et+1]
|
||||
offset = offset + et + 1
|
||||
val, i := GetFistWord(l[offset:])
|
||||
var key string
|
||||
var alias string
|
||||
if len(wl) >= 5 {
|
||||
alias = wl[1].Str
|
||||
}
|
||||
for _, w := range wl {
|
||||
key = key + w.Str
|
||||
}
|
||||
|
||||
return Option{
|
||||
Key: key[1 : len(key)-1],
|
||||
Val: val[1 : len(val)-1],
|
||||
wl: wl,
|
||||
alias: alias,
|
||||
}, offset + i
|
||||
}
|
||||
func protoService(l []*word, offset int) (Service, int) {
|
||||
name, i := GetFistWordBehindStr(l[offset:], "service")
|
||||
offset = offset + i
|
||||
st, et := GetBrackets(l[offset:], "{", "}")
|
||||
newOffset := offset + et
|
||||
nl := l[offset+st : offset+et]
|
||||
|
||||
got := Service{
|
||||
Name: name,
|
||||
Opt: make(map[string]Option, 0),
|
||||
Rpc: make(map[string]ServiceRpc, 0),
|
||||
}
|
||||
for offset := 0; offset < len(nl); offset++ {
|
||||
work := nl[offset]
|
||||
switch work.Ty {
|
||||
case wordT_line:
|
||||
case wordT_division:
|
||||
case wordT_doc:
|
||||
case wordT_word:
|
||||
switch work.Str {
|
||||
case "option":
|
||||
var val Option
|
||||
val, offset = serverOption(nl, offset)
|
||||
got.Opt[val.Key] = val
|
||||
case "rpc":
|
||||
var val ServiceRpc
|
||||
val, offset = protoRpc(nl, offset)
|
||||
got.Rpc[val.Name] = val
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return got, newOffset
|
||||
}
|
||||
|
||||
func protoRpc(l []*word, offset int) (ServiceRpc, int) {
|
||||
name, i := GetFistWordBehindStr(l[offset:], "rpc")
|
||||
offset = offset + i + 1
|
||||
Param, i := GetFistWord(l[offset:])
|
||||
offset = offset + i + 1
|
||||
Return, i := GetWord(l[offset:], 2)
|
||||
offset = offset + i + 1
|
||||
// opt
|
||||
opt := make(map[string]Option)
|
||||
st, et := GetBrackets(l[offset:], "{", "}")
|
||||
newOffset := offset + et + 1
|
||||
nl := l[offset+st : newOffset]
|
||||
for offset := 0; offset < len(nl); offset++ {
|
||||
work := nl[offset]
|
||||
switch work.Ty {
|
||||
case wordT_line:
|
||||
case wordT_division:
|
||||
case wordT_doc:
|
||||
case wordT_word:
|
||||
switch work.Str {
|
||||
case "option":
|
||||
var val Option
|
||||
val, offset = serverOption(nl, offset)
|
||||
opt[val.Key] = val
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ServiceRpc{
|
||||
Name: name,
|
||||
Param: Param,
|
||||
Return: Return,
|
||||
Opt: opt,
|
||||
}, newOffset
|
||||
}
|
||||
|
||||
func protoMessage(l []*word, offset int) (Message, int) {
|
||||
name, i := GetFistWordBehindStr(l[offset:], "message")
|
||||
offset = offset + i
|
||||
st, et := GetBrackets(l[offset:], "{", "}")
|
||||
newOffset := offset + et
|
||||
nl := l[offset+st : offset+et]
|
||||
|
||||
got := Message{
|
||||
Name: name,
|
||||
Attr: nil,
|
||||
Opt: nil,
|
||||
}
|
||||
for offset := 0; offset < len(nl); offset++ {
|
||||
work := nl[offset]
|
||||
switch work.Ty {
|
||||
case wordT_line:
|
||||
case wordT_division:
|
||||
case wordT_doc:
|
||||
case wordT_word:
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return got, newOffset
|
||||
}
|
||||
|
||||
func protoEnum(l []*word, offset int) (Enum, int) {
|
||||
name, i := GetFistWordBehindStr(l[offset:], "enum")
|
||||
offset = offset + i
|
||||
st, et := GetBrackets(l[offset:], "{", "}")
|
||||
newOffset := offset + et
|
||||
nl := l[offset+st : offset+et]
|
||||
|
||||
got := Enum{
|
||||
Name: name,
|
||||
}
|
||||
for offset := 0; offset < len(nl); offset++ {
|
||||
work := nl[offset]
|
||||
switch work.Ty {
|
||||
case wordT_line:
|
||||
case wordT_division:
|
||||
case wordT_doc:
|
||||
case wordT_word:
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return got, newOffset
|
||||
}
|
||||
|
||||
func protoExtend(l []*word, offset int) (Message, int) {
|
||||
name, i := GetFistWordBehindStr(l[offset:], "enum")
|
||||
offset = offset + i
|
||||
st, et := GetBrackets(l[offset:], "{", "}")
|
||||
newOffset := offset + et
|
||||
nl := l[offset+st : offset+et]
|
||||
|
||||
got := Message{
|
||||
Name: name,
|
||||
Attr: nil,
|
||||
Opt: nil,
|
||||
}
|
||||
for offset := 0; offset < len(nl); offset++ {
|
||||
work := nl[offset]
|
||||
switch work.Ty {
|
||||
case wordT_line:
|
||||
case wordT_division:
|
||||
case wordT_doc:
|
||||
case wordT_word:
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return got, newOffset
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user