diff options
author | Patrick Steinhardt <psteinhardt@gitlab.com> | 2022-05-02 10:39:20 +0300 |
---|---|---|
committer | Patrick Steinhardt <psteinhardt@gitlab.com> | 2022-05-06 11:44:31 +0300 |
commit | ba661eea55984bd4ab0c00f15c2dfa07efb07cb7 (patch) | |
tree | b6231a2ca5a91507c7f291877ae6fbba76963ca9 /tools/protoc-gen-gitaly-protolist | |
parent | 1466804c6e8e1cd5e237349998e0fca370ca3855 (diff) |
tools: Move Protoc plugins into top-level `tools/` directory
The Protoc plugins we use are hidden away deep into the `proto/`
directory, which makes it very hard to discover them when one doesn't
already know about their existence. Let's move them into a new top-level
`tools/` directory.
Diffstat (limited to 'tools/protoc-gen-gitaly-protolist')
-rw-r--r-- | tools/protoc-gen-gitaly-protolist/main.go | 139 |
1 files changed, 139 insertions, 0 deletions
diff --git a/tools/protoc-gen-gitaly-protolist/main.go b/tools/protoc-gen-gitaly-protolist/main.go new file mode 100644 index 000000000..7e775360e --- /dev/null +++ b/tools/protoc-gen-gitaly-protolist/main.go @@ -0,0 +1,139 @@ +// Command protoc-gen-gitaly-protolist is designed to be used as a protobuf compiler to generate a +// list of protobuf files via a publicly accessible variable. +// +// This plugin can be accessed by invoking the protoc compiler with the following arguments: +// +// protoc --plugin=protoc-gen-gitaly-protolist --gitaly_protolist_out=. +// +// The plugin accepts a protobuf message in STDIN that describes the parsed protobuf files. A +// response is sent back on STDOUT that contains any errors. +package main + +import ( + "bytes" + "fmt" + "go/format" + "io" + "log" + "os" + "path/filepath" + "strings" + "text/template" + + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/pluginpb" +) + +const ( + gitalyProtoDirArg = "proto_dir" + gitalypbDirArg = "gitalypb_dir" +) + +func main() { + data, err := io.ReadAll(os.Stdin) + if err != nil { + log.Fatalf("reading input: %s", err) + } + + req := &pluginpb.CodeGeneratorRequest{} + + if err := proto.Unmarshal(data, req); err != nil { + log.Fatalf("parsing input proto: %s", err) + } + + if err := generateProtolistGo(req); err != nil { + log.Fatal(err) + } +} + +func parseArgs(argString string) (gitalyProtoDir string, gitalypbDir string) { + for _, arg := range strings.Split(argString, ",") { + argKeyValue := strings.Split(arg, "=") + if len(argKeyValue) != 2 { + continue + } + switch argKeyValue[0] { + case gitalyProtoDirArg: + gitalyProtoDir = argKeyValue[1] + case gitalypbDirArg: + gitalypbDir = argKeyValue[1] + } + } + + return gitalyProtoDir, gitalypbDir +} + +func generateProtolistGo(req *pluginpb.CodeGeneratorRequest) error { + var err error + gitalyProtoDir, gitalypbDir := parseArgs(req.GetParameter()) + + if gitalyProtoDir == "" { + return fmt.Errorf("%s not provided", gitalyProtoDirArg) + } + if gitalypbDir == "" { + return fmt.Errorf("%s not provided", gitalypbDirArg) + } + + var protoNames []string + + if gitalyProtoDir, err = filepath.Abs(gitalyProtoDir); err != nil { + return fmt.Errorf("failed to get absolute path for %s: %v", gitalyProtoDir, err) + } + + files, err := os.ReadDir(gitalyProtoDir) + if err != nil { + return fmt.Errorf("failed to read %s: %v", gitalyProtoDir, err) + } + + for _, fi := range files { + if !fi.IsDir() && strings.HasSuffix(fi.Name(), ".proto") { + protoNames = append(protoNames, fmt.Sprintf(`"%s"`, fi.Name())) + } + } + + f, err := os.Create(filepath.Join(gitalypbDir, "protolist.go")) + if err != nil { + return fmt.Errorf("could not create protolist.go: %v", err) + } + defer f.Close() + + if err = renderProtoList(f, protoNames); err != nil { + return fmt.Errorf("could not render go code: %v", err) + } + + return nil +} + +// renderProtoList generate a go file with a list of gitaly protos +func renderProtoList(dest io.WriteCloser, protoNames []string) error { + joinFunc := template.FuncMap{"join": strings.Join} + protoList := `package gitalypb + // Code generated by protoc-gen-gitaly. DO NOT EDIT + + // GitalyProtos is a list of gitaly protobuf files + var GitalyProtos = []string{ + {{join . ",\n"}}, + } + ` + protoListTempl, err := template.New("protoList").Funcs(joinFunc).Parse(protoList) + if err != nil { + return fmt.Errorf("could not create go code template: %v", err) + } + + var rawGo bytes.Buffer + + if err := protoListTempl.Execute(&rawGo, protoNames); err != nil { + return fmt.Errorf("could not execute go code template: %v", err) + } + + formattedGo, err := format.Source(rawGo.Bytes()) + if err != nil { + return fmt.Errorf("could not format go code: %v", err) + } + + if _, err = io.Copy(dest, bytes.NewBuffer(formattedGo)); err != nil { + return fmt.Errorf("failed to write protolist.go file: %v", err) + } + + return nil +} |