Zephus 发表于 2022-12-10 19:00

听说你还在自己做重复劳动?看我一键生成错误码映射


generate.jpg

大家在工作中定义错误码的时候都是如何处理的? xdm 可以评论区交流交流

我看到过有的是这样定义错误码的:
m := make(mapstring)m = "OK"m = "链接失败"m = "文件类型错误"...
还看到过这样定义错误码的:
type myErr struct {    code   int    err    error    extMsg interface{}}myOk := myErr{0,errors.New("PROCESS OK"),"xxx"}myOk := myErr{1,errors.New("文件类型错误"),"xxx"}myOk := myErr{2,errors.New("连接数据库失败"),"xxx"}...
也有见到过这样做的:
const (    ERR_OK          = 0    ERR_CONN_REFUSE = 1    ERR_FILE_NOT    = 2)var m = mapstring{    ERR_OK:          "OK",    ERR_CONN_REFUSE: "链接被拒绝",    ERR_FILE_NOT:    "文件不存在",}
现在有一个更好的方法来实现我们工作中错误码的映射
引入 go generate


咱们引入 go generate ,可以只用定义错误码和写注释,就可以达到,当我们调用错误码的时候,能够正确的输出我们想要的错误信息

举个例子:

我们先建立如下目录,将错误码文件 errcode.go,放在一个单独的包里面
.├── go.mod├── main.go└── mycodes    └── errcode.go
我们还需要运用 stringer 工具,来辅助我们完成这个目标
go get golang.org/x/tools/cmd/stringer
我们来看看上述文件的内容:

./mycodes/errcode.go
/*_______   _____ ___________\   \///   \\__    ___/ \   //\ /\ |    | /   \ /    Y    \|    |/___/\\\____|__/|____|      \_/      \/createTime:2021/10/10createUser:Administrator*/package mycodestype ErrCode int64 //错误码const (      ERR_CODE_OK             ErrCode = 0 // PROCESS OK      ERR_CODE_INVALID_PARAMS ErrCode = 1 // 参数无效      ERR_CODE_TIMEOUT      ErrCode = 2 // 超时      ERR_CODE_FILE_NOT_EXIST ErrCode = 3 // 文件不存在      ERR_CODE_CONN_REFUSE    ErrCode = 4 // 连接被拒绝      ERR_CODE_NET_ABNORMAL   ErrCode = 5 // 网络异常)
main.go
/*_______   _____ ___________\   \///   \\__    ___/ \   //\ /\ |    | /   \ /    Y    \|    |/___/\\\____|__/|____|      \_/      \/createTime:2021/10/10createUser:Administrator*/package mainimport (      "fmt"      "myerr/mycodes")func main() {      fmt.Println(mycodes.ERR_CODE_CONN_REFUSE)}
我们在 main.go 统计目录下初始化一下 go 的 模块,go mod init myerr

go.mod
module myerrgo 1.15开始演示


我们直接在 main.go 的同级目录下执行 go run main.go,输出如下:
4
是 ERR_CODE_CONN_REFUSE 对应的枚举值 4 ,可是我们期望的课不是这个,我们是期望能直接输出错误码对应的错误信息
使用 stringer


手动在 mycodes 目录下使用刚才安装的stringer 工具
stringer -linecomment -type ErrCode
此处的 ErrCode 是错误码的类型 , 执行上述语句后,mycodes 生成了一个文件 errcode_string.go ,现在目录结构是这样的
.├── go.mod├── main.go└── mycodes    ├── errcode.go    └── errcode_string.go
看看 errcode_string.go 内容
// Code generated by "stringer -linecomment -type ErrCode"; DO NOT EDIT.package mycodesimport "strconv"const _ErrCode_name = "PROCESS OK参数无效超时文件不存在连接被拒绝网络异常"var _ErrCode_index = [...]uint8{0, 10, 22, 28, 43, 58, 70}func (i ErrCode) String() string {      if i < 0 || i >= ErrCode(len(_ErrCode_index)-1) {                return "ErrCode(" + strconv.FormatInt(int64(i), 10) + ")"      }      return _ErrCode_name:_ErrCode_index]}
我们可以看出 stringer 工具,将我们的错误码信息映射的字符串全部合并起来放在_ErrCode_name 常量中,且有_ErrCode_index来作为每一个错误码映射字符串的索引值 ,最终便能实现错误码和字符串的映射,这个就很简单吧
效果展示


此时,我们仍然在 main.go 的同级目录下执行 go run main.go,输出如下:
连接被拒绝
显示的正式我们期望的错误信息
stringer 工具


我们来看看 stringer 工具的帮助,在来详细学习一波
# stringer -hUsage of stringer:      stringer -type T       stringer -type T files... # Must be a single packageFor more information, see:      http://godoc.org/golang.org/x/tools/cmd/stringerFlags:-linecomment      use line comment text as printed text when present-output string      output file name; default srcdir/<type>_string.go-trimprefix prefix      trim the prefix from the generated constant names-type string      comma-separated list of type names; must be set
可以看到大的用法有 2 种:
stringer -type T stringer -type T files... # Must be a single package
第一种可以在类型 T 后面加上目录

第二种可以指定类型 T 后面指定明确的文件,但是这种方式必须是在一个单独的包里面

参数如下:
-linecomment

使用行注释文本作为打印文本
-output string

输出文件名称;默认源目录下的 / <类型> _string.go,所以我们可以看到例子中我们的输出文件在 mycodes 下的 errcode_string.go
-trimprefix prefix

从生成的常量名称中修剪前缀
-type string

以逗号分隔的类型名称列表,这个参数是必须要设置的
go generate


刚才我们是在命令行中,使用 stringer 工具来生成的,那么我们要把这些东西放入项目代码中就需要使用go generate 工具了

先大致了解一下 go generate 是个啥玩意

go generate 是 go 自带的一个工具,我们可以通过在命令行中查看到:
# go
image

咱们查看一下帮助文档 go help generate
# go help generate...Go generate scans the file for directives, which are lines ofthe form,      //go:generate command argument......Go generate sets several variables when it runs the generator:      $GOARCH                The execution architecture (arm, amd64, etc.)      $GOOS                The execution operating system (linux, windows, etc.)      $GOFILE                The base name of the file.      $GOLINE                The line number of the directive in the source file.      $GOPACKAGE                The name of the package of the file containing the directive.      $DOLLAR                A dollar sign.
上面这些是 go generate 使用时候的环境变量
$GOARCH

体系架构(arm、amd64 等)
$GOOS

当前的 OS 环境(linux、windows 等)
$GOFILE

当前处理中的文件名
$GOLINE

当前命令在文件中的行号
$GOPACKAGE

当前处理文件的包名
go generate命令格式

go generate [-run regexp] [-n] [-v] [-x] 参数说明如下:-run 正则表达式匹配命令行,仅执行匹配的命令;-v 输出被处理的包名和源文件名;-n 显示不执行命令;-x 显示并执行命令;command 可以是在环境变量 PATH 中的任何命令。generate 用法


上面帮助文档有体现,我们可以使用 //go:generate command argument...来讲 generate 工具用起来
实际案例


我们来简单的尝试一下

我们在刚才的 main.go 中加入 generate 的语句,使用 generate 执行,ls -alh
/*_______   _____ ___________\   \///   \\__    ___/ \   //\ /\ |    | /   \ /    Y    \|    |/___/\\\____|__/|____|      \_/      \/createTime:2021/10/10createUser:Administrator*/package main//go:generate ls -alhimport (      "fmt"      "myerr/mycodes")func main() {      fmt.Println(mycodes.ERR_CODE_CONN_REFUSE)}
在 main.go 同级目录下执行 go generate 看效果
# go generatetotal 20Kdrwxr-xr-x3 root root 4.0K Oct 10 17:30 .drwxr-xr-x 11 root root 4.0K Oct 10 16:25 ..-rw-r--r--1 root root   22 Oct 10 16:02 go.mod-rw-r--r--1 root root346 Oct 10 17:30 main.godrwxr-xr-x2 root root 4.0K Oct 10 17:13 mycodes
果然是调用 ls -alh 成功了
go generate + stringer 的使用


那么我们就把刚才我们实践的 stringer工具也加进去玩玩

此时目录是这样的
.├── go.mod├── main.go└── mycodes    └── errcode.go
main.go 是这样的
/*_______   _____ ___________\   \///   \\__    ___/ \   //\ /\ |    | /   \ /    Y    \|    |/___/\\\____|__/|____|      \_/      \/createTime:2021/10/10createUser:Administrator*/package main//go:generate stringer -linecomment -type ErrCode ./mycodesimport (      "fmt"      "myerr/mycodes")func main() {      fmt.Println(mycodes.ERR_CODE_CONN_REFUSE)}
没错我们加入了 //go:generate stringer -linecomment -type ErrCode ./mycodes

直接执行 go generate -x 来看效果吧
# go generate -xstringer -linecomment -type ErrCode ./mycodes
errcode_string.go 又生成了
.├── go.mod├── main.go└── mycodes    ├── errcode.go    └── errcode_string.go
执行 go run main.go 当然必须是我们想要的东西啦
# go run main.go连接被拒绝go generate 的使用规范

运行go generate命令时,才会执行特殊注释后面的命令特殊注释必须以//go:generate开头,双斜线后面没有空格该特殊注释必须在 .go 源码文件中每个源码文件可以包含多个 generate 特殊注释当go generate命令执行出错时,将终止程序的运行
最后说说 go generate 还能干些啥


go generate 能干的事情还真不少,只要是能够在 path 下面能找到的可执行程序,都可以放在 //go:generate 后面玩,一般使用 go generate 会有如下场景:
protobuf:从 protocol buffer 定义文件(.proto)生成 .pb.go 文件 , 这种情况 grpc 通信的时候常用yacc:从 .y 文件生成 .go 文件HTML:将 HTML 文件嵌入到 go 源码bindata:将形如 JPEG 这样的文件转成 go 代码中的字节数组Unicode:从 UnicodeData.txt 生成 Unicode 表

工具要用用起来才能体现它的价值
欢迎点赞,关注,收藏


朋友们,你的支持和鼓励,是我坚持分享,提高质量的动力

image

好了,本次就到这里

技术是开放的,我们的心态,更应是开放的。拥抱变化,向阳而生,努力向前行。

我是阿兵云原生,欢迎点赞关注收藏,下次见~
页: [1]
查看完整版本: 听说你还在自己做重复劳动?看我一键生成错误码映射