#### 源码--GitHub:<a href="https://github.com/af913337456/ErrorDocAutoPrinter">https://github.com/af913337456/ErrorDocAutoPrinter</a>
如果你是一个`后端Server程序`开发人员。你应该知道,在你写完`API`之后,是需要给`客户端`的同学提供`调用`文档的。
例如下面一个`api handler`,`创建一个用户`。
```golang
func HandleCreateUser(w http.ResponseWriter,r *http.Request) map[string]interface{} {
if r.Body == nil {
return util.GetCommonErr(23,"create user req body is fucking null?")
}
....
....
return util.GetCommonSuccess("success")
}
```
上面有一行`错误信息输出的代码`
```golang
util.GetCommonErr(23,"create user req body is fucking null?")
```
假设我们要写成`markDown风格的`文档,上面的可能是这样一种对应
| 错误码 | 含义 |提示|
| - | - | - |
|23|create user req body is fucking null?|暂无|
Ok,这只是一个错误信息的情况,我们何况就`手动`写完了。
如果有`几百上千个`呢?一个完整的服务端程序,肯定会有很多这种`错误信息输出`的代码。在`几百上千个`的时候,还要手动写?这是多么`低效率`,且`浪费时间`的`令人窒息`的操作。
## 而我这篇文章要介绍的就是`一个帮你自动检索并生成API输出错误信息文档`的`开源程序`
# `ErrorDocAutoPrinter`
### 它,具备下面的特点
* 自定义代码文件夹路径
* `Json` 配置文件形式导入设置,避免反复编译程序
* 按照给定的`代码方法名称`自动检索对应的代码行
* 按照给定的`切割参数规则`,自动切割组合
* 按照给定的`列名描述`,自动组合成新的文字
* 接口化的设计逻辑,高度自定义
* 自动按照`code` 从小到大排序输出,可控!
* 自动提示`重复出现过`的错误信息。
* 自动按照设定生成输出文件
* 可设置`符合目标`的文件`后缀`
* 可设置`需要过滤`的文件名,符合就不处理
* 自定义输出风格,`markDown`?`txt`?`html`?
* 自行定义输出的逻辑,可以映射到很多情况的文字玩法
* 总之:‘为所欲为’
### 我,提供了两种风格的输出
* `简单文本` 风格
* `markDown` 风格
### 使用步骤
1. 配置好`json`文件 `DefaultConfig.json`
```json
{
"TargetFileSuffix":[".go"],
"TargetErrorFuncName":["util.GetCommonErr","util.GetErrWithTips"],
"FilterFileName":["core"],
"ParamsColumnNames":[" 错 误 码 "," 含 义 ","提 示"],
"ParamsSplitChar":","
}
```
2. 输入你的`代码文件夹路径`并运行程序
```golang
func TestDocPrinter(t *testing.T) {
p := NewDefaultErrorDocPrinter(NewDefaultMarkDownErrorDocPrinter())
if p == nil {
return
}
fmt.Println(p.printErrorDoc("../../errorDocPrinter"))
}
```
3. 复制粘贴`结果`
| 错误码 | 含义 |提示|
| - | - | - |
|-9|invalid create user|--空缺--|
|-4|invalid create user|--空缺--|
|-1|create user failed|创建用户失败|
|88|创建评论失败|--空缺--|
|3110|error params|--空缺--|
|3111|update failed|--空缺--|
|3112|yellow 内容涉黄|--空缺--|
|3113|forbid 禁止访问|--空缺--|
|3114|empty id|--空缺--|
|3115|服务端开启事务失败|--空缺--|
|3116|服务端事务提交失败|--空缺--|
|3117|update effect row <= 0|--空缺--|
|3118|RowsAffected 失败|--空缺--|
|3119|更新只有部分成功|--空缺--|
|3120|empty userId|--空缺--|
|3121|too lager|--空缺--|
|3122|user not exits|--空缺--|
|3123|非法更新|--空缺--|
|3124|参数个数长度限制|--空缺--|
|3126|服务端事务提交失败|--空缺--|
|3127|invalid money|--空缺--|
|3128|money not enough|--空缺--|
|3129|创建消费记录失败|--空缺--|
##### 基本说完了,源码见上面的开源链接,去玩吧。
### 简单分析下 `markDown` 风格的生成
接口
```golang
type IErrorDocPrinter interface {
FindLines(printer *ErrorDocPrinter,reader *bufio.Reader,fileName,currentSuffix string,handleLine func(line string)) []string
BuildACell(printer ErrorDocPrinter,columns,size int,prefixName,param string) string
ResultLine(line string)
EndOfAFile(printer ErrorDocPrinter,aFileRetLines []string)
EndOfAllFile(printer ErrorDocPrinter,allRetLines []string)
}
```
#### 找到一个文件所有行
```golang
func (p MarkDownErrorDocPrinter) FindLines(
printer *ErrorDocPrinter,reader *bufio.Reader,fileName,currentSuffix string,handleLine func(line string)) []string {
// 正则匹配 todo
var lines []string
printer.currentLineNum = 0
for {
byt, _, err := reader.ReadLine()
if err != nil {
// 读完一个文件
break
}
line := string(byt)
// 排除注释
printer.currentLineNum++
if startWith(line,"//") {
continue
}
if startWith(line,"/*") {
continue
}
if startWith(line,"*") {
continue
}
for _,value := range printer.TargetErrorFuncName {
if strings.Contains(line,value) {
// hit,准备生成
handleLine(line)
lines = append(lines,line)
}
}
}
return lines
}
```
#### 处理一个单元格
```golang
func (p MarkDownErrorDocPrinter) BuildACell(
printer ErrorDocPrinter,columns,size int,prefixName,param string) string {
/**
| Name | Academy | score |
| - | - | - |
| Harry Potter | Gryffindor| 90 |
| Hermione Granger | Gryffindor | 100 |
| Draco Malfoy | Slytherin | 90 |
*/
if columns == 0 {
code,err := strconv.ParseInt(param,10,64)
if err == nil {
codeArr = append(codeArr,code)
}
return "|" + param
}
count := tipsMap[param]
if columns == 1 {
// 保存提示列
if count != 0 {
count++
diffMap[fmt.Sprintf("param: -- %s -- times:%d",param,count-1)] =
fmt.Sprintf(" 与 %s 的第 %d 行提示重复",printer.currentFileName,printer.currentLineNum)
}else{
count = 1
}
tipsMap[param] = count
}
if columns == size - 1 {
return "|" + param + "|"
}
// 找出提示一样,但是 code 不一样的
return "|" + param
}
```
### 从小到大排序--code
```golang
func quickSort(arr *[]int64,left,right int) {
if arr == nil {
return
}
if right == len(*arr) {
right--
}
if left < 0 || left >= len(*arr) {
return
}
hight := right
low := left
base := (*arr)[left]
if low < hight {
for ;low < hight; {
for ;low < hight && base <= (*arr)[hight]; {
hight--
break
}
(*arr)[low] = (*arr)[hight]
for ;low < hight && base >= (*arr)[low]; {
low++
break
}
(*arr)[hight] = (*arr)[low]
}
(*arr)[low] = base
quickSort(arr,left,low-1)
quickSort(arr,low+1,right)
}
}
```
#### 组装
```golang
quickSort(&codeArr,0,len(codeArr))
codeArrSize := len(codeArr)
for i:=0; i<codeArrSize ;i++ {
codeAtr := strconv.Itoa((int)(codeArr[i]))
index := 0
for _,line := range allRetLines {
if strings.Contains(line,"|"+codeAtr+"|") {
final = append(final,line)
// 减去一个,减少循环次数
//retLines = append(retLines[:index],retLines[index+1:]...)
index--
break
}
index++
}
}
// 生成文件
fileName := "errorInfo.md"
file,err := os.Create(fileName)
defer file.Close()
if err!=nil {
fmt.Println(err)
}
for _,line := range final {
fmt.Println(line)
file.WriteString(line+"\n")
}
```
## 如果编程不是为了让复杂的问题简单化,那和机械学习有什么区别?