关于go:go如何从零编写protoBuf-插件

11次阅读

共计 3574 个字符,预计需要花费 9 分钟才能阅读完成。

本期的次要内容将手把手教会大家,编写 probuf 的 go 插件,以我本人编写的一个生成构造体的插件为例子。我的项目位于 https://github.com/hisheng/pr…

一、自定义 ProtoBuf 插件介绍

咱们罕用的 go 反对 protobuf 插件有

插件名称 介绍
protoc-gen-go 通过.proto 文件生成.pb.go 文件
protoc-gen-doc 通过.proto 文件生成文档
protoc-gen-go-errors 通过.proto 文件生成 error
protoc-gen-go-errors 通过.proto 文件生成 error
protoc-gen-go-grpc 通过.proto 文件生成 grpc
protoc-gen-go-http 通过.proto 文件生成 http
protoc-gen-openapi 通过.proto 文件生成 openapi
protoc-gen-validate 通过.proto 文件生成 validate 验证
protoc-gen-go-enum 通过.proto 文件生成 go 自定义枚举

基本上该有的插件,都曾经有写过了,所以咱们能够查看他人的源码,来察看怎么来写一个插件。

二、手把手写自定义插件(以 protoc-gen-go-struct 为例)

2.1 新建 protoc-gen-go-struct 文件夹, 并且进入到这个文件夹里。

mkdir protoc-gen-go-struct && cd protoc-gen-go-struct

2.2 新建 go module 我的项目

咱们执行 go mod init modName 命名来生成我的项目如下:

go mod init github/hisheng/protoc-gen-go-struct

此时咱们查看文件夹发现生产了一个 go.mod 文件,查看一下代码如下:

module github/hisheng/protoc-gen-go-struct

go 1.19

2.3 写 main 函数

咱们在我的项目根目录,写一个 main.go 如下

touch main.go

此时咱们发现我的项目根目录下,生成了一个 main.go 文件。
咱们写一个 main 函数,代码如下:

package main

import ("google.golang.org/protobuf/compiler/protogen")

func main() {protogen.Options{}.Run(func(gen *protogen.Plugin) error {
        for _, f := range gen.Files {
            if !f.Generate {continue}
            generateFile(gen, f)
        }
        return nil
    })
}

这个 main()函数大家能够间接复制,根本所有的插件都是这样的格局,当然这个 main 也能够承受参数,这里咱们简化,先不介绍,感兴趣的人,能够参考 protoc-gen-go 的 main 函数来写。
咱们本人写的办法次要是 generateFile(gen, f) 这个函数。
这个函数用来读取.proto 文件,并且生产 go 文件。

2.4 自定义 generateFile(gen, f)函数

这个函数全称是 generateFile(gen protogen.Plugin, file protogen.File),承受的两个参数

gen *protogen.Plugin 为生成的插件,次要用来生成 go 文件
file *protogen.File 为.proto 文件对他的 file 对象

我这里的次要代码是:

// 生成.struct.go 文件,参数为 输入插件 gen,以及读取的文件 file
func generateFile(gen *protogen.Plugin, file *protogen.File) {
    filename := file.GeneratedFilenamePrefix + ".struct.go"
    g := gen.NewGeneratedFile(filename, file.GoImportPath)
    // 输入 package packageName
    g.P("package", file.GoPackageName)
    g.P() // 换行

    for _, m := range file.Messages {
        // 输入 type m.GoIdent struct {g.P("type", m.GoIdent, "struct {")
        for _, field := range m.Fields {leadingComment := field.Comments.Leading.String()
            trailingComment := field.Comments.Trailing.String()

            line := fmt.Sprintf("%s %s `json:\"%s\"` %s", field.GoName, field.Desc.Kind(), field.Desc.JSONName(), trailingComment)
            // 输入 行首正文
            g.P(leadingComment)
            // 输入 行内容
            g.P(line)
        }
        // 输入 }
        g.P("}")
    }
    g.P() // 换行}

就是如此简略的 10 几行代码,就能够读取.proto 文件,并生成.go 文件了。接下来咱们具体的介绍一下外面次要的变量以及对象。

2.4.1 第一步学生成 GeneratedFile 对象
filename := file.GeneratedFilenamePrefix + ".struct.go"
g := gen.NewGeneratedFile(filename, file.GoImportPath) 
2.4.2 第二步生成 go 代码的 package
// 输入 package packageName
g.P("package", file.GoPackageName)
g.P() // 换行
2.4.3 第三步便当 proto 文件的 message

因为咱们这里是找 message 而后生成 struct,所以应用 file.Messages 来获取所有的 message,而后遍历

    for _, m := range file.Messages {
        // 输入 type m.GoIdent struct {g.P("type", m.GoIdent, "struct {")
        for _, field := range m.Fields {leadingComment := field.Comments.Leading.String()
            trailingComment := field.Comments.Trailing.String()

            line := fmt.Sprintf("%s %s `json:\"%s\"` %s", field.GoName, field.Desc.Kind(), field.Desc.JSONName(), trailingComment)
            // 输入 行首正文
            g.P(leadingComment)
            // 输入 行内容
            g.P(line)
        }
        // 输入 }
        g.P("}")
    }

这一步代码次要生产 go 的 struct,生成后的样子如下:

type User struct {
    // xingming
    Name string `json:"name"` // 姓名
    // age
    Age int64 `json:"age"` // 年龄
}

三、编译插件

咱们把这个代码,在本地编译装置,在我的项目根目录执行 go install

go install

此时咱们到本人的 GOPATH 目录查看是否生成
咱们 cd 到 $GOPATH 的 bin 目录,个别 go install 装置的命令都在这里

cd  $GOPATH/bin && ls -al

咱们看到了 protoc-gen-go-struct 二进制命令。

四、protoc 应用 protoc-gen-go-struct 插件

咱们在其余中央写测试方法,写一个 pt.proto 文件

mkdir protoc_struct && cd protoc_struct && touch pt.proto

而后把 pt.ptoto 外面写入代码

syntax = "proto3";
package pt;
option go_package = "./protoc_struct";

message User {
  // xingming
  string name = 1;// 姓名
  // age
  int64 age = 2;// 年龄
}

最初咱们在执行 protoc 命令

protoc --go-struct_out=./ pt.proto

此时咱们发现生产了 pt.struct.go 文件
文件外面生成的 go 代码如下

package protoc_struct

type User struct {
    // xingming
    Name string `json:"name"` // 姓名
    // age
    Age int64 `json:"age"` // 年龄
}

和下面的.proto 文件一一对应。
写到这里,咱们本人写一个 go 对应的 protobuf 插件就实现。

本文参加了思否技术征文,欢送正在浏览的你也退出。

正文完
 0