From 84899f9935851b6f83edd476f24cd259eb5decb0 Mon Sep 17 00:00:00 2001 From: Kaxi <1042864399@qq.com> Date: Thu, 9 Oct 2025 02:04:32 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E7=BC=93=E5=AD=98=E6=9C=BA?= =?UTF-8?q?=E5=88=B6=20=E8=A1=A5=E5=85=85=E9=94=99=E8=AF=AF=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cache/cache.go | 78 ++++++++++++++++ cmd/cache.go | 71 +++++++++++++++ cmd/errcode.go | 97 ++++++++++++++++---- cmd/pb2port.go | 22 +++-- cmd/root.go | 19 ++++ config/plugin.go | 40 +++++++++ parser/clientConfig.go | 163 ++++++++++++++++++++++++++++++++++ parser/errCode.go | 108 ++++++++++++++++++++++ parser/zm_lib/cmd_lib.go | 14 +++ parser/zm_lib/public_tools.go | 12 ++- 10 files changed, 596 insertions(+), 28 deletions(-) create mode 100644 cache/cache.go create mode 100644 cmd/cache.go create mode 100644 config/plugin.go create mode 100644 parser/clientConfig.go create mode 100644 parser/errCode.go create mode 100644 parser/zm_lib/cmd_lib.go diff --git a/cache/cache.go b/cache/cache.go new file mode 100644 index 0000000..88db25c --- /dev/null +++ b/cache/cache.go @@ -0,0 +1,78 @@ +package cache + +import ( + "encoding/json" + "os" + "sync" +) + +type Cache interface { + Get(key string) (any, bool) + GetDefault(key string, defaultValue any) (any, error) + Set(key string, val any) error + Iteration(func(key string, val any)) +} + +type cache struct { + filename string + data map[string]any + mu sync.Mutex +} + +func (c *cache) Get(key string) (any, bool) { + c.mu.Lock() + defer c.mu.Unlock() + + val, ok := c.data[key] + return val, ok +} + +func (c *cache) GetDefault(key string, defaultVal any) (any, error) { + c.mu.Lock() + defer c.mu.Unlock() + val, ok := c.data[key] + if ok { + return val, nil + } + + c.data[key] = defaultVal + bytes, err := json.Marshal(c.data) + if err != nil { + return defaultVal, err + } + err = os.WriteFile(c.filename, bytes, 0666) + + return defaultVal, err +} + +func (c *cache) Set(key string, val any) error { + c.mu.Lock() + defer c.mu.Unlock() + c.data[key] = val + bytes, err := json.Marshal(c.data) + if err != nil { + return err + } + return os.WriteFile(c.filename, bytes, 0666) +} + +func (c *cache) Iteration(f func(key string, val any)) { + for k, v := range c.data { + f(k, v) + } +} + +func NewCache(filename string) (Cache, error) { + var c = &cache{ + filename: filename, + data: make(map[string]any), + } + bytes, err := os.ReadFile(filename) + if err != nil { + return c, nil + } + if err = json.Unmarshal(bytes, &c.data); err != nil { + return c, err + } + return c, nil +} diff --git a/cmd/cache.go b/cmd/cache.go new file mode 100644 index 0000000..52e4226 --- /dev/null +++ b/cmd/cache.go @@ -0,0 +1,71 @@ +package cmd + +import ( + "complie-erlang/cache" + "complie-erlang/parser/zm_lib" + "fmt" + "github.com/spf13/cobra" +) + +type cacheSet struct { + Path string + debug bool +} + +func (c *cacheSet) Run(cmd *cobra.Command, args []string) { + if len(args) == 0 { + fmt.Println("No command specified.") + return + } + + if err := c.run(args); err != nil { + fmt.Println(err) + return + } + + fmt.Println("Done.") + +} + +func (c *cacheSet) run(args []string) error { + pathByExecutable, err := zm_lib.FindPathByExecutable(c.Path) + if err != nil { + return err + } + + newCache, err := cache.NewCache(pathByExecutable) + if err != nil { + return err + } + + switch args[0] { + case "set": + return newCache.Set(args[1], args[2]) + case "get": + val, is := newCache.Get(args[1]) + if is { + fmt.Printf("%s : %s \n", args[0], val) + } else { + fmt.Printf("%s : nil \n", args[0]) + } + + default: + return fmt.Errorf("unknown command: %s", args[0]) + } + return nil +} + +func init() { + var singleSet = new(cacheSet) + var logsCmd = &cobra.Command{ + Use: "global", + Short: "缓存数据模版", + Long: `构建功能数据`, + Run: singleSet.Run, + } + + logsCmd.PersistentFlags().BoolVar(&singleSet.debug, "debug", false, "是否启动调试模式") + logsCmd.PersistentFlags().StringVar(&singleSet.Path, "path", globalCacheFileName, "缓存数据存放地址(相对于根目录)") + + rootCmd.AddCommand(logsCmd) +} diff --git a/cmd/errcode.go b/cmd/errcode.go index 3ed9e5e..2188830 100644 --- a/cmd/errcode.go +++ b/cmd/errcode.go @@ -1,11 +1,12 @@ package cmd import ( + "complie-erlang/cache" + "complie-erlang/parser" "complie-erlang/parser/zm_lib" "fmt" "github.com/spf13/cobra" "log" - "os" "path/filepath" ) @@ -18,21 +19,71 @@ type ErrCode struct { } func (s *ErrCode) run(_ *cobra.Command, arg []string) { - pluginPathByWd, err := zm_lib.GetPluginPathByWd(s.Plugin) - if err != nil { - log.Printf("[warn] plugin path no find:%s", err.Error()) - pluginPathByWd = "" - } - errPath := filepath.Join(pluginPathByWd, s.ErrPath) - fmt.Printf("errPath:%s %s\n", pluginPathByWd, errPath) - errorLanguageBytes, err := os.ReadFile(errPath) - if err != nil { - log.Printf("[error] read errPath:%s %s\n", errPath, err.Error()) - return + if len(arg) == 0 { + arg = append(arg, "show") } + switch arg[0] { + case "merge": + newCache, err := cache.NewCache("errorCode.json") + if err != nil { + log.Printf("[error] newCache err:%s", err.Error()) + return + } + fmt.Println("") + newCache.Iteration(func(key string, val any) { + fmt.Printf(" key:%s val:%s\n", key, val) + }) + fmt.Println("ok") + case "set": + newCache, err := cache.NewCache("errorCode.json") + if err != nil { + log.Printf("[error] newCache err:%s", err.Error()) + return + } + if len(arg) != 3 { + log.Printf("[error] len(arg):%d %s", len(arg), arg) + return + } + err = newCache.Set(arg[1], arg[2]) + fmt.Println("err:", err) + case "get": + newCache, err := cache.NewCache("errorCode.json") + if err != nil { + log.Printf("[error] newCache err:%s", err.Error()) + return + } + fmt.Println("") + newCache.Iteration(func(key string, val any) { + fmt.Printf(" key:%s val:%s\n", key, val) + }) + fmt.Println("") + default: + pluginPathByWd, err := zm_lib.GetPluginPathByWd(s.Plugin) + if err != nil { + log.Printf("[warn] plugin path no find:%s", err.Error()) + pluginPathByWd = "" + } + errPath := filepath.Join(pluginPathByWd, s.ErrPath) - fmt.Printf("errPath:%s %s\n", errorLanguageBytes, errPath) + clientCfg, err := parser.NewClientConfigWithFile(errPath) + if err != nil { + log.Printf("[error] load errPath:%s %s\n", errPath, err.Error()) + return + } + + errCode := parser.NewErrCode(clientCfg) + err = errCode.LoadWd() + + newCache, err := cache.NewCache("errorCode.json") + if err != nil { + log.Printf("[error] newCache err:%s", err.Error()) + return + } + + _ = errCode.PrintErrCode(newCache) + fmt.Printf("err:%v \n", err) + } } @@ -40,14 +91,24 @@ func init() { var singleSet = new(ErrCode) var logsCmd = &cobra.Command{ Use: "err_code", - Short: "根据proto 文件构建 功能模版", - Long: `构建功能数据`, - Run: singleSet.run, + Short: "检索代码模块生成错误码配置", + Long: `检索代码模块生成错误码配置 + +更多用法: +- 查看缓存区 get +- 设置缓存区 set <错误码> <错误码翻译设置> +- 提交缓存区 merge <功能名称> +`, + Run: singleSet.run, + PostRun: func(cmd *cobra.Command, args []string) { + fmt.Println("\n-h 可解锁更多用法") + }, } logsCmd.PersistentFlags().BoolVar(&singleSet.debug, "debug", false, "是否启动调试模式") - logsCmd.PersistentFlags().StringVar(&singleSet.ErrPath, "path", ".cfg/ErrorLanguage.txt", "错误码地址(相对于根目录)") - logsCmd.PersistentFlags().StringVar(&singleSet.Plugin, "plugin", "plugin", "代码根目录") + + _ = zm_lib.PersistentFlagsStringVar(logsCmd, &singleSet.ErrPath, globalCache, "path", "cfg/ErrorLanguagePackage.txt", "错误码地址(相对于根目录)") + _ = zm_lib.PersistentFlagsStringVar(logsCmd, &singleSet.Plugin, globalCache, "plugin", "plugin", "代码根目录") rootCmd.AddCommand(logsCmd) } diff --git a/cmd/pb2port.go b/cmd/pb2port.go index 0d146b7..47a11bd 100644 --- a/cmd/pb2port.go +++ b/cmd/pb2port.go @@ -9,7 +9,6 @@ import ( "log" "os" "path/filepath" - "strings" ) type Pb2Port struct { @@ -113,20 +112,25 @@ func init() { // 写入默认数据 if currentDir, err := os.Getwd(); err == nil { - out = fmt.Sprintf("%s_port.erl", filepath.Base(currentDir)) - pluginSpilt := strings.Split(currentDir, "plugin") - if len(pluginSpilt) > 0 { - pluginSpilt = pluginSpilt[:len(pluginSpilt)-1] - protoPath = filepath.Join(strings.Join(pluginSpilt, "plugin"), fmt.Sprintf("\\gpb\\game\\pro_%s.proto", filepath.Base(currentDir))) + outPut, _ := globalCache.GetDefault(fmt.Sprintf("%s.out", logsCmd.Use), "%s_port.erl") + out = fmt.Sprintf(outPut.(string), filepath.Base(currentDir)) + + plugin, _ := globalCache.GetDefault(fmt.Sprintf("%s.Plugin", logsCmd.Use), "plugin") + pluginPathByWd, err := zm_lib.GetPluginPathByWd(plugin.(string)) + if err == nil { + defaultProtoPath, _ := globalCache.GetDefault(fmt.Sprintf("%s.Proto", logsCmd.Use), "\\gpb\\game\\pro_%s.proto") + protoPath = filepath.Join(pluginPathByWd, fmt.Sprintf(defaultProtoPath.(string), filepath.Base(currentDir))) } } logsCmd.PersistentFlags().BoolVar(&singleSet.debug, "debug", false, "是否启动调试模式") logsCmd.PersistentFlags().StringVar(&singleSet.protoPath, "proto", protoPath, "读取文件") logsCmd.PersistentFlags().StringVar(&singleSet.out, "out", out, "输出文件") - logsCmd.PersistentFlags().StringVar(&singleSet.author, "author", "st,sutong@youkia.net", "作者") - logsCmd.PersistentFlags().StringVar(&singleSet.mainTemplate, "main_tpl", "ErlangPort", "主模版") - logsCmd.PersistentFlags().StringVar(&singleSet.tplDir, "tpl", "./templates/*.tpl", "模版地址") + _ = zm_lib.PersistentFlagsStringVar(logsCmd, &singleSet.author, globalCache, "author", "st,sutong@youkia.net", "作者") + _ = zm_lib.PersistentFlagsStringVar(logsCmd, &singleSet.mainTemplate, globalCache, "main_tpl", "ErlangPort", "主模版") + _ = zm_lib.PersistentFlagsStringVar(logsCmd, &singleSet.tplDir, globalCache, "tpl", "./templates/*.tpl", "模版地址") + //logsCmd.PersistentFlags().StringVar(&singleSet.mainTemplate, "main_tpl", "ErlangPort", "主模版") + //logsCmd.PersistentFlags().StringVar(&singleSet.tplDir, "tpl", "./templates/*.tpl", "模版地址") rootCmd.AddCommand(logsCmd) } diff --git a/cmd/root.go b/cmd/root.go index d452803..334a297 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -1,11 +1,30 @@ package cmd import ( + "complie-erlang/cache" + "complie-erlang/parser/zm_lib" "fmt" "github.com/spf13/cobra" "os" ) +// 全局缓存 +var globalCache = InitGlobalCache() + +const globalCacheFileName = "global.json" + +func InitGlobalCache() cache.Cache { + pathByExecutable, err := zm_lib.FindPathByExecutable(globalCacheFileName) + if err != nil { + panic("未查找到根目录 Err:" + err.Error()) + } + newCache, err := cache.NewCache(pathByExecutable) + if err != nil { + panic("创建缓存失败 Err:" + err.Error()) + } + return newCache +} + var rootCmd = &cobra.Command{ Use: "", Short: "erlang注解生成配置模版工具", diff --git a/config/plugin.go b/config/plugin.go new file mode 100644 index 0000000..759a9b6 --- /dev/null +++ b/config/plugin.go @@ -0,0 +1,40 @@ +package config + +import ( + "complie-erlang/parser/zm_proto" + "time" +) + +type PluginConfig struct { + Name string + Desc string + CreatAt time.Time + Type PluginType // 项目类型 + // 差异数据 + ProtoDir string // 接口proto 地址 + CfgDir string // 配置地址 + FunOpenId int + Port []zm_proto.Port + ErrorCodes map[string]string +} + +type Plugin interface { + Init(args []string) error +} + +type PluginType string + +var ( + PluginTypeGame PluginType = "Game" + PluginTypeGm = "Gm" + PluginTypeGmActivity = "GmActivity" +) + +const pluginPath = "./plugin.json" + +func (p *PluginConfig) Init(args []string) error { + // 创建配置文件 + + // 根据类型创建各种 初始化文件 + return nil +} diff --git a/parser/clientConfig.go b/parser/clientConfig.go new file mode 100644 index 0000000..d17833e --- /dev/null +++ b/parser/clientConfig.go @@ -0,0 +1,163 @@ +package parser + +import ( + "bufio" + "fmt" + "io" + "os" + "strings" + "sync" +) + +type ClientConfig struct { + Types map[int]ClientValue + TypeName2Index map[string]int + Data map[string]map[int]string + mu sync.RWMutex +} + +type ClientValue struct { + Desc string + Type string + Name string +} + +func NewClientConfig() *ClientConfig { + return &ClientConfig{ + Types: make(map[int]ClientValue), + Data: make(map[string]map[int]string), + TypeName2Index: make(map[string]int), + mu: sync.RWMutex{}, + } +} + +func NewClientConfigWithFile(filename string) (*ClientConfig, error) { + c := NewClientConfig() + return c, c.LoadFile(filename) +} + +func (c *ClientConfig) LoadFile(filename string) error { + file, err := os.Open(filename) + if err != nil { + return err + } + defer file.Close() + c.mu.Lock() + defer c.mu.Unlock() + return c.loadByReader(bufio.NewReader(file)) +} + +func (c *ClientConfig) LoadByReader(reader io.Reader) error { + c.mu.Lock() + defer c.mu.Unlock() + return c.loadByReader(reader) +} + +func (c *ClientConfig) loadByReader(reader io.Reader) error { + // 创建缓存读取 分隔符设置成 回车 + scan := bufio.NewScanner(reader) + scan.Split(bufio.ScanLines) + + var line int + var types []string + var desc []string + + for scan.Scan() { + line++ + + lineContent := strings.TrimSpace(scan.Text()) + if len(lineContent) == 0 { + continue + } + + // 去除注释 + split := strings.Split(lineContent, "//") + if len(split) > 1 { + lineContent = split[0] + } + + // 类型 + if line == 1 { + types = strings.Split(lineContent, "\t") + continue + } + + // 备注 + if line == 2 { + desc = strings.Split(lineContent, "\t") + continue + } + + // key + if line == 3 { + for i, t := range strings.Split(lineContent, "\t") { + if len(types) < i { + return fmt.Errorf("line %d: invalid type", line) + } + + if len(desc) < i { + return fmt.Errorf("line %d: invalid desc", line) + } + + c.Types[i] = ClientValue{ + Desc: desc[i], + Type: types[i], + Name: t, + } + c.TypeName2Index[t] = i + } + + continue + } + var lineData = make(map[int]string) + var lineKey string + for i1, v1 := range strings.Split(lineContent, "\t") { + if i1 == 0 { + lineKey = strings.TrimSpace(v1) + } + lineData[i1] = strings.TrimSpace(v1) + } + if len(lineData) == 0 { + continue + } + c.Data[lineKey] = lineData + } + + return nil +} + +// Value 读取单个 +func (c *ClientConfig) Value(key string, objKey string) (ClientValue, string, error) { + c.mu.RLock() + defer c.mu.RUnlock() + + l, is := c.Data[key] + if !is { + return ClientValue{}, "", fmt.Errorf("key %s not found", key) + } + index, is2 := c.TypeName2Index[objKey] + if !is2 { + return ClientValue{}, "", fmt.Errorf("objKey %s not found", objKey) + } + return c.Types[index], l[index], nil +} + +// ValueByLine 读取 +func (c *ClientConfig) ValueByLine(line map[int]string, key string) (ClientValue, string, error) { + c.mu.RLock() + defer c.mu.RUnlock() + + index, is2 := c.TypeName2Index[key] + if !is2 { + return ClientValue{}, "", fmt.Errorf("objKey %s not found", key) + } + return c.Types[index], line[index], nil +} + +// LineByKey 通过Key获取一行 +func (c *ClientConfig) LineByKey(key string) map[int]string { + c.mu.RLock() + defer c.mu.RUnlock() + + return c.Data[key] +} diff --git a/parser/errCode.go b/parser/errCode.go new file mode 100644 index 0000000..d1bb018 --- /dev/null +++ b/parser/errCode.go @@ -0,0 +1,108 @@ +package parser + +import ( + "complie-erlang/cache" + "complie-erlang/parser/zm_lib" + "fmt" + "os" + "path/filepath" + "regexp" +) + +type ErrCode struct { + codeMap map[string]bool + ClientConfig *ClientConfig +} + +func NewErrCode(c *ClientConfig) *ErrCode { + return &ErrCode{ + codeMap: make(map[string]bool), + ClientConfig: c, + } +} + +func (e *ErrCode) LoadWd() error { + wdDir, err := zm_lib.FindPathByWd(".") + if err != nil { + return err + } + return e.iterationDir(wdDir) +} + +func (e *ErrCode) iterationDir(baseDir string) error { + + dir, err := os.ReadDir(baseDir) + if err != nil { + return err + } + for _, d := range dir { + if d.IsDir() { + err = e.iterationDir(filepath.Join(filepath.Join(baseDir, d.Name()))) + if err != nil { + return err + } + continue + } + path := filepath.Join(baseDir, d.Name()) + + if err = e.loadFile(path); err != nil { + return err + } + } + + return nil +} + +func (e *ErrCode) loadFile(filename string) error { + bytes, err := os.ReadFile(filename) + if err != nil { + return err + } + + // 定义正则表达式 + // 注意:由于\?在Go的字符串中是一个特殊字符,我们使用\\?来表示一个问号。 + // 同时,由于双引号在正则表达式中有特殊含义,我们需要使用\\"来表示一个普通双引号。 + // 我们使用非贪婪匹配来确保只匹配到第一个双引号后的内容。 + re := regexp.MustCompile(`(?:\\?\?ThrowErr|throw)\("([^"]+)"\)`) + + // 查找所有匹配项 + matches := re.FindAllStringSubmatch(string(bytes), -1) + + // 提取并打印双引号内的内容 + for _, match := range matches { + // match[0] 是整个匹配的字符串,match[1] 是第一个括号内的匹配(即双引号内的内容) + if e.codeMap[match[1]] { + continue + } + e.codeMap[match[1]] = true + + } + return nil +} + +func (e *ErrCode) PrintErrCode(c cache.Cache) error { + fmt.Println(">") + + for key := range e.codeMap { + line := e.ClientConfig.LineByKey(key) + if line == nil { + val, is := c.Get(key) + if is { + fmt.Printf(" %s: %v[cache] \n", key, val) + } else { + fmt.Printf(" %s: %v \n", key, nil) + } + + } else { + _, s, err := e.ClientConfig.ValueByLine(line, "UserCN") + if err != nil { + return err + } + fmt.Printf(" %s: %s \n", key, s) + } + } + + fmt.Println() + + return nil +} diff --git a/parser/zm_lib/cmd_lib.go b/parser/zm_lib/cmd_lib.go new file mode 100644 index 0000000..47109d8 --- /dev/null +++ b/parser/zm_lib/cmd_lib.go @@ -0,0 +1,14 @@ +package zm_lib + +import ( + "complie-erlang/cache" + "fmt" + "github.com/spf13/cobra" +) + +func PersistentFlagsStringVar(cmd *cobra.Command, varK *string, cache cache.Cache, globalKey string, defaultValue string, desc string) error { + pluginKey := fmt.Sprintf("%s.%s", cmd.Use, globalKey) + plugin, err := cache.GetDefault(pluginKey, defaultValue) + cmd.PersistentFlags().StringVar(varK, globalKey, plugin.(string), fmt.Sprintf("%s (global.key:\"%s\")", desc, pluginKey)) + return err +} diff --git a/parser/zm_lib/public_tools.go b/parser/zm_lib/public_tools.go index a5eb46c..568e2c6 100644 --- a/parser/zm_lib/public_tools.go +++ b/parser/zm_lib/public_tools.go @@ -67,6 +67,16 @@ func FindPathByBasePath(basefun func() (basePath string, err error), path string if err != nil { return "", err } - path = filepath.Join(filepath.Dir(basePath), path) + + info, err := os.Stat(basePath) + if err != nil { + return "", err + } + if !info.IsDir() { + path = filepath.Join(filepath.Dir(basePath), path) + } else { + path = filepath.Join(basePath, path) + } + return path, nil }