bean + mysql orm

This commit is contained in:
jinzhu
2022-03-29 15:20:24 +08:00
parent f25999efa9
commit 07eade7d3f
24 changed files with 2762 additions and 438 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/.idea

17
Makefile Normal file
View 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
View 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
View 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
}

View 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,
}
}

View 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
View 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
View 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
}

View 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
}

View 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
View 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
View 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
}

View File

@@ -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() {
}

View File

@@ -1,14 +0,0 @@
package bean
import (
f "fmt"
)
// @Bean
type orm struct {
}
// test api
func (c orm) api() {
f.Println("test")
}

View File

@@ -1 +0,0 @@
package ults

16
go.mod
View File

@@ -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
View 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=

View File

@@ -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()
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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

View File

@@ -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
}

View File

@@ -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},

View File

@@ -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
}