From 10a9e63d3e505fc420412443b0eacd113ffea2bd Mon Sep 17 00:00:00 2001 From: guonaihong Date: Wed, 20 Dec 2023 16:57:30 +0800 Subject: [PATCH] =?UTF-8?q?codemsg=E6=A8=A1=E5=9D=97=E5=8F=AF=E4=BB=A5?= =?UTF-8?q?=E6=8A=8A=E6=84=9F=E5=85=B4=E8=B6=A3=E7=9A=84=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=E6=8F=90=E5=8F=96=E5=88=B0map=E9=87=8C=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- codemsg.md | 37 ++++++++++++ codemsg/codemsg.go | 10 ++-- codemsg/stringer.go | 111 +++++++++++------------------------- codemsg/take_to_map.go | 73 ++++++++++++++++++++++++ codemsg/take_to_map_test.go | 74 ++++++++++++++++++++++++ testdata/err.go | 4 +- 6 files changed, 226 insertions(+), 83 deletions(-) create mode 100644 codemsg/take_to_map.go create mode 100644 codemsg/take_to_map_test.go diff --git a/codemsg.md b/codemsg.md index be6e61c..522cb22 100644 --- a/codemsg.md +++ b/codemsg.md @@ -3,6 +3,13 @@ 只要实现code,自动生成String()类型和CodeMsg类型。可以在错误码代码节约时间 1.给每个ErrNo类型生成String()方法, 内容就是注释里面的。 2.给每个ErrNo类型生成CodeMsg{}结构 + +### 一、生成code msg代码 +1.1 使用命令 +```go +h2o codemsg --code-msg --linecomment --type ErrNo ./testdata/err.go +``` +1.2 code msg定义例子 ```go // 这段代码是我们要写的。 package demo @@ -85,3 +92,33 @@ var ( // 如果要生成grpc的错误就用下面的命令 // h2o codemsg --code-msg --linecomment --type ErrNo ./testdata/err.go --grpc --string-method string2 --string ``` + +### 二、提取某些code到指定的map里面 +主要是过滤一些code,比如对错误进行分级的使用场景, 有些错误非服务端引起。需要忽略这些错误 +2.1 命令 +```go +h2o codemsg --code-msg --linecomment --type ErrNo --take-code-to-map ./testdata/err.go +``` + +2.2 code msg定义例子, 提取感兴趣的code到map里面 +```go +// 这段代码是我们要写的。 +package demo + +type ErrNo int32 // 这里的类型可以自定义,--type后面类型 + +const ( + ENo ErrNo = 1003 // @TakeCodeToMap(InfoMap) 号码出错 + + ENotFound ErrNo = 1004 //@TakeCodeToMap(InfoMap) 找不到 +) + +``` + +生成的代码 +```go +package demo + +var InfoMap = map[int]bool{1003: true, 1004: true} + +``` diff --git a/codemsg/codemsg.go b/codemsg/codemsg.go index 07f92b7..5b8cc50 100644 --- a/codemsg/codemsg.go +++ b/codemsg/codemsg.go @@ -53,6 +53,9 @@ func genCodeMsg(c *CodeMsg) { for _, typeName := range types { g.generateCodeMsg(c, typeName) + for mapName, m := range c.SaveCodeToMap { + saveTakeFileMap(dir, g.pkg.name, typeName, mapName, m) + } } saveFile(c, dir, g.format(), "_codemsg.go", types[0]) @@ -62,14 +65,13 @@ func genCodeMsg(c *CodeMsg) { } func saveFile(c *CodeMsg, dir string, src []byte, suffix string, types string) { - outputName := c.Output if outputName == "" { baseName := fmt.Sprintf("%s%s", types, suffix) outputName = filepath.Join(dir, strings.ToLower(baseName)) } - err := ioutil.WriteFile(outputName, src, 0644) + err := ioutil.WriteFile(outputName, src, 0o644) if err != nil { log.Fatalf("writing output: %s", err) } @@ -77,7 +79,6 @@ func saveFile(c *CodeMsg, dir string, src []byte, suffix string, types string) { // generate produces the String method for the named type. func (g *Generator) generateCodeMsg(c *CodeMsg, typeName string) { - values := make([]Value, 0, 100) for _, file := range g.pkg.files { @@ -85,6 +86,7 @@ func (g *Generator) generateCodeMsg(c *CodeMsg, typeName string) { // Set the state for this run of the walker. file.typeName = typeName file.values = nil + file.c = c if file.file != nil { ast.Inspect(file.file, file.genDecl) values = append(values, file.values...) @@ -100,7 +102,7 @@ func (g *Generator) generateCodeMsg(c *CodeMsg, typeName string) { tmpl.Args = strings.Join(os.Args[2:], " ") tmpl.PkgName = g.pkg.name - //tmpl.Gen(os.Stdout) + // tmpl.Gen(os.Stdout) if err := tmpl.Gen(&g.buf); err != nil { io.Copy(os.Stdout, bytes.NewReader(g.buf.Bytes())) } diff --git a/codemsg/stringer.go b/codemsg/stringer.go index e9d3a0a..0560dc5 100644 --- a/codemsg/stringer.go +++ b/codemsg/stringer.go @@ -95,15 +95,16 @@ type CodeMsg struct { Grpc bool `clop:"long" usage:"Generate grpc error type"` StringMethod string `clop:"long" usage:"String function name" default:"String"` String bool `clop:"long" usage:"Generate string function"` - CodeMsgStructName string `clop:"long" usage:"turn on the ability to generate codemsg code" default:"CodeMsg"` //TODO + CodeMsgStructName string `clop:"long" usage:"turn on the ability to generate codemsg code" default:"CodeMsg"` // TODO - CodeName string `clop:"long" usage:"set new code name" default:"Code"` - MsgName string `clop:"long" usage:"set new message name" default:"Message"` - Args []string `clop:"args=file" usage:"file or dir" default:"["."]"` + CodeName string `clop:"long" usage:"set new code name" default:"Code"` + MsgName string `clop:"long" usage:"set new message name" default:"Message"` + TakeCodeToMap bool `clop:"long" usage:"Enable the take to map feature"` + Args []string `clop:"args=file" usage:"file or dir" default:"["."]"` + SaveCodeToMap map[string]map[int]bool } func genString(c *CodeMsg) { - log.SetFlags(0) log.SetPrefix("h2o codemsg: ") @@ -187,6 +188,7 @@ type File struct { trimPrefix string lineComment bool + c *CodeMsg } type Package struct { @@ -239,7 +241,6 @@ func (g *Generator) addPackage(pkg *packages.Package) { // generate produces the String method for the named type. func (g *Generator) generate(typeName string, c *CodeMsg) { - values := make([]Value, 0, 100) for _, file := range g.pkg.files { @@ -247,6 +248,10 @@ func (g *Generator) generate(typeName string, c *CodeMsg) { // Set the state for this run of the walker. file.typeName = typeName file.values = nil + file.c = c + if file.c == nil { + panic("codemsg is nil") + } if file.file != nil { ast.Inspect(file.file, file.genDecl) values = append(values, file.values...) @@ -450,82 +455,34 @@ func (f *File) genDecl(node ast.Node) bool { } else { v.Name = strings.TrimPrefix(v.OriginalName, f.trimPrefix) } - f.values = append(f.values, v) - } - } - return false -} - -// Helpers - -// usize returns the number of bits of the smallest unsigned integer -// type that will hold n. Used to create the smallest possible slice of -// integers to use as indexes into the concatenated strings. -func usize(n int) int { - switch { - case n < 1<<8: - return 8 - case n < 1<<16: - return 16 - default: - // 2^32 is enough constants for anyone. - return 32 - } -} - -// declareIndexAndNameVars declares the index slices and concatenated names -// strings representing the runs of values. -func (g *Generator) declareIndexAndNameVars(runs [][]Value, typeName string) { - var indexes, names []string - for i, run := range runs { - index, name := g.createIndexAndNameDecl(run, typeName, fmt.Sprintf("_%d", i)) - if len(run) != 1 { - indexes = append(indexes, index) - } - names = append(names, name) - } - g.Printf("const (\n") - for _, name := range names { - g.Printf("\t%s\n", name) - } - g.Printf(")\n\n") - if len(indexes) > 0 { - g.Printf("var (") - for _, index := range indexes { - g.Printf("\t%s\n", index) - } - g.Printf(")\n\n") - } -} + if f.c == nil { + panic("f.c is nil") + } -// declareIndexAndNameVar is the single-run version of declareIndexAndNameVars -func (g *Generator) declareIndexAndNameVar(run []Value, typeName string) { - index, name := g.createIndexAndNameDecl(run, typeName, "") - g.Printf("const %s\n", name) - g.Printf("var %s\n", index) -} + if f.c.TakeCodeToMap { + mapName := "" + var err error + v.Name, mapName, err = parseTakeCodeToMap(v.Name) + if err != nil { + panic(err) + } + if f.c.SaveCodeToMap == nil { + f.c.SaveCodeToMap = make(map[string]map[int]bool) + } + + saveMap := f.c.SaveCodeToMap[mapName] + if saveMap == nil { + saveMap = make(map[int]bool) + } + saveMap[int(v.value)] = true + f.c.SaveCodeToMap[mapName] = saveMap + } -// createIndexAndNameDecl returns the pair of declarations for the run. The caller will add "const" and "var". -func (g *Generator) createIndexAndNameDecl(run []Value, typeName string, suffix string) (string, string) { - b := new(bytes.Buffer) - indexes := make([]int, len(run)) - for i := range run { - b.WriteString(run[i].Name) - indexes[i] = b.Len() - } - nameConst := fmt.Sprintf("_%s_name%s = %q", typeName, suffix, b.String()) - nameLen := b.Len() - b.Reset() - fmt.Fprintf(b, "_%s_index%s = [...]uint%d{0, ", typeName, suffix, usize(nameLen)) - for i, v := range indexes { - if i > 0 { - fmt.Fprintf(b, ", ") + f.values = append(f.values, v) } - fmt.Fprintf(b, "%d", v) } - fmt.Fprintf(b, "}") - return b.String(), nameConst + return false } // declareNameVars declares the concatenated names string representing all the values in the runs. diff --git a/codemsg/take_to_map.go b/codemsg/take_to_map.go new file mode 100644 index 0000000..cfcc6e8 --- /dev/null +++ b/codemsg/take_to_map.go @@ -0,0 +1,73 @@ +package codemsg + +import ( + "bytes" + "fmt" + "go/format" + "os" + "path/filepath" + "strings" + "text/template" +) + +const ( + takeToMapStart = "@TakeCodeToMap(" + takeToMapEnd = ")" +) + +// text样子是 @take_to_map(map名字) +func parseTakeCodeToMap(text string) (newText, mapName string, err error) { + start := strings.Index(text, takeToMapStart) + if start == -1 { + return text, "", nil + } + + end := strings.Index(text[start+len(takeToMapStart):], takeToMapEnd) + if end == -1 { + return text, "", nil + } + + mapName = text[start+len(takeToMapStart) : start+len(takeToMapStart)+end] + + old := text[start : start+len(takeToMapStart)+end+1] + newText = strings.ReplaceAll(text, old, "") + return newText, mapName, nil +} + +func saveTakeFileMap(dir, packageName, types string, mapName string, m map[int]bool) { + baseName := fmt.Sprintf("%s_take_to_map_%s.go", types, mapName) + fileName := filepath.Join(dir, strings.ToLower(baseName)) + + const tmpl = `package {{.PackageName}} + var {{.MapName}} = {{printf "%#v" .MyMap}}` + tmplParsed, err := template.New("example").Parse(tmpl) + if err != nil { + panic(err) + } + + // 渲染模板到一个缓冲区 + var buf bytes.Buffer + + data := struct { + PackageName string + MapName string + MyMap map[int]bool + }{ + MapName: mapName, + MyMap: m, + PackageName: packageName, + } + err = tmplParsed.Execute(&buf, data) + if err != nil { + panic(err) + } + sourceCode, err := format.Source(buf.Bytes()) + if err != nil { + panic(err) + } + + err = os.WriteFile(fileName, sourceCode, 0o644) + if err != nil { + panic(err) + } +} diff --git a/codemsg/take_to_map_test.go b/codemsg/take_to_map_test.go new file mode 100644 index 0000000..1d9151d --- /dev/null +++ b/codemsg/take_to_map_test.go @@ -0,0 +1,74 @@ +package codemsg + +import ( + "bytes" + "fmt" + "testing" + "text/template" +) + +type test_TakeCodeToMap struct { + data string + needData string + mapName string +} + +func Test_TakeCodeToMap(t *testing.T) { + t.Run("test 成功的情况中", func(t *testing.T) { + for _, v := range []test_TakeCodeToMap{ + {"@TakeCodeToMap(mapName)", "", "mapName"}, + {"@TakeCodeToMap(mapName) 鉴权失败", " 鉴权失败", "mapName"}, + {"鉴权失败 @TakeCodeToMap(mapName)", "鉴权失败 ", "mapName"}, + {"鉴权失败 @TakeCodeToMap(mapName) 鉴权失败的情况有3种", "鉴权失败 鉴权失败的情况有3种", "mapName"}, + } { + newData, newMapName, err := parseTakeCodeToMap(v.data) + if err != nil { + t.Errorf("parseTakeCodeToMap(%s) error(%v)", v.data, err) + } + + if newData != v.needData { + t.Errorf("parseTakeCodeToMap[%s] newData[%s] needData[%s]", v.data, newData, v.needData) + } + + if newMapName != v.mapName { + t.Errorf("parseTakeCodeToMap[%s] newMapName[%s] mapName[%s]", v.data, newMapName, v.mapName) + } + } + }) +} + +func Test_2(t *testing.T) { + // 创建一个map[int]bool + myMap := map[int]bool{ + 1: true, + 2: true, + } + + // 准备数据 + data := struct { + MapName string + MyMap map[int]bool + }{ + MapName: "myMap", + MyMap: myMap, + } + + // 定义模板 + const tmpl = `var {{.MapName}} = {{printf "%#v" .MyMap}}` + + // 解析模板 + tmplParsed, err := template.New("example").Parse(tmpl) + if err != nil { + panic(err) + } + + // 渲染模板到一个缓冲区 + var buf bytes.Buffer + err = tmplParsed.Execute(&buf, data) + if err != nil { + panic(err) + } + + // 输出渲染结果 + fmt.Println(buf.String()) +} diff --git a/testdata/err.go b/testdata/err.go index b8e5aae..0dfe6a5 100644 --- a/testdata/err.go +++ b/testdata/err.go @@ -3,7 +3,7 @@ package demo type ErrNo int32 const ( - ENo ErrNo = 1003 // 号码出错 + ENo ErrNo = 1003 // 号码出错 @TakeCodeToMap(InfoMap) - ENotFound ErrNo = 1004 // 找不到 + ENotFound ErrNo = 1004 // 找不到 @TakeCodeToMap(InfoMap) )