pax_global_header00006660000000000000000000000064151574346470014532gustar00rootroot0000000000000052 comment=f097f84c5bece9e7f0eda602dc24d17da3248947 golang-github-pseudomuto-protokit-0.2.1/000077500000000000000000000000001515743464700203345ustar00rootroot00000000000000golang-github-pseudomuto-protokit-0.2.1/.github/000077500000000000000000000000001515743464700216745ustar00rootroot00000000000000golang-github-pseudomuto-protokit-0.2.1/.github/CONTRIBUTING.md000066400000000000000000000027741515743464700241370ustar00rootroot00000000000000# Contributing to Protokit First off, glad you're here and want to contribute! :heart: ## Getting Started It's always good to start simple. Clone the repo and `make test` to make sure you're starting from a good place. ## Submitting a PR Here are some general guidelines for making PRs for this repo. 1. [Fork this repo](https://github.com/pseudomuto/protokit/fork) 1. Make a branch off of master (`git checkout -b `) 1. Make focused commits with descriptive messages 1. Add tests that fail without your code, and pass with it (`make test` is your friend) 1. GoFmt your code! (see to setup your editor to do this for you) 1. **Ping someone on the PR** (Lots of people, including myself, won't get a notification unless pinged directly) Every PR should have a well detailed summary of the changes being made and the reasoning behind them. I've added a PR template that should help with this. ## Code Guidelines I don't want to be too dogmatic about this, but here are some general things I try to keep in mind: * GoFmt all the things! * Imports are grouped into external, stdlib, internal groups in each file (see any go file in this repo for an example) - really just use `goimports` and be done with it. * Test are defined in `_test` packages to ensure only the public interface is tested. * If you export something, make sure you add appropriate godoc comments and tests. ## Tagging a Release * Ensure you're on a clean master * Run `make release` golang-github-pseudomuto-protokit-0.2.1/.github/PULL_REQUEST_TEMPLATE.md000066400000000000000000000010121515743464700254670ustar00rootroot00000000000000ping @pseudomuto ### What is Changing? _Make sure you spell out in as much detail as necessary what will happen to which systems when your PR is merged, what are the expected changes._ ### How is it Changing? _Include any relevant implementation details, mimize surprises for the reviewers in this section, if you had to take some unorthodox approaches (read hacks), explain why here._ ### What Could Go Wrong? _How has this change been tested? In your opinion what is the risk, if any, of merging these changes._ golang-github-pseudomuto-protokit-0.2.1/.github/workflows/000077500000000000000000000000001515743464700237315ustar00rootroot00000000000000golang-github-pseudomuto-protokit-0.2.1/.github/workflows/ci.yaml000066400000000000000000000007101515743464700252060ustar00rootroot00000000000000name: CI on: push: branches: [ master ] pull_request: branches: [ master ] jobs: test: strategy: matrix: go: [ '1.17.2' ] runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions/setup-go@v2 with: go-version: ${{ matrix.go }} - name: Tests run: | make test/ci - name: Upload coverage to Codecov uses: codecov/codecov-action@v2 golang-github-pseudomuto-protokit-0.2.1/.github/workflows/release.yaml000066400000000000000000000007761515743464700262470ustar00rootroot00000000000000name: Release on: push: tags: - '*' jobs: release: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v2 with: fetch-depth: 0 - name: Setup Go uses: actions/setup-go@v2 with: go-version: 1.17 - name: Release uses: goreleaser/goreleaser-action@v2 with: version: latest args: release --rm-dist env: GITHUB_TOKEN: ${{ secrets.GH_RELEASE_TOKEN }} golang-github-pseudomuto-protokit-0.2.1/.gitignore000066400000000000000000000000311515743464700223160ustar00rootroot00000000000000/bin /coverage.txt /dist golang-github-pseudomuto-protokit-0.2.1/.goreleaser.yaml000066400000000000000000000003221515743464700234230ustar00rootroot00000000000000project_name: protokit before: hooks: - go mod tidy builds: - skip: true checksum: name_template: 'checksums.txt' snapshot: name_template: "{{ incpatch .Version }}-next" changelog: sort: asc golang-github-pseudomuto-protokit-0.2.1/CHANGELOG.md000066400000000000000000000031311515743464700221430ustar00rootroot00000000000000# Changelog All noteworthy changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). ## [Unreleased](https://github.com/pseudomuto/protokit/compare/v0.2.0...master) ## [0.2.0](https://github.com/pseudomuto/protokit/compare/v0.1.0...v0.2.0) ### Added * Support for extended options in files, services, methods, enums, enum values, messages and fields. For example, the [`google.api.http` method option](https://cloud.google.com/service-infrastructure/docs/service-management/reference/rpc/google.api#httprule). ## [0.1.0](https://github.com/pseudomuto/protokit/compare/v0.1.0-pre3...v0.1.0) ### Added * `(Get)PackageComments` and `(Get)SyntaxComments` to `FileDescriptor` ### Deprecated * `Comments` and `GetComments()` from `FileDescriptor` ## [0.1.0-pre3](https://github.com/pseudomuto/protokit/compare/v0.1.0-pre2...v0.1.0-pre3) ### Added * `FileDescriptor{}.Imported` for referencing publically imported descriptors, enums, and extensions * `Comments{}.Get` to create empty comments (rather than `nil`) for all objects when a comment isn't defined. ## [0.1.0-pre2](https://github.com/pseudomuto/protokit/compare/v0.1.0-pre...v0.1.0-pre2) - 2018-02-22 ### Fixed * CreateGenRequest should filter for FileToGenerate using the `GetName()` rather than getting the base name ## [0.1.0-pre](https://github.com/pseudomuto/protokit/compare/c3aa082037b33bcd713106641e88afba846d003d...v0.1.0-pre) - 2018-02-21 The initial alpha release of protokit. golang-github-pseudomuto-protokit-0.2.1/LICENSE000066400000000000000000000020701515743464700213400ustar00rootroot00000000000000MIT License Copyright (c) 2018 David Muto (pseudomuto) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. golang-github-pseudomuto-protokit-0.2.1/Makefile000066400000000000000000000034601515743464700217770ustar00rootroot00000000000000.PHONY: bench release setup test TEST_PKGS = ./ ./utils TOOLPATH = $(abspath bin) VERSION = $(shell cat version.go | sed -n 's/.*const Version = "\(.*\)"/\1/p') BOLD = \033[1m CLEAR = \033[0m CYAN = \033[36m help: ## Display this help @awk '\ BEGIN {FS = ":.*##"; printf "Usage: make $(CYAN)$(CLEAR)\n"} \ /^[a-z0-9]+([\/]%)?([\/](%-)?[a-z\-0-9%]+)*:.*? ##/ { printf " $(CYAN)%-15s$(CLEAR) %s\n", $$1, $$2 } \ /^##@/ { printf "\n$(BOLD)%s$(CLEAR)\n", substr($$0, 5) }' \ $(MAKEFILE_LIST) ##@ Test test: fixtures/fileset.pb ## Run unit tests @go test -race -cover $(TEST_PKGS) test/bench: ## Run benchmark tests go test -bench=. test/ci: $(TOOLPATH)/goverage fixtures/fileset.pb test/bench ## Run CI tests include benchmarks with coverage @bin/goverage -race -coverprofile=coverage.txt -covermode=atomic $(TEST_PKGS) ##@ Release release: git tag v$(VERSION) git push origin --tags release/snapshot: $(TOOLPATH)/goreleaser ## Create a local release snapshot @bin/goreleaser --snapshot --rm-dist release/validate: $(TOOLPATH)/goreleaser ## Run goreleaser checks @bin/goreleaser check ################################################################################ # Indirect targets ################################################################################ $(TOOLPATH)/goreleaser: @echo "$(CYAN)Installing goreleaser v1.5.0...$(CLEAR)" @TOOLPKG=github.com/goreleaser/goreleaser@v1.5.0 make build-tool $(TOOLPATH)/goverage: @echo "$(CYAN)Installing goverage...$(CLEAR)" @TOOLPKG=github.com/haya14busa/goverage make build-tool .PHONY: build-tool build-tool: @{ \ TMP_DIR=$$(mktemp -d); \ cd $$TMP_DIR; \ go mod init tmp; \ GOBIN=$(TOOLPATH) go install $(TOOLPKG); \ rm -rf $$TMP_DIR; \ } fixtures/fileset.pb: fixtures/*.proto $(info Generating fixtures...) @cd fixtures && go generate golang-github-pseudomuto-protokit-0.2.1/README.md000066400000000000000000000043531515743464700216200ustar00rootroot00000000000000# protokit [![CI][github-svg]][github-ci] [![codecov][codecov-svg]][codecov-url] [![GoDoc][godoc-svg]][godoc-url] [![Go Report Card][goreport-svg]][goreport-url] A starter kit for building protoc-plugins. Rather than write your own, you can just use an existing one. See the [examples](examples/) directory for uh...examples. ## Getting Started ```golang package main import ( "github.com/golang/protobuf/proto" "github.com/golang/protobuf/protoc-gen-go/plugin" "github.com/pseudomuto/protokit" _ "google.golang.org/genproto/googleapis/api/annotations" // Support (google.api.http) option (from google/api/annotations.proto). "log" ) func main() { // all the heavy lifting done for you! if err := protokit.RunPlugin(new(plugin)); err != nil { log.Fatal(err) } } // plugin is an implementation of protokit.Plugin type plugin struct{} func (p *plugin) Generate(in *plugin_go.CodeGeneratorRequest) (*plugin_go.CodeGeneratorResponse, error) { descriptors := protokit.ParseCodeGenRequest(req) resp := new(plugin_go.CodeGeneratorResponse) for _, d := range descriptors { // TODO: YOUR WORK HERE fileName := // generate a file name based on d.GetName() content := // generate content for the output file resp.File = append(resp.File, &plugin_go.CodeGeneratorResponse_File{ Name: proto.String(fileName), Content: proto.String(content), }) } return resp, nil } ``` Then invoke your plugin via `protoc`. For example (assuming your app is called `thingy`): `protoc --plugin=protoc-gen-thingy=./thingy -I. --thingy_out=. rpc/*.proto` [github-svg]: https://github.com/pseudomuto/protokit/actions/workflows/ci.yaml/badge.svg?branch=master [github-ci]: https://github.com/pseudomuto/protokit/actions/workflows/ci.yaml [codecov-svg]: https://codecov.io/gh/pseudomuto/protokit/branch/master/graph/badge.svg [codecov-url]: https://codecov.io/gh/pseudomuto/protokit [godoc-svg]: https://godoc.org/github.com/pseudomuto/protokit?status.svg [godoc-url]: https://godoc.org/github.com/pseudomuto/protokit [goreport-svg]: https://goreportcard.com/badge/github.com/pseudomuto/protokit [goreport-url]: https://goreportcard.com/report/github.com/pseudomuto/protokit golang-github-pseudomuto-protokit-0.2.1/comments.go000066400000000000000000000047331515743464700225170ustar00rootroot00000000000000package protokit import ( "github.com/golang/protobuf/protoc-gen-go/descriptor" "bytes" "strconv" "strings" ) // A Comment describes the leading, trailing, and detached comments for a proto object. See `SourceCodeInfo_Location` in // descriptor.proto for details on what those terms mean type Comment struct { Leading string Trailing string Detached []string } // String returns the leading and trailing comments joined by 2 line breaks (`\n\n`). If either are empty, the line // breaks are removed. func (c *Comment) String() string { b := new(bytes.Buffer) if c.GetLeading() != "" { b.WriteString(c.GetLeading()) b.WriteString("\n\n") } b.WriteString(c.GetTrailing()) return strings.TrimSpace(b.String()) } func newComment(loc *descriptor.SourceCodeInfo_Location) *Comment { detached := make([]string, len(loc.GetLeadingDetachedComments())) for i, c := range loc.GetLeadingDetachedComments() { detached[i] = scrub(c) } return &Comment{ Leading: scrub(loc.GetLeadingComments()), Trailing: scrub(loc.GetTrailingComments()), Detached: detached, } } // GetLeading returns the leading comments func (c *Comment) GetLeading() string { return c.Leading } // GetTrailing returns the leading comments func (c *Comment) GetTrailing() string { return c.Trailing } // GetDetached returns the detached leading comments func (c *Comment) GetDetached() []string { return c.Detached } // Comments is a map of source location paths to values. type Comments map[string]*Comment // ParseComments parses all comments within a proto file. The locations are encoded into the map by joining the paths // with a "." character. E.g. `4.2.3.0`. // // Leading/trailing spaces are trimmed for each comment type (leading, trailing, detached) func ParseComments(fd *descriptor.FileDescriptorProto) Comments { comments := make(Comments) for _, loc := range fd.GetSourceCodeInfo().GetLocation() { if loc.GetLeadingComments() == "" && loc.GetTrailingComments() == "" && len(loc.GetLeadingDetachedComments()) == 0 { continue } path := loc.GetPath() key := make([]string, len(path)) for idx, p := range path { key[idx] = strconv.Itoa(int(p)) } comments[strings.Join(key, ".")] = newComment(loc) } return comments } func (c Comments) Get(path string) *Comment { if val, ok := c[path]; ok { return val } // return an empty comment return &Comment{Detached: make([]string, 0)} } func scrub(str string) string { return strings.TrimSpace(strings.Replace(str, "\n ", "\n", -1)) } golang-github-pseudomuto-protokit-0.2.1/comments_test.go000066400000000000000000000026521515743464700235540ustar00rootroot00000000000000package protokit_test import ( "github.com/stretchr/testify/suite" "fmt" "testing" "github.com/pseudomuto/protokit" "github.com/pseudomuto/protokit/utils" ) type CommentsTest struct { suite.Suite comments protokit.Comments } func TestComments(t *testing.T) { suite.Run(t, new(CommentsTest)) } func (assert *CommentsTest) SetupSuite() { pf, err := utils.LoadDescriptor("todo.proto", "fixtures", "fileset.pb") assert.NoError(err) assert.comments = protokit.ParseComments(pf) } func (assert *CommentsTest) TestComments() { tests := []struct { key string leading string trailing string }{ {"6.0.2.1", "Add an item to your list\n\nAdds a new item to the specified list.", ""}, // leading commend {"4.0.2.0", "", "The id of the list."}, // tailing comment } for _, test := range tests { assert.Equal(test.leading, assert.comments[test.key].GetLeading()) assert.Equal(test.trailing, assert.comments[test.key].GetTrailing()) assert.Len(assert.comments[test.key].GetDetached(), 0) } assert.NotNil(assert.comments.Get("WONTBETHERE")) assert.Equal("", assert.comments.Get("WONTBETHERE").String()) } // Join the leading and trailing comments together func ExampleComment_String() { c := &protokit.Comment{Leading: "Some leading comment", Trailing: "Some trailing comment"} fmt.Println(c.String()) // Output: Some leading comment // // Some trailing comment } golang-github-pseudomuto-protokit-0.2.1/context.go000066400000000000000000000042701515743464700223520ustar00rootroot00000000000000package protokit import ( "context" ) type contextKey string const ( fileContextKey = contextKey("file") descriptorContextKey = contextKey("descriptor") enumContextKey = contextKey("enum") serviceContextKey = contextKey("service") ) // ContextWithFileDescriptor returns a new context with the attached `FileDescriptor` func ContextWithFileDescriptor(ctx context.Context, fd *FileDescriptor) context.Context { return context.WithValue(ctx, fileContextKey, fd) } // FileDescriptorFromContext returns the `FileDescriptor` from the context and whether or not the key was found. func FileDescriptorFromContext(ctx context.Context) (*FileDescriptor, bool) { val, ok := ctx.Value(fileContextKey).(*FileDescriptor) return val, ok } // ContextWithDescriptor returns a new context with the specified `Descriptor` func ContextWithDescriptor(ctx context.Context, d *Descriptor) context.Context { return context.WithValue(ctx, descriptorContextKey, d) } // DescriptorFromContext returns the associated `Descriptor` for the context and whether or not it was found func DescriptorFromContext(ctx context.Context) (*Descriptor, bool) { val, ok := ctx.Value(descriptorContextKey).(*Descriptor) return val, ok } // ContextWithEnumDescriptor returns a new context with the specified `EnumDescriptor` func ContextWithEnumDescriptor(ctx context.Context, d *EnumDescriptor) context.Context { return context.WithValue(ctx, enumContextKey, d) } // EnumDescriptorFromContext returns the associated `EnumDescriptor` for the context and whether or not it was found func EnumDescriptorFromContext(ctx context.Context) (*EnumDescriptor, bool) { val, ok := ctx.Value(enumContextKey).(*EnumDescriptor) return val, ok } // ContextWithServiceDescriptor returns a new context with `service` func ContextWithServiceDescriptor(ctx context.Context, service *ServiceDescriptor) context.Context { return context.WithValue(ctx, serviceContextKey, service) } // ServiceDescriptorFromContext returns the `Service` from the context and whether or not the key was found. func ServiceDescriptorFromContext(ctx context.Context) (*ServiceDescriptor, bool) { val, ok := ctx.Value(serviceContextKey).(*ServiceDescriptor) return val, ok } golang-github-pseudomuto-protokit-0.2.1/context_test.go000066400000000000000000000032101515743464700234020ustar00rootroot00000000000000package protokit_test import ( "github.com/stretchr/testify/suite" "context" "testing" "github.com/pseudomuto/protokit" ) type ContextTest struct { suite.Suite } func TestContext(t *testing.T) { suite.Run(t, new(ContextTest)) } func (assert *ContextTest) TestContextWithFileDescriptor() { ctx := context.Background() val, found := protokit.FileDescriptorFromContext(ctx) assert.Nil(val) assert.False(found) ctx = protokit.ContextWithFileDescriptor(ctx, new(protokit.FileDescriptor)) val, found = protokit.FileDescriptorFromContext(ctx) assert.NotNil(val) assert.True(found) } func (assert *ContextTest) TestContextWithEnumDescriptor() { ctx := context.Background() val, found := protokit.EnumDescriptorFromContext(ctx) assert.Nil(val) assert.False(found) ctx = protokit.ContextWithEnumDescriptor(ctx, new(protokit.EnumDescriptor)) val, found = protokit.EnumDescriptorFromContext(ctx) assert.NotNil(val) assert.True(found) } func (assert *ContextTest) TestContextWithDescriptor() { ctx := context.Background() val, found := protokit.DescriptorFromContext(ctx) assert.Nil(val) assert.False(found) ctx = protokit.ContextWithDescriptor(ctx, new(protokit.Descriptor)) val, found = protokit.DescriptorFromContext(ctx) assert.NotNil(val) assert.True(found) } func (assert *ContextTest) TestContextWithServiceDescriptor() { ctx := context.Background() val, found := protokit.ServiceDescriptorFromContext(ctx) assert.Empty(val) assert.False(found) ctx = protokit.ContextWithServiceDescriptor(ctx, new(protokit.ServiceDescriptor)) val, found = protokit.ServiceDescriptorFromContext(ctx) assert.NotNil(val) assert.True(found) } golang-github-pseudomuto-protokit-0.2.1/doc.go000066400000000000000000000011761515743464700214350ustar00rootroot00000000000000// Package protokit is a library that makes it easy to create your own protoc plugins. It has excellent test coverage, // and saves you so much time! // // There are two main things this library provides; a parser for parsing protobuf files into some well-defined structs, // and an abstraction to make it simple to write your own protoc plugins. // // Getting Started // // For a quick view of how to get started, see https://godoc.org/github.com/pseudomuto/protokit#example-RunPlugin // // If you want see/try a working example, check out the examples in // https://github.com/pseudomuto/protokit/tree/master/examples. package protokit golang-github-pseudomuto-protokit-0.2.1/example_plugin_test.go000066400000000000000000000014751515743464700247420ustar00rootroot00000000000000package protokit_test import ( "github.com/golang/protobuf/proto" "github.com/golang/protobuf/protoc-gen-go/plugin" "github.com/pseudomuto/protokit" "log" ) type plugin struct{} func (p *plugin) Generate(r *plugin_go.CodeGeneratorRequest) (*plugin_go.CodeGeneratorResponse, error) { descriptors := protokit.ParseCodeGenRequest(r) resp := new(plugin_go.CodeGeneratorResponse) for _, desc := range descriptors { resp.File = append(resp.File, &plugin_go.CodeGeneratorResponse_File{ Name: proto.String(desc.GetName() + ".out"), Content: proto.String("Some relevant output"), }) } return resp, nil } // An example of running a custom plugin. This would be in your main.go file. func ExampleRunPlugin() { // in func main() {} if err := protokit.RunPlugin(new(plugin)); err != nil { log.Fatal(err) } } golang-github-pseudomuto-protokit-0.2.1/examples/000077500000000000000000000000001515743464700221525ustar00rootroot00000000000000golang-github-pseudomuto-protokit-0.2.1/examples/jsonator/000077500000000000000000000000001515743464700240115ustar00rootroot00000000000000golang-github-pseudomuto-protokit-0.2.1/examples/jsonator/.gitignore000066400000000000000000000000411515743464700257740ustar00rootroot00000000000000third_party jsonator output.json golang-github-pseudomuto-protokit-0.2.1/examples/jsonator/README.md000066400000000000000000000021421515743464700252670ustar00rootroot00000000000000# jsonator Quite possibly the most useless protoc-gen plugin out there. This is just a demo to show you how to use `protokit`. All this does, is generate a single output file (`output.json`) which contains the full name and description of each of the proto files to generate as well as any services and associated methods. **Running this example** * `go generate && cat output.json` * :rofl: You'll see something like this (assuming you made no changes to the proto files here): ```json [ { "name":"com.jsonator.v1.sample.proto", "description":"This is just a sample proto for demonstrating how to use this library.\n\nThere's nothing \"fancy\" here.", "services":[ { "name":"SampleService", "methods":[ "RandomSample" ] } ] }, { "name":"com.jsonator.v2.sample2.proto", "description":"This is just another sample proto for demonstrating how to use this library.\n\nThere's also nothing \"fancy\" here.", "services":[ { "name":"SampleService", "methods":[ "RandomSample" ] } ] } ] ``` golang-github-pseudomuto-protokit-0.2.1/examples/jsonator/generate.go000066400000000000000000000011271515743464700261330ustar00rootroot00000000000000package main //go:generate go build -o ./jsonator main.go //go:generate mkdir -p third_party/google/api //go:generate curl -sSL -o third_party/google/api/annotations.proto https://raw.githubusercontent.com/googleapis/googleapis/master/google/api/annotations.proto //go:generate curl -sSL -o third_party/google/api/http.proto https://raw.githubusercontent.com/googleapis/googleapis/master/google/api/http.proto //go:generate protoc --plugin=protoc-gen-jsonator=./jsonator -I. -Ithird_party --jsonator_out=. ./sample.proto ./sample2.proto //go:generate rm -rf third_party //go:generate rm ./jsonator golang-github-pseudomuto-protokit-0.2.1/examples/jsonator/main.go000066400000000000000000000053601515743464700252700ustar00rootroot00000000000000package main import ( "github.com/golang/protobuf/proto" "github.com/golang/protobuf/protoc-gen-go/plugin" "github.com/pseudomuto/protokit" "google.golang.org/genproto/googleapis/api/annotations" "bytes" "encoding/json" "fmt" "log" ) func main() { if err := protokit.RunPlugin(new(plugin)); err != nil { log.Fatal(err) } } type plugin struct{} func (p *plugin) Generate(req *plugin_go.CodeGeneratorRequest) (*plugin_go.CodeGeneratorResponse, error) { descriptors := protokit.ParseCodeGenRequest(req) files := make([]*file, len(descriptors)) for i, d := range descriptors { files[i] = newFile(d) } buf := new(bytes.Buffer) enc := json.NewEncoder(buf) enc.SetIndent("", " ") if err := enc.Encode(files); err != nil { return nil, err } resp := new(plugin_go.CodeGeneratorResponse) resp.File = append(resp.File, &plugin_go.CodeGeneratorResponse_File{ Name: proto.String("output.json"), Content: proto.String(buf.String()), }) return resp, nil } type file struct { Name string `json:"name"` Description string `json:"description"` Services []*service `json:"services"` } func newFile(fd *protokit.FileDescriptor) *file { svcs := make([]*service, len(fd.GetServices())) for i, sd := range fd.GetServices() { svcs[i] = newService(sd) } return &file{ Name: fmt.Sprintf("%s.%s", fd.GetPackage(), fd.GetName()), Description: fd.GetComments().String(), Services: svcs, } } type service struct { Name string `json:"name"` Methods []*method `json:"methods"` } func newService(sd *protokit.ServiceDescriptor) *service { methods := make([]*method, len(sd.GetMethods())) for i, md := range sd.GetMethods() { methods[i] = newMethod(md) } return &service{Name: sd.GetName(), Methods: methods} } type method struct { Name string `json:"name"` HTTPRules []string `json:"http_rules"` } func newMethod(md *protokit.MethodDescriptor) *method { httpRules := make([]string, 0) if httpRule, ok := md.OptionExtensions["google.api.http"].(*annotations.HttpRule); ok { switch httpRule.GetPattern().(type) { case *annotations.HttpRule_Get: httpRules = append(httpRules, fmt.Sprintf("GET %s", httpRule.GetGet())) case *annotations.HttpRule_Put: httpRules = append(httpRules, fmt.Sprintf("PUT %s", httpRule.GetPut())) case *annotations.HttpRule_Post: httpRules = append(httpRules, fmt.Sprintf("POST %s", httpRule.GetPost())) case *annotations.HttpRule_Delete: httpRules = append(httpRules, fmt.Sprintf("DELETE %s", httpRule.GetDelete())) case *annotations.HttpRule_Patch: httpRules = append(httpRules, fmt.Sprintf("PATCH %s", httpRule.GetPatch())) } // Append more for each rule in httpRule.AdditionalBindings... } return &method{Name: md.GetName(), HTTPRules: httpRules} } golang-github-pseudomuto-protokit-0.2.1/examples/jsonator/sample.proto000066400000000000000000000006321515743464700263600ustar00rootroot00000000000000syntax = "proto3"; import "google/api/annotations.proto"; import "google/protobuf/empty.proto"; // This is just a sample proto for demonstrating how to use this library. // // There's nothing "fancy" here. package com.jsonator.v1; service SampleService { rpc RandomSample(google.protobuf.Empty) returns (Sample) { option (google.api.http).get = "/v1/sample"; } } message Sample { int64 id = 1; } golang-github-pseudomuto-protokit-0.2.1/examples/jsonator/sample2.proto000066400000000000000000000006451515743464700264460ustar00rootroot00000000000000syntax = "proto3"; import "google/api/annotations.proto"; import "google/protobuf/empty.proto"; // This is just another sample proto for demonstrating how to use this library. // // There's also nothing "fancy" here. package com.jsonator.v2; service SampleService { rpc RandomSample(google.protobuf.Empty) returns (Sample) { option (google.api.http).get = "/v2/sample"; } } message Sample { int64 id = 1; } golang-github-pseudomuto-protokit-0.2.1/fixtures/000077500000000000000000000000001515743464700222055ustar00rootroot00000000000000golang-github-pseudomuto-protokit-0.2.1/fixtures/booking.proto000066400000000000000000000046321515743464700247270ustar00rootroot00000000000000syntax = "proto2"; import "extend.proto"; /** * Booking related messages. * * This file is really just an example. The data model is completely fictional. */ package com.pseudomuto.protokit.v1; option (com.pseudomuto.protokit.v1.extend_file) = true; /** * Service for handling vehicle bookings. */ service BookingService { option (com.pseudomuto.protokit.v1.extend_service) = true; /// Used to book a vehicle. Pass in a Booking and a BookingStatus will be returned. rpc BookVehicle (Booking) returns (BookingStatus) { option (com.pseudomuto.protokit.v1.extend_method) = true; }; } /** * Represents the status of a vehicle booking. */ message BookingStatus { /** * A flag for the status result. */ enum StatusCode { OK = 200; // OK result. BAD_REQUEST = 400; // BAD result. } required int32 id = 1; /// Unique booking status ID. required string description = 2; /// Booking status description. E.g. "Active". optional StatusCode status_code = 3; /// The status of this status? extensions 100 to max; } // File-level extension extend BookingStatus { /* The country the booking occurred in. */ optional string country = 100 [default = "china", (com.pseudomuto.protokit.v1.extend_field) = true]; } /** * The type of booking. */ enum BookingType { option (com.pseudomuto.protokit.v1.extend_enum) = true; IMMEDIATE = 100; // Immediate booking. FUTURE = 101 [(com.pseudomuto.protokit.v1.extend_enum_value) = true]; // Future booking. } /** * Represents the booking of a vehicle. * * Vehicles are some cool shit. But drive carefully! */ message Booking { option (com.pseudomuto.protokit.v1.extend_message) = true; required int32 vehicle_id = 1; /// ID of booked vehicle. required int32 customer_id = 2; /// Customer that booked the vehicle. required BookingStatus status = 3; /// Status of the booking. /** Has booking confirmation been sent? */ required bool confirmation_sent = 4; /** Has payment been received? */ optional bool payment_received = 5 [default = true, (com.pseudomuto.protokit.v1.extend_field) = true]; oneof things { int32 reference_num = 6; // the numeric reference number string reference_tag = 7; // the reference tag (string) } // Nested extentions are also a thing. extend BookingStatus { optional string optional_field_1 = 101; // An optional field to be used however you please. } } golang-github-pseudomuto-protokit-0.2.1/fixtures/extend.proto000066400000000000000000000017661515743464700245730ustar00rootroot00000000000000syntax = "proto2"; import "google/protobuf/descriptor.proto"; package com.pseudomuto.protokit.v1; /** * Extension of protobuf file options. */ extend google.protobuf.FileOptions { optional bool extend_file = 20000; } /** * Extension of protobuf service options. */ extend google.protobuf.ServiceOptions { optional bool extend_service = 20000; } /** * Extension of protobuf method options. */ extend google.protobuf.MethodOptions { optional bool extend_method = 20000; } /** * Extension of protobuf enum options. */ extend google.protobuf.EnumOptions { optional bool extend_enum = 20000; } /** * Extension of protobuf enum value options. */ extend google.protobuf.EnumValueOptions { optional bool extend_enum_value = 20000; } /** * Extension of protobuf message options. */ extend google.protobuf.MessageOptions { optional bool extend_message = 20000; } /** * Extension of protobuf field options. */ extend google.protobuf.FieldOptions { optional bool extend_field = 20000; } golang-github-pseudomuto-protokit-0.2.1/fixtures/fileset.pb000066400000000000000000002116741515743464700241760ustar00rootroot00000000000000 Λω google/protobuf/descriptor.protogoogle.protobuf"M FileDescriptorSet8 file ( 2$.google.protobuf.FileDescriptorProtoRfile"δ FileDescriptorProto name ( Rname package ( Rpackage dependency ( R dependency+ public_dependency (RpublicDependency' weak_dependency (RweakDependencyC message_type ( 2 .google.protobuf.DescriptorProtoR messageTypeA enum_type ( 2$.google.protobuf.EnumDescriptorProtoRenumTypeA service ( 2'.google.protobuf.ServiceDescriptorProtoRserviceC extension ( 2%.google.protobuf.FieldDescriptorProtoR extension6 options ( 2.google.protobuf.FileOptionsRoptionsI source_code_info ( 2.google.protobuf.SourceCodeInfoRsourceCodeInfo syntax ( Rsyntax"Ή DescriptorProto name ( Rname; field ( 2%.google.protobuf.FieldDescriptorProtoRfieldC extension ( 2%.google.protobuf.FieldDescriptorProtoR extensionA nested_type ( 2 .google.protobuf.DescriptorProtoR nestedTypeA enum_type ( 2$.google.protobuf.EnumDescriptorProtoRenumTypeX extension_range ( 2/.google.protobuf.DescriptorProto.ExtensionRangeRextensionRangeD oneof_decl ( 2%.google.protobuf.OneofDescriptorProtoR oneofDecl9 options ( 2.google.protobuf.MessageOptionsRoptionsU reserved_range ( 2..google.protobuf.DescriptorProto.ReservedRangeR reservedRange# reserved_name ( R reservedNamez ExtensionRange start (Rstart end (Rend@ options ( 2&.google.protobuf.ExtensionRangeOptionsRoptions7 ReservedRange start (Rstart end (Rend"| ExtensionRangeOptionsX uninterpreted_optionη ( 2$.google.protobuf.UninterpretedOptionRuninterpretedOption* θ€€€€"˜ FieldDescriptorProto name ( Rname number (RnumberA label (2+.google.protobuf.FieldDescriptorProto.LabelRlabel> type (2*.google.protobuf.FieldDescriptorProto.TypeRtype type_name ( RtypeName extendee ( Rextendee# default_value ( R defaultValue oneof_index (R oneofIndex json_name ( RjsonName7 options ( 2.google.protobuf.FieldOptionsRoptions"Ά Type TYPE_DOUBLE TYPE_FLOAT TYPE_INT64 TYPE_UINT64 TYPE_INT32 TYPE_FIXED64 TYPE_FIXED32 TYPE_BOOL TYPE_STRING  TYPE_GROUP  TYPE_MESSAGE  TYPE_BYTES  TYPE_UINT32  TYPE_ENUM TYPE_SFIXED32 TYPE_SFIXED64 TYPE_SINT32 TYPE_SINT64"C Label LABEL_OPTIONAL LABEL_REQUIRED LABEL_REPEATED"c OneofDescriptorProto name ( Rname7 options ( 2.google.protobuf.OneofOptionsRoptions"γ EnumDescriptorProto name ( Rname? value ( 2).google.protobuf.EnumValueDescriptorProtoRvalue6 options ( 2.google.protobuf.EnumOptionsRoptions] reserved_range ( 26.google.protobuf.EnumDescriptorProto.EnumReservedRangeR reservedRange# reserved_name ( R reservedName; EnumReservedRange start (Rstart end (Rend"ƒ EnumValueDescriptorProto name ( Rname number (Rnumber; options ( 2!.google.protobuf.EnumValueOptionsRoptions"§ ServiceDescriptorProto name ( Rname> method ( 2&.google.protobuf.MethodDescriptorProtoRmethod9 options ( 2.google.protobuf.ServiceOptionsRoptions"‰ MethodDescriptorProto name ( Rname input_type ( R inputType output_type ( R outputType8 options ( 2.google.protobuf.MethodOptionsRoptions0 client_streaming (:falseRclientStreaming0 server_streaming (:falseRserverStreaming"’ FileOptions! java_package ( R javaPackage0 java_outer_classname ( RjavaOuterClassname5 java_multiple_files (:falseRjavaMultipleFilesD java_generate_equals_and_hash (BRjavaGenerateEqualsAndHash: java_string_check_utf8 (:falseRjavaStringCheckUtf8S optimize_for (2).google.protobuf.FileOptions.OptimizeMode:SPEEDR optimizeFor go_package ( R goPackage5 cc_generic_services (:falseRccGenericServices9 java_generic_services (:falseRjavaGenericServices5 py_generic_services (:falseRpyGenericServices7 php_generic_services* (:falseRphpGenericServices% deprecated (:falseR deprecated/ cc_enable_arenas (:falseRccEnableArenas* objc_class_prefix$ ( RobjcClassPrefix) csharp_namespace% ( RcsharpNamespace! swift_prefix' ( R swiftPrefix( php_class_prefix( ( RphpClassPrefix# php_namespace) ( R phpNamespace4 php_metadata_namespace, ( RphpMetadataNamespace! ruby_package- ( R rubyPackageX uninterpreted_optionη ( 2$.google.protobuf.UninterpretedOptionRuninterpretedOption": OptimizeMode SPEED CODE_SIZE LITE_RUNTIME* θ€€€€J&'"Ρ MessageOptions< message_set_wire_format (:falseRmessageSetWireFormatL no_standard_descriptor_accessor (:falseRnoStandardDescriptorAccessor% deprecated (:falseR deprecated map_entry (RmapEntryX uninterpreted_optionη ( 2$.google.protobuf.UninterpretedOptionRuninterpretedOption* θ€€€€J J  "β FieldOptionsA ctype (2#.google.protobuf.FieldOptions.CType:STRINGRctype packed (RpackedG jstype (2$.google.protobuf.FieldOptions.JSType: JS_NORMALRjstype lazy (:falseRlazy% deprecated (:falseR deprecated weak (:falseRweakX uninterpreted_optionη ( 2$.google.protobuf.UninterpretedOptionRuninterpretedOption"/ CType STRING CORD STRING_PIECE"5 JSType JS_NORMAL JS_STRING JS_NUMBER* θ€€€€J"s OneofOptionsX uninterpreted_optionη ( 2$.google.protobuf.UninterpretedOptionRuninterpretedOption* θ€€€€"ΐ EnumOptions allow_alias (R allowAlias% deprecated (:falseR deprecatedX uninterpreted_optionη ( 2$.google.protobuf.UninterpretedOptionRuninterpretedOption* θ€€€€J"ž EnumValueOptions% deprecated (:falseR deprecatedX uninterpreted_optionη ( 2$.google.protobuf.UninterpretedOptionRuninterpretedOption* θ€€€€"œ ServiceOptions% deprecated! (:falseR deprecatedX uninterpreted_optionη ( 2$.google.protobuf.UninterpretedOptionRuninterpretedOption* θ€€€€"ΰ MethodOptions% deprecated! (:falseR deprecatedq idempotency_level" (2/.google.protobuf.MethodOptions.IdempotencyLevel:IDEMPOTENCY_UNKNOWNRidempotencyLevelX uninterpreted_optionη ( 2$.google.protobuf.UninterpretedOptionRuninterpretedOption"P IdempotencyLevel IDEMPOTENCY_UNKNOWN NO_SIDE_EFFECTS IDEMPOTENT* θ€€€€"š UninterpretedOptionA name ( 2-.google.protobuf.UninterpretedOption.NamePartRname) identifier_value ( RidentifierValue, positive_int_value (RpositiveIntValue, negative_int_value (RnegativeIntValue! double_value (R doubleValue! string_value ( R stringValue' aggregate_value ( RaggregateValueJ NamePart name_part ( RnamePart! is_extension (R isExtension"§ SourceCodeInfoD location ( 2(.google.protobuf.SourceCodeInfo.LocationRlocationΞ Location path (BRpath span (BRspan) leading_comments ( RleadingComments+ trailing_comments ( RtrailingComments: leading_detached_comments ( RleadingDetachedComments"Ρ GeneratedCodeInfoM annotation ( 2-.google.protobuf.GeneratedCodeInfo.AnnotationR annotationm Annotation path (BRpath source_file ( R sourceFile begin (Rbegin end (RendB com.google.protobufBDescriptorProtosHZ>github.com/golang/protobuf/protoc-gen-go/descriptor;descriptorψ’GPBͺGoogle.Protobuf.ReflectionJ¬Ύ 'ς ͺ  '2Α Protocol Buffers - Google's data interchange format Copyright 2008 Google Inc. All rights reserved. https://developers.google.com/protocol-buffers/ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2Ϋ Author: kenton@google.com (Kenton Varda) Based on original Protocol Buffers design by Sanjay Ghemawat, Jeff Dean, and others. The messages in this file describe the definitions found in .proto files. A valid .proto file can be translated directly to a FileDescriptorProto without any other information (e.g. without reading its imports).  )  *U  *U  +, +,  ,1 ,1  -7 %-7  .! $.!  / /  3   3t descriptor.proto must be optimized for speed because reflection-based algorithms don't work during bootstrapping. j 79^ The protocol compiler can output a FileDescriptorSet containing the .proto files it parses. 7 8( 8 8  8# 8&' / <Y# Describes a complete .proto file. < 9 =", file name, relative to root of source tree = =  = = * >" e.g. "foo", "foo.bar", etc. > >  > > 4 A!' Names of files imported by this file. A A  A A Q C(D Indexes of the public imported files in the dependency list above. C C  C" C%' z F&m Indexes of the weak imported files in the dependency list. For Google-internal migration only. Do not use. F F  F F#% 6 I,) All top-level definitions in this file. I I  I' I*+ J- J J  J( J+, K. K K ! K") K,- L. L L  L ) L,-  N#  N  N   N  N!" τ  T/ζ This field contains optional information about the original source code. You may safely remove this entire field without harming runtime functionality of the descriptors -- the information is needed only by development tools.  T  T   T*  T-. ]  XP The syntax of the proto file. The supported values are "proto2" and "proto3".  X  X   X  X ' \| Describes a message type. \ ] ] ]  ] ] _* _ _  _ % _() `. ` `  ` ) `,- b+ b b  b& b)* c- c c  c( c+, ej e  f  f  f   f  f g  g  g   g  g i/  i  i "  i#*  i-. k. k k  k) k,- m/ m m  m * m-. o& o o  o! o$% ͺ tw› Range of reserved tag numbers. Reserved tag numbers may not be used by fields or extension ranges in the same message. Reserved ranges may not overlap. t   u" Inclusive.  u  u   u  u  v" Exclusive.  v  v   v  v x, x x  x' x*+ ‚  {%u Reserved field names, which may not be used by fields in the same message. A given name may only be reserved once.  {  {   {  {"$ ~„ ~ O €:A The parser stores options it doesn't recognize here. See above. € €  €3 €69 Z ƒM Clients can define custom options in extensions of this message. See above. ƒ  ƒ  ƒ 3 ‡Υ% Describes a field within a message. ‡  ˆ§ ˆ S ‹C 0 is reserved for errors. Order is weird for historical reasons.  ‹  ‹  Œ  Œ  Œ w g Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT64 if negative values are likely.           w “g Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT32 if negative values are likely.  “  “  ”  ”  ”  •  •  •  –  –  –  —  —  — β  œΡ Tag-delimited aggregate. Group type is deprecated and not supported in proto3. However, Proto3 implementations should still be able to parse the group wire format and treat group fields as unknown fields.   œ   œ -  " Length-delimited aggregate.       #    New in version 2.           ‘   ‘   ‘   ’   ’   ’  £  £  £  €  €  € ' ₯" Uses ZigZag encoding.  ₯  ₯ ' ¦" Uses ZigZag encoding.  ¦  ¦  © © * « 0 is reserved for errors  «  «  ¬  ¬  ¬  ­  ­  ­ ° ° °  ° ° ± ± ±  ± ± ² ² ²  ² ² œ Ά If type_name is set, this need not be set. If both this and type_name are set, this must be one of TYPE_ENUM, TYPE_MESSAGE or TYPE_GROUP. Ά Ά  Ά Ά · ½ ¨ For message and enum types, this is the name of the type. If the name starts with a '.', it is fully-qualified. Otherwise, C++-like scoping rules are used to find the type (i.e. first the nested types within this message are searched, then within the parent, on up to the root namespace). ½ ½  ½ ½ ~ Αp For extensions, this is the name of the type being extended. It is resolved in the same manner as type_name. Α Α  Α Α ± Θ$’ For numeric types, contains the original text representation of the value. For booleans, "true" or "false". For strings, contains the default text contents (not escaped in any way). For bytes, contains the C escaped value. All bytes >= 128 are escaped. TODO(kenton): Base-64 encode? Θ Θ  Θ Θ"# „ Μ!v If set, gives the index of a oneof in the containing type's oneof_decl list. This field is a member of that oneof. Μ Μ  Μ Μ ϊ !λ JSON name of this field. The value is set by protocol compiler. If the user has set a "json_name" option on this field, that option's value will be used. Otherwise, it's deduced from the field's name by converting it to camelCase.       Τ$  Τ  Τ   Τ  Τ"# " ΨΫ Describes a oneof. Ψ Ω Ω Ω  Ω Ω Ϊ$ Ϊ Ϊ  Ϊ Ϊ"# ' ήψ Describes an enum type. ή ί ί ί  ί ί α. α α # α$) α,- γ# γ γ  γ γ!" ― λξž Range of reserved numeric values. Reserved values may not be used by entries in the same enum. Reserved ranges may not overlap. Note that this is distinct from DescriptorProto.ReservedRange in that it is inclusive such that it can appropriately represent the entire int32 domain. λ   μ" Inclusive.  μ  μ   μ  μ  ν" Inclusive.  ν  ν   ν  ν ͺ σ0› Range of reserved numeric values. Reserved numeric values may not be used by enum values in the same enum declaration. Reserved ranges may not overlap. σ σ  σ+ σ./ l χ$^ Reserved enum value names, which may not be reused. A given name may only be reserved once. χ χ  χ χ"# 1 ϋ€# Describes a value within an enum. ϋ ό ό ό  ό ό ύ ύ ύ  ύ ύ (    # &' $ ƒˆ Describes a service. ƒ „ „ „  „ „ …, … … …!' …*+ ‡& ‡ ‡  ‡! ‡$% 0  ‹™" Describes a method of a service.  ‹  Œ  Œ  Œ   Œ  Œ —  !ˆ Input and output type names. These are resolved in the same way as FieldDescriptorProto.type_name, but must refer to a message type.           ‘"  ‘  ‘   ‘  ‘ !  “%  “  “   “  “#$ E  –57 Identifies if client streams multiple client messages  –  –   –  –#$  –%4  –.3 E  ˜57 Identifies if server streams multiple server messages  ˜  ˜   ˜  ˜#$  ˜%4  ˜.3 ―  ½·2N =================================================================== Options 2Π Each of the definitions above may have "options" attached. These are just annotations which may cause code to be generated slightly differently or may contain hints for code that manipulates protocol messages. Clients may define custom options as extensions of the *Options messages. These extensions may not yet be known at parsing time, so the parser cannot store the values in them. Instead it stores them in a field in the *Options message called uninterpreted_option. This field must have the same name across all *Options messages. We then use this field to populate the extensions when we build a descriptor, at which point all protos have been parsed and so all extensions are known. Extension numbers for custom options may be chosen as follows: * For options which will only be used within a single application or organization, or for experimental options, use field numbers 50000 through 99999. It is up to you to ensure that you do not use the same number for multiple options. * For options which will be published and used publicly by multiple independent entities, e-mail protobuf-global-extension-registry@google.com to reserve extension numbers. Simply provide your project name (e.g. Objective-C plugin) and your project website (if available) -- there's no need to explain how you intend to use them. Usually you only need one extension number. You can declare multiple options with only one extension number by putting them in a sub-message. See the Custom Options section of the docs for examples: https://developers.google.com/protocol-buffers/docs/proto#options If this turns out to be popular, a web service will be set up to automatically assign option numbers.  ½ τ  Γ#ε Sets the Java package where classes generated from this .proto will be placed. By default, the proto package is used, but this is often inappropriate because proto packages do not normally start with backwards domain names.  Γ  Γ   Γ  Γ!" Ώ  Λ+° If set, all the classes from the .proto file are wrapped in a single outer class with the given name. This applies to both Proto1 (equivalent to the old "--one_java_file" option) and Proto2 (where a .proto always translates to a single class, but you may want to explicitly choose the class name).  Λ  Λ   Λ&  Λ)* £  Σ9” If set true, then the Java code generator will generate a separate .java file for each top-level message, enum, and service defined in the .proto file. Thus, these types will *not* be nested inside the outer class named by java_outer_classname. However, the outer class will still be generated to contain the file's getDescriptor() method as well as any top-level extensions defined in the file.  Σ  Σ   Σ#  Σ&(  Σ)8  Σ27 )  ΦE This option does nothing.  Φ  Φ   Φ-  Φ02  Φ3D   Φ4C ζ  ή<Χ If set true, then the Java2 code generator will generate code that throws an exception whenever an attempt is made to assign a non-UTF-8 byte sequence to a string field. Message reflection will do the same. However, an extension field still accepts non-UTF-8 byte sequences. This option has no effect on when used with the lite runtime.  ή  ή   ή&  ή)+  ή,;  ή5: L  βη< Generated classes can be optimized for speed or code size.  β D  γ"4 Generate complete code for parsing, serialization,   γ   γ G  ε etc. "/ Use ReflectionOps to implement these methods.   ε   ε G  ζ"7 Generate code using MessageLite and the lite runtime.   ζ   ζ  θ9  θ  θ   θ$  θ'(  θ)8  θ27 β  ο"Σ Sets the Go package where structs generated from this .proto will be placed. If omitted, the Go package will be derived from the following: - The basename of the package import path, if provided. - Otherwise, the package statement in the .proto file, if present. - Otherwise, the basename of the .proto file, without extension.  ο  ο   ο  ο! Τ  ύ9Ε Should generic services be generated in each language? "Generic" services are not specific to any particular RPC system. They are generated by the main code generators in each language (without additional plugins). Generic services were the only kind of service generation supported by early versions of google.protobuf. Generic services are now considered deprecated in favor of using plugins that generate code specific to your particular RPC system. Therefore, these default to false. Old code which depends on generic services should explicitly set them to true.  ύ  ύ   ύ#  ύ&(  ύ)8  ύ27  ώ;  ώ  ώ   ώ%  ώ(*  ώ+:  ώ49   9          #   &(   )8   27   €:   €   €    €$   €')   €*9   €38 σ   †0δ Is this file deprecated? Depending on the target platform, this can emit Deprecated annotations for everything in the file, or it will be completely ignored; in the very least, this is a formalization for deprecating files.   †   †    †   †   † /   †).    Š6q Enables the use of arenas for the proto messages in this file. This applies only to generated classes for C++.   Š   Š    Š   Š#%   Š&5   Š/4 ’   )ƒ Sets the objective c class prefix which is prepended to all objective c generated classes from this .proto. There is no default.          #   &( I  ’(; Namespace for generated classes; defaults to the package.  ’  ’   ’"  ’%' ‘  ˜$‚ By default Swift generators will take the proto package and CamelCase it replacing '.' with underscore and use that to prefix the types/symbols defined. When this options is provided, they will use this value instead to prefix the types/symbols defined.  ˜  ˜   ˜  ˜!# ~  œ(p Sets the php class prefix which is prepended to all php generated classes from this .proto. Default is empty.  œ  œ   œ"  œ%' Ύ  ‘%― Use this option to change the namespace of php generated classes. Default is empty. When this option is empty, the package name will be used for determining the namespace.  ‘  ‘   ‘  ‘"$ Κ  §.» Use this option to change the namespace of php generated metadata classes. Default is empty. When this option is empty, the proto file name will be used for determining the namespace.  §  §   §(  §+- Β  ¬$³ Use this option to change the package of ruby generated classes. Default is empty. When this option is not set, the package name will be used for determining the ruby package.  ¬  ¬   ¬  ¬!# |  °:n The parser stores options it doesn't recognize here. See the documentation for the "Options" section above.  °  °   °3  °69 ‡  ΄z Clients can define custom options in extensions of this message. See the documentation for the "Options" section above.  ΄   ΄   ΄  Ά   Ά  Ά  Ά  Ήψ  Ή Ψ  Μ<Ι Set true to use the old proto1 MessageSet wire format for extensions. This is provided for backwards-compatibility with the MessageSet wire format. You should not use this for any other reason: It's less efficient, has fewer features, and is more complicated. The message must be defined exactly as follows: message Foo { option message_set_wire_format = true; extensions 4 to max; } Note that the message cannot have any defined fields; MessageSets only have extensions. All extensions of your type must be singular messages; e.g. they cannot be int32s, enums, or repeated messages. Because this is an option, the above two restrictions are not enforced by the protocol compiler.  Μ  Μ   Μ'  Μ*+  Μ,;  Μ5: λ  ΡDά Disables the generation of the standard "descriptor()" accessor, which can conflict with a field of the same name. This is meant to make migration from proto1 easier; new code should avoid fields named "descriptor".  Ρ  Ρ   Ρ/  Ρ23  Ρ4C  Ρ=B ξ  Χ/ί Is this message deprecated? Depending on the target platform, this can emit Deprecated annotations for the message, or it will be completely ignored; in the very least, this is a formalization for deprecating messages.  Χ  Χ   Χ  Χ  Χ.  Χ(- ž  ξ Whether the message is an automatically generated map entry type for the maps field. For maps fields: map map_field = 1; The parsed descriptor looks like: message MapFieldEntry { option map_entry = true; optional KeyType key = 1; optional ValueType value = 2; } repeated MapFieldEntry map_field = 1; Implementations may choose not to generate the map_entry=true message, but use a native map in the target language to hold the keys and values. The reflection APIs in such implementions still need to work as if the field is a repeated message field. NOTE: Do not set the option in .proto files. Always use the maps syntax instead. The option should only be implicitly set by the proto compiler parser.  ξ  ξ   ξ  ξ $  π " javalite_serializable  π  π  π   ρ " javanano_as_lite  ρ  ρ  ρ O  τ:A The parser stores options it doesn't recognize here. See above.  τ  τ   τ3  τ69 Z  χM Clients can define custom options in extensions of this message. See above.  χ   χ   χ  ϊΥ  ϊ £  .” The ctype option instructs the C++ code generator to use a different representation of the field than it normally would. See the specific options below. This option is not yet implemented in the open source release -- sorry, we'll try to include it in a future version!           -  &,   €‡  €   ‚ Default mode.   ‚   ‚    „   „   „   †   †   † Ϊ  Λ The packed option can be enabled for repeated primitive fields to enable a more efficient representation on the wire. Rather than repeatedly writing the tag and type for each element, the entire array is encoded as a single length-delimited blob. In proto3, only explicit setting it to false will avoid using packed encoding.          š  š3‹ The jstype option determines the JavaScript type used for values of the field. The option is permitted only for 64 bit integral and fixed types (int64, uint64, sint64, fixed64, sfixed64). A field with jstype JS_STRING is represented as JavaScript string, which avoids loss of precision that can happen when a large value is converted to a floating point JavaScript. Specifying JS_NUMBER for the jstype causes the generated JavaScript code to use the JavaScript "number" type. The behavior of the default option JS_NORMAL is implementation dependent. This option is an enum to permit additional types to be added, e.g. goog.math.Integer.  š  š   š  š  š2  š(1   ›€  › '   Use the default type.       )    Use JavaScript strings.         )  £ Use JavaScript numbers.   £   £ ο  Β)ΰ Should this field be parsed lazily? Lazy applies only to message-type fields. It means that when the outer message is initially parsed, the inner message's contents will not be parsed but instead stored in encoded form. The inner message will actually be parsed when it is first accessed. This is only a hint. Implementations are free to choose whether to use eager or lazy parsing regardless of the value of this option. However, setting this option true suggests that the protocol author believes that using lazy parsing on this field is worth the additional bookkeeping overhead typically needed to implement it. This option does not affect the public interface of any generated code; all method signatures remain the same. Furthermore, thread-safety of the interface is not affected by this option; const methods remain safe to call from multiple threads concurrently, while non-const methods continue to require exclusive access. Note that implementations may choose not to check required fields within a lazy sub-message. That is, calling IsInitialized() on the outer message may return true even if the inner message has missing required fields. This is necessary because otherwise the inner message would have to be parsed in order to perform the check, defeating the purpose of lazy parsing. An implementation which chooses not to check required fields must be consistent about it. That is, for any particular sub-message, the implementation must either *always* check its required fields, or *never* check its required fields, regardless of whether or not the message has been parsed.  Β  Β   Β  Β  Β(  Β"' θ  Θ/Ω Is this field deprecated? Depending on the target platform, this can emit Deprecated annotations for accessors, or it will be completely ignored; in the very least, this is a formalization for deprecating fields.  Θ  Θ   Θ  Θ  Θ.  Θ(- ?  Λ*1 For Google-internal migration only. Do not use.  Λ  Λ   Λ  Λ  Λ)  Λ#( O  Ο:A The parser stores options it doesn't recognize here. See above.  Ο  Ο   Ο3  Ο69 Z  M Clients can define custom options in extensions of this message. See above.           Τ " removed jtype  Τ  Τ  Τ  Χέ  Χ O  Ω:A The parser stores options it doesn't recognize here. See above.  Ω  Ω   Ω3  Ω69 Z  άM Clients can define custom options in extensions of this message. See above.  ά   ά   ά ίς ί ` γ R Set this option to true to allow mapping different tag names to the same value. γ γ  γ γ ε ι/Φ Is this enum deprecated? Depending on the target platform, this can emit Deprecated annotations for the enum, or it will be completely ignored; in the very least, this is a formalization for deprecating enums. ι ι  ι ι ι. ι(-   λ " javanano_as_lite  λ  λ  λ O ξ:A The parser stores options it doesn't recognize here. See above. ξ ξ  ξ3 ξ69 Z ρM Clients can define custom options in extensions of this message. See above. ρ  ρ  ρ τ€ τ χ ω/θ Is this enum value deprecated? Depending on the target platform, this can emit Deprecated annotations for the enum value, or it will be completely ignored; in the very least, this is a formalization for deprecating enum values. ω ω  ω ω ω. ω(- O ό:A The parser stores options it doesn't recognize here. See above. ό ό  ό3 ό69 Z M Clients can define custom options in extensions of this message. See above.      ‚” ‚ Ω 0ί Is this service deprecated? Depending on the target platform, this can emit Deprecated annotations for the service, or it will be completely ignored; in the very least, this is a formalization for deprecating services. 2θ Note: Field numbers 1 through 32 are reserved for Google's internal RPC framework. We apologize for hoarding these numbers to ourselves, but we were already using them long before we decided to release Protocol Buffers.       / ). O :A The parser stores options it doesn't recognize here. See above.    3 69 Z “M Clients can define custom options in extensions of this message. See above. “  “  “ –³ – Φ ‘0ά Is this method deprecated? Depending on the target platform, this can emit Deprecated annotations for the method, or it will be completely ignored; in the very least, this is a formalization for deprecating methods. 2θ Note: Field numbers 1 through 32 are reserved for Google's internal RPC framework. We apologize for hoarding these numbers to ourselves, but we were already using them long before we decided to release Protocol Buffers. ‘ ‘  ‘ ‘ ‘ / ‘). π ¦ͺί Is this method side-effect-free (or safe in HTTP parlance), or idempotent, or neither? HTTP based RPC implementation may choose GET verb for safe methods, and PUT verb for idempotent methods instead of the default POST. ¦  §  §  § $ ¨" implies idempotent  ¨  ¨ 7 ©"' idempotent, but may have side effects  ©  ©  «¬' « «  «- ¬ ¬ & ¬% O ―:A The parser stores options it doesn't recognize here. See above. ― ―  ―3 ―69 Z ²M Clients can define custom options in extensions of this message. See above. ²  ²  ² ‹ ΌΠό A message representing a option the parser does not recognize. This only appears in options protos created by the compiler::Parser class. DescriptorPool resolves these when building Descriptor objects. Therefore, options protos in descriptor objects (e.g. returned by Descriptor::options(), or produced by Descriptor::CopyTo()) will never have UninterpretedOptions in them. Ό Λ ΒΕΊ The name of the uninterpreted option. Each string represents a segment in a dot-separated name. is_extension is true iff a segment represents an extension (denoted with parentheses in options specs in .proto files). E.g.,{ ["foo", false], ["bar.baz", true], ["qux", false] } represents "foo.(bar.baz).qux". Β   Γ"  Γ  Γ   Γ  Γ !  Δ#  Δ  Δ   Δ  Δ!" Ζ Ζ Ζ  Ζ Ζ œ Κ' The value of the uninterpreted option, in whatever type the tokenizer identified it as during parsing. Exactly one of these should be set. Κ Κ  Κ" Κ%& Λ) Λ Λ  Λ$ Λ'( Μ( Μ Μ  Μ# Μ&' Ν# Ν Ν  Ν Ν!" Ξ" Ξ Ξ  Ξ Ξ ! Ο& Ο Ο  Ο! Ο$% Ϊ ΧΨj Encapsulates information about the original source file from which a FileDescriptorProto was generated. 2` =================================================================== Optional source code info Χ ‚ ƒ!σ A Location identifies a piece of source code in a .proto file which corresponds to a particular definition. This information is intended to be useful to IDEs, code indexers, documentation generators, and similar tools. For example, say we have a file like: message Foo { optional string foo = 1; } Let's look at just the field definition: optional string foo = 1; ^ ^^ ^^ ^ ^^^ a bc de f ghi We have the following locations: span path represents [a,i) [ 4, 0, 2, 0 ] The whole field definition. [a,b) [ 4, 0, 2, 0, 4 ] The label (optional). [c,d) [ 4, 0, 2, 0, 5 ] The type (string). [e,f) [ 4, 0, 2, 0, 1 ] The name (foo). [g,h) [ 4, 0, 2, 0, 3 ] The number (1). Notes: - A location may refer to a repeated field itself (i.e. not to any particular index within it). This is used whenever a set of elements are logically enclosed in a single code segment. For example, an entire extend block (possibly containing multiple extension definitions) will have an outer location whose path refers to the "extensions" repeated field without an index. - Multiple locations may have the same path. This happens when a single logical declaration is spread out across multiple places. The most obvious example is the "extend" block again -- there may be multiple extend blocks in the same scope, each of which will have the same path. - A location's span is not always a subset of its parent's span. For example, the "extendee" of an extension declaration appears at the beginning of the "extend" block and is shared by all extensions within the block. - Just because a location's span is a subset of some other location's span does not mean that it is a descendent. For example, a "group" defines both a type and a field in a single declaration. Thus, the locations corresponding to the type and field and their components will overlap. - Code which tries to interpret locations should probably be designed to ignore those that it doesn't understand, as more types of locations could be recorded in the future. ƒ ƒ  ƒ ƒ  „Χ „  ƒ œ*ς Identifies which part of the FileDescriptorProto was defined at this location. Each element is a field number or an index. They form a path from the root FileDescriptorProto to the place where the definition. For example, this path: [ 4, 3, 2, 7, 1 ] refers to: file.message_type(3) // 4, 3 .field(7) // 2, 7 .name() // 1 This is because FileDescriptorProto.message_type has field number 4: repeated DescriptorProto message_type = 4; and DescriptorProto.field has field number 2: repeated FieldDescriptorProto field = 2; and FieldDescriptorProto.name has field number 1: optional string name = 1; Thus, the above path gives the location of a field name. If we removed the last element: [ 4, 3, 2, 7 ] this path refers to the whole field declaration (from the beginning of the label to the terminating semicolon).  œ  œ   œ  œ  œ)  œ(  £*Α Always has exactly three or four elements: start line, start column, end line (optional, otherwise assumed same as start line), end column. These are packed into a single field for efficiency. Note that line and column numbers are zero-based -- typically you will want to add 1 to each before displaying to a user.  £  £   £  £  £)  £( ₯ Τ)” If this SourceCodeInfo represents a complete declaration, these are any comments appearing before and after the declaration which appear to be attached to the declaration. A series of line comments appearing on consecutive lines, with no other tokens appearing on those lines, will be treated as a single comment. leading_detached_comments will keep paragraphs of comments that appear before (but not connected to) the current element. Each paragraph, separated by empty lines, will be one comment element in the repeated field. Only the comment content is provided; comment markers (e.g. //) are stripped out. For block comments, leading whitespace and an asterisk will be stripped from the beginning of each line other than the first. Newlines are included in the output. Examples: optional int32 foo = 1; // Comment attached to foo. // Comment attached to bar. optional int32 bar = 2; optional string baz = 3; // Comment attached to baz. // Another line attached to baz. // Comment attached to qux. // // Another line attached to qux. optional double qux = 4; // Detached comment for corge. This is not leading or trailing comments // to qux or corge because there are blank lines separating it from // both. // Detached comment for corge paragraph 2. optional string corge = 5; /* Block comment attached * to corge. Leading asterisks * will be removed. */ /* Block comment attached to * grault. */ optional int32 grault = 6; // ignored detached comments.  Τ  Τ   Τ$  Τ'(  Υ*  Υ  Υ   Υ%  Υ()  Φ2  Φ  Φ   Φ-  Φ01 ξ έςί Describes the relationship between generated code and its original source file. A GeneratedCodeInfo message is associated with only one generated source file, but may contain references to different source .proto files. έ x ΰ%j An Annotation connects some span of text in generated code to an element of its generating .proto file. ΰ ΰ  ΰ ΰ#$  αρ α   δ* Identifies the element in the original source .proto file. This field is formatted the same as SourceCodeInfo.Location.path.  δ  δ   δ  δ  δ)  δ( O η$? Identifies the filesystem path to the original source .proto.  η  η   η  η"# w λg Identifies the starting offset in bytes in the generated code that relates to the identified object.  λ  λ   λ  λ Ϋ πΚ Identifies the ending offset in bytes in the generated code that relates to the identified offset. The end offset should be one past the last relevant byte (so the length of the text = end - begin).  π  π   π  π Τ extend.protocom.pseudomuto.protokit.v1 google/protobuf/descriptor.proto:? extend_file.google.protobuf.FileOptions œ (R extendFile:H extend_service.google.protobuf.ServiceOptions œ (R extendService:E extend_method.google.protobuf.MethodOptions œ (R extendMethod:? extend_enum.google.protobuf.EnumOptions œ (R extendEnum:O extend_enum_value!.google.protobuf.EnumValueOptions œ (RextendEnumValue:H extend_message.google.protobuf.MessageOptions œ (R extendMessage:B extend_field.google.protobuf.FieldOptions œ (R extendFieldJ“ 5    )  " 2   '* Extension of protobuf file options.  $  #        # 5 ** Extension of protobuf service options. ' &     !& 4 )* Extension of protobuf method options. & $      % 2  '* Extension of protobuf enum options. $ "     # 8 %'-* Extension of protobuf enum value options. &* %' & &  &! &$) 5 ,.** Extension of protobuf message options. -' ,% - -  - -!& 3 35(* Extension of protobuf field options. 4% 3# 4 4  4 4$ ΐ booking.protocom.pseudomuto.protokit.v1 extend.proto"Λ BookingStatus id (Rid description ( R descriptionU status_code (24.com.pseudomuto.protokit.v1.BookingStatus.StatusCodeR statusCode"' StatusCode OKΘ BAD_REQUEST*d€€€€"£ Booking vehicle_id (R vehicleId customer_id (R customerIdA status ( 2).com.pseudomuto.protokit.v1.BookingStatusRstatus+ confirmation_sent (RconfirmationSent5 payment_received (:trueB€β RpaymentReceived% reference_num (HR referenceNum% reference_tag ( HR referenceTag2S optional_field_1).com.pseudomuto.protokit.v1.BookingStatuse ( RoptionalField1:€β B things*4 BookingType IMMEDIATEd FUTUREe€β €β 2{ BookingServicec BookVehicle#.com.pseudomuto.protokit.v1.Booking).com.pseudomuto.protokit.v1.BookingStatus"€β €β :P country).com.pseudomuto.protokit.v1.BookingStatusd ( :chinaB€β RcountryB€β J… X     v  "l* Booking related messages. This file is really just an example. The data model is completely fictional.   7  œ 7 6 ** Service for handling vehicle bookings.  <  œ< ` R/ Used to book a vehicle. Pass in a Booking and a BookingStatus will be returned.   %2 =   œ= ; */* Represents the status of a vehicle booking.  /  #!* A flag for the status result.    !" OK result.  !  !  "" BAD result.  "  " ) %&"/ Unique booking status ID. % %  % %$% : &&"-/ Booking status description. E.g. "Active". & &  & &$% * '&"/ The status of this status? ' '  '! '$% ) )  )  ) ! -0 File-level extension 1 /f& The country the booking occurred in. - / /  / / / e /+2  œ/4d $ 5:* The type of booking. 5 69  œ69 ! 8" Immediate booking. 8 8  9J" Future booking. 9 9 9I   œ9H h AX\* Represents the booking of a vehicle. Vehicles are some cool shit. But drive carefully! A B<  œB< % D$"/ ID of booked vehicle. D D  D D"# 1 E$"$/ Customer that booked the vehicle. E E  E E"# & F$"/ Status of the booking. F F  F F"# 3 I&&* Has booking confirmation been sent? I I  I! I$% * Lh* Has payment been received? L L  L L#$ L%g L04   œL6f NQ N + O" the numeric reference number O O  O ) P" the reference tag (string) P P  P 2 UW2% Nested extentions are also a thing. ? V+"2 An optional field to be used however you please. U  V V  V$ V'* «, google/protobuf/any.protogoogle.protobuf"6 Any type_url ( RtypeUrl value ( RvalueBo com.google.protobufBAnyProtoPZ%github.com/golang/protobuf/ptypes/any’GPBͺGoogle.Protobuf.WellKnownTypesJΛ* ™ Μ  2Α Protocol Buffers - Google's data interchange format Copyright 2008 Google Inc. All rights reserved. https://developers.google.com/protocol-buffers/ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.     "; %";  #<  #<  $, $,  %) %)  &"  &"  '! $'! δ y™Φ `Any` contains an arbitrary serialized protocol buffer message along with a URL that describes the type of the serialized message. Protobuf library provides support to pack/unpack Any values in the form of utility functions or additional generated methods of the Any type. Example 1: Pack and unpack a message in C++. Foo foo = ...; Any any; any.PackFrom(foo); ... if (any.UnpackTo(&foo)) { ... } Example 2: Pack and unpack a message in Java. Foo foo = ...; Any any = Any.pack(foo); ... if (any.is(Foo.class)) { foo = any.unpack(Foo.class); } Example 3: Pack and unpack a message in Python. foo = Foo(...) any = Any() any.Pack(foo) ... if any.Is(Foo.DESCRIPTOR): any.Unpack(foo) ... Example 4: Pack and unpack a message in Go foo := &pb.Foo{...} any, err := ptypes.MarshalAny(foo) ... foo := &pb.Foo{} if err := ptypes.UnmarshalAny(any, foo); err != nil { ... } The pack methods provided by protobuf library will by default use 'type.googleapis.com/full.type.name' as the type URL and the unpack methods only use the fully qualified type name after the last '/' in the type URL, for example "foo.bar.com/x/y.z" will yield type name "y.z". JSON ==== The JSON representation of an `Any` value uses the regular representation of the deserialized, embedded message, with an additional field `@type` which contains the type URL. Example: package google.profile; message Person { string first_name = 1; string last_name = 2; } { "@type": "type.googleapis.com/google.profile.Person", "firstName": , "lastName": } If the embedded message type is well-known and has a custom JSON representation, that representation will be embedded adding a field `value` which holds the custom JSON in addition to the `@type` field. Example (for message [google.protobuf.Duration][]): { "@type": "type.googleapis.com/google.protobuf.Duration", "value": "1.212s" } y ‘ •’ A URL/resource name that uniquely identifies the type of the serialized protocol buffer message. The last segment of the URL's path must represent the fully qualified name of the type (as in `path/google.protobuf.Duration`). The name should be in a canonical form (e.g., leading "." is not accepted). In practice, teams usually precompile into the binary all types that they expect it to use in the context of Any. However, for URLs which use the scheme `http`, `https`, or no scheme, one can optionally set up a type server that maps type URLs to message definitions as follows: * If no scheme is provided, `https` is assumed. * An HTTP GET on the URL must yield a [google.protobuf.Type][] value in binary format, or produce an error. * Applications are allowed to cache lookup results based on the URL, or have them precompiled into a binary to avoid any lookup. Therefore, binary compatibility needs to be preserved on changes to types. (Use versioned type names to manage breaking changes.) Note: this functionality is not currently available in the official protobuf release, and it is not used for type URLs beginning with type.googleapis.com. Schemes other than `http`, `https` (or the empty scheme) might be used with implementation specific semantics.  •y • •  • W ˜I Must be a valid serialized protocol buffer of the above specified type.  ˜• ˜ ˜ ˜bproto3 ―/ google/protobuf/timestamp.protogoogle.protobuf"; Timestamp seconds (Rseconds nanos (RnanosB~ com.google.protobufBTimestampProtoPZ+github.com/golang/protobuf/ptypes/timestampψ’GPBͺGoogle.Protobuf.WellKnownTypesJ΅- † Μ  2Α Protocol Buffers - Google's data interchange format Copyright 2008 Google Inc. All rights reserved. https://developers.google.com/protocol-buffers/ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.     "; %";  # #  $B  $B  %, %,  &/ &/  '"  '"  (! $(! ΄ z†¦ A Timestamp represents a point in time independent of any time zone or calendar, represented as seconds and fractions of seconds at nanosecond resolution in UTC Epoch time. It is encoded using the Proleptic Gregorian Calendar which extends the Gregorian calendar backwards to year one. It is encoded assuming all minutes are 60 seconds long, i.e. leap seconds are "smeared" so that no leap second table is needed for interpretation. Range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By restricting to that range, we ensure that we can convert to and from RFC 3339 date strings. See [https://www.ietf.org/rfc/rfc3339.txt](https://www.ietf.org/rfc/rfc3339.txt). # Examples Example 1: Compute Timestamp from POSIX `time()`. Timestamp timestamp; timestamp.set_seconds(time(NULL)); timestamp.set_nanos(0); Example 2: Compute Timestamp from POSIX `gettimeofday()`. struct timeval tv; gettimeofday(&tv, NULL); Timestamp timestamp; timestamp.set_seconds(tv.tv_sec); timestamp.set_nanos(tv.tv_usec * 1000); Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`. FILETIME ft; GetSystemTimeAsFileTime(&ft); UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime; // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z. Timestamp timestamp; timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL)); timestamp.set_nanos((INT32) ((ticks % 10000000) * 100)); Example 4: Compute Timestamp from Java `System.currentTimeMillis()`. long millis = System.currentTimeMillis(); Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000) .setNanos((int) ((millis % 1000) * 1000000)).build(); Example 5: Compute Timestamp from current time in Python. timestamp = Timestamp() timestamp.GetCurrentTime() # JSON Mapping In JSON format, the Timestamp type is encoded as a string in the [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the format is "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z" where {year} is always expressed using four digits while {month}, {day}, {hour}, {min}, and {sec} are zero-padded to two digits each. The fractional seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution), are optional. The "Z" suffix indicates the timezone ("UTC"); the timezone is required. A proto3 JSON serializer should always use UTC (as indicated by "Z") when printing the Timestamp type and a proto3 JSON parser should be able to accept both UTC and other timezones (as indicated by an offset). For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past 01:30 UTC on January 15, 2017. In JavaScript, one can convert a Date object to this format using the standard [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString] method. In Python, a standard `datetime.datetime` object can be converted to this format using [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use the Joda Time's [`ISODateTimeFormat.dateTime()`]( http://www.joda.org/joda-time/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime-- ) to obtain a formatter capable of generating timestamps in this format. z œ Ž Represents seconds of UTC time since Unix epoch 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59Z inclusive. z    ε …Φ Non-negative fractions of a second at nanosecond resolution. Negative second values with fractions must still have non-negative nanos values that count forward in time. Must be from 0 to 999,999,999 inclusive.  … … … …bproto3 – todo_import.protocom.pseudomuto.protokit.v1"' ListItemDetails notes ( Rnotes*! ListItemDetailEnum DEFAULTJ      g "] This is really just in place to make sure imported files are included in the parsed result. $  Details for list items  & " Some notes for the item      5  ) A dummy enum to ensure importing works.   !  " The default value.    bproto3 Χ& todo.protocom.pseudomuto.protokit.v1google/protobuf/any.protogoogle/protobuf/timestamp.proto extend.prototodo_import.proto"Ϋ List id (Rid name ( B€β Rname8 type (2$.com.pseudomuto.protokit.v1.ListTypeRtype9 created_at ( 2.google.protobuf.TimestampR createdAt. details ( 2.google.protobuf.AnyRdetails:€β "' CreateListRequest name ( Rname"· CreateListResponse4 list ( 2 .com.pseudomuto.protokit.v1.ListRlistM status ( 25.com.pseudomuto.protokit.v1.CreateListResponse.StatusRstatus Status code (Rcode"› Item id (Rid title ( RtitleE completed (2'.com.pseudomuto.protokit.v1.Item.StatusR completed9 created_at ( 2.google.protobuf.TimestampR createdAtE details ( 2+.com.pseudomuto.protokit.v1.ListItemDetailsRdetails"$ Status PENDING COMPLETED"] AddItemRequest list_id (RlistId title ( Rtitle completed (R completed"G AddItemResponse4 item ( 2 .com.pseudomuto.protokit.v1.ItemRitem*4 ListType REMINDERS CHECKLIST€β €β 2γ Todoq CreateList-.com.pseudomuto.protokit.v1.CreateListRequest..com.pseudomuto.protokit.v1.CreateListResponse"€β b AddItem*.com.pseudomuto.protokit.v1.AddItemRequest+.com.pseudomuto.protokit.v1.AddItemResponse€β B Ztodo€β J– d D  : Top-level comments are attached to the syntax directive. " (    !     β "Χ The official documentation for the Todo API. Some parts of this file are unnecessarily complicated. In order to have a test for nested messages, enums, etc. I've added some odd looking implementation details. So you know, don't use this in real life for a todo service. The get started run the following: * `make setup` * `make test`  7  œ7 m %a A service for managing "todo" items. Add, complete, and remove your items on your todo lists.  <  œ< &  Create a new todo list  " -? =   œ= P $8C Add an item to your list Adds a new item to the specified list. $ $ $'6 * (- An enumeration of list types ( )9  œ)9 " +" The reminders type. + + " ,H" The checklist type. , , ,G   œ,F  08 A list object. 0 1<  œ1< " 3+" The id of the list. 31< 3 3 3)* $ 4^" The name of the list. 43+ 4 4 4)* 4+]   œ4,\  5+" The type of list 54^ 5 5  5)* * 6+" The timestamp for creation. 65+ 6 6& 6)* + 7+" Some arbitrary list details. 76+ 7 7 7)* 7 ;>+ A request object for creating todo lists. ; $ = The name of the list. =; = = = 3 AI' A successfully created list response. A * CE An internal status message C  ! D" The status code.  DC  D  D   D ) G" The list that was created. GE G G G + H" The status for the response. HG H H  H  LX A list item L 3 NQ% An enumeration of possible statuses N $ O" The pending status.  O  O & P" The completed status.  P  P " S+" The id of the item. SQ S S S)* % T+" The title of the item. TS+ T T  T)* . U+"! The current status of the item. UT+ U U  U)* * V+" The timestamp for creation. VU+ V V& V)*  W+" Item details. WV+ W W W)* 5 [_) A request message for adding new items. [ , \" The id of the list to add to. \[ \ \ \ % ]" The title of the item. ]\ ] ]  ] 4 ^"' Whether or not the item is completed. ^] ^ ^ ^ 1 bd% A successfully added item response. b , c" The list item that was added. cb c c cPbproto3golang-github-pseudomuto-protokit-0.2.1/fixtures/generate.go000066400000000000000000000002331515743464700243240ustar00rootroot00000000000000package main //go:generate protoc --descriptor_set_out=fileset.pb --include_imports --include_source_info -I. ./booking.proto ./todo.proto ./extend.proto golang-github-pseudomuto-protokit-0.2.1/fixtures/todo.proto000066400000000000000000000062121515743464700242400ustar00rootroot00000000000000// Top-level comments are attached to the syntax directive. syntax = "proto3"; import "google/protobuf/any.proto"; import "google/protobuf/timestamp.proto"; import "extend.proto"; import public "todo_import.proto"; option go_package = "todo"; // The official documentation for the Todo API. // // Some parts of this file are unnecessarily complicated. In order to have a test for nested messages, enums, etc. I've // added some odd looking implementation details. So you know, don't use this in real life for a todo service. // // The get started run the following: // // * `make setup` // * `make test` package com.pseudomuto.protokit.v1; option (com.pseudomuto.protokit.v1.extend_file) = true; // A service for managing "todo" items. // // Add, complete, and remove your items on your todo lists. service Todo { option (com.pseudomuto.protokit.v1.extend_service) = true; // Create a new todo list rpc CreateList(CreateListRequest) returns (CreateListResponse) { option (com.pseudomuto.protokit.v1.extend_method) = true; } // Add an item to your list // // Adds a new item to the specified list. rpc AddItem(AddItemRequest) returns (AddItemResponse); } // An enumeration of list types enum ListType { option (com.pseudomuto.protokit.v1.extend_enum) = true; REMINDERS = 0; // The reminders type. CHECKLIST = 1 [(com.pseudomuto.protokit.v1.extend_enum_value) = true]; // The checklist type. } // A list object. message List { option (com.pseudomuto.protokit.v1.extend_message) = true; int64 id = 1; // The id of the list. string name = 2 [(com.pseudomuto.protokit.v1.extend_field) = true]; // The name of the list. ListType type = 3; // The type of list google.protobuf.Timestamp created_at = 4; // The timestamp for creation. google.protobuf.Any details = 5; // Some arbitrary list details. } // A request object for creating todo lists. message CreateListRequest { // The name of the list. string name = 1; } // A successfully created list response. message CreateListResponse { // An internal status message message Status { sint32 code = 1; // The status code. } List list = 1; // The list that was created. Status status = 2; // The status for the response. } // A list item message Item { // An enumeration of possible statuses enum Status { PENDING = 0; // The pending status. COMPLETED = 1; // The completed status. } int64 id = 1; // The id of the item. string title = 2; // The title of the item. Status completed = 3; // The current status of the item. google.protobuf.Timestamp created_at = 4; // The timestamp for creation. ListItemDetails details = 5; // Item details. } // A request message for adding new items. message AddItemRequest { int64 list_id = 1; // The id of the list to add to. string title = 2; // The title of the item. bool completed = 3; // Whether or not the item is completed. } // A successfully added item response. message AddItemResponse { Item item = 1; // The list item that was added. } golang-github-pseudomuto-protokit-0.2.1/fixtures/todo_import.proto000066400000000000000000000005521515743464700256330ustar00rootroot00000000000000syntax = "proto3"; // This is really just in place to make sure imported files are included in the parsed result. package com.pseudomuto.protokit.v1; // Details for list items message ListItemDetails { string notes = 1; // Some notes for the item } // A dummy enum to ensure importing works. enum ListItemDetailEnum { DEFAULT = 0; // The default value. } golang-github-pseudomuto-protokit-0.2.1/go.mod000066400000000000000000000005621515743464700214450ustar00rootroot00000000000000module github.com/pseudomuto/protokit go 1.17 require ( github.com/golang/protobuf v1.0.0 github.com/stretchr/testify v1.2.1 google.golang.org/genproto v0.0.0-20180427144745-86e600f69ee4 ) require ( github.com/davecgh/go-spew v1.1.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect ) golang-github-pseudomuto-protokit-0.2.1/go.sum000066400000000000000000000021501515743464700214650ustar00rootroot00000000000000github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/golang/protobuf v1.0.0 h1:lsek0oXi8iFE9L+EXARyHIjU5rlWIhhTkjDz3vHhWWQ= github.com/golang/protobuf v1.0.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/testify v1.2.1 h1:52QO5WkIUcHGIR7EnGagH88x1bUzqGXTC5/1bDTUQ7U= github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= google.golang.org/genproto v0.0.0-20180427144745-86e600f69ee4 h1:0rk3/gV3HbvCeUzVMhdxV3TEVKMVPDnayjN7sYRmcxY= google.golang.org/genproto v0.0.0-20180427144745-86e600f69ee4/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= golang-github-pseudomuto-protokit-0.2.1/parser.go000066400000000000000000000210671515743464700221650ustar00rootroot00000000000000package protokit import ( "github.com/golang/protobuf/protoc-gen-go/descriptor" "github.com/golang/protobuf/protoc-gen-go/plugin" "context" "fmt" "strings" ) const ( // tag numbers in FileDescriptorProto packageCommentPath = 2 messageCommentPath = 4 enumCommentPath = 5 serviceCommentPath = 6 extensionCommentPath = 7 syntaxCommentPath = 12 // tag numbers in DescriptorProto messageFieldCommentPath = 2 // field messageMessageCommentPath = 3 // nested_type messageEnumCommentPath = 4 // enum_type messageExtensionCommentPath = 6 // extension // tag numbers in EnumDescriptorProto enumValueCommentPath = 2 // value // tag numbers in ServiceDescriptorProto serviceMethodCommentPath = 2 ) // ParseCodeGenRequest parses the given request into `FileDescriptor` objects. Only the `req.FilesToGenerate` will be // returned. // // For example, given the following invocation, only booking.proto will be returned even if it imports other protos: // // protoc --plugin=protoc-gen-test=./test -I. protos/booking.proto func ParseCodeGenRequest(req *plugin_go.CodeGeneratorRequest) []*FileDescriptor { allFiles := make(map[string]*FileDescriptor) genFiles := make([]*FileDescriptor, len(req.GetFileToGenerate())) for _, pf := range req.GetProtoFile() { allFiles[pf.GetName()] = parseFile(context.Background(), pf) } for i, f := range req.GetFileToGenerate() { genFiles[i] = allFiles[f] parseImports(genFiles[i], allFiles) } return genFiles } func parseFile(ctx context.Context, fd *descriptor.FileDescriptorProto) *FileDescriptor { comments := ParseComments(fd) file := &FileDescriptor{ comments: comments, FileDescriptorProto: fd, Comments: comments.Get(fmt.Sprintf("%d", packageCommentPath)), PackageComments: comments.Get(fmt.Sprintf("%d", packageCommentPath)), SyntaxComments: comments.Get(fmt.Sprintf("%d", syntaxCommentPath)), } if fd.Options != nil { file.setOptions(fd.Options) } fileCtx := ContextWithFileDescriptor(ctx, file) file.Enums = parseEnums(fileCtx, fd.GetEnumType()) file.Extensions = parseExtensions(fileCtx, fd.GetExtension()) file.Messages = parseMessages(fileCtx, fd.GetMessageType()) file.Services = parseServices(fileCtx, fd.GetService()) return file } func parseEnums(ctx context.Context, protos []*descriptor.EnumDescriptorProto) []*EnumDescriptor { enums := make([]*EnumDescriptor, len(protos)) file, _ := FileDescriptorFromContext(ctx) parent, hasParent := DescriptorFromContext(ctx) for i, ed := range protos { longName := ed.GetName() commentPath := fmt.Sprintf("%d.%d", enumCommentPath, i) if hasParent { longName = fmt.Sprintf("%s.%s", parent.GetLongName(), longName) commentPath = fmt.Sprintf("%s.%d.%d", parent.path, messageEnumCommentPath, i) } enums[i] = &EnumDescriptor{ common: newCommon(file, commentPath, longName), EnumDescriptorProto: ed, Comments: file.comments.Get(commentPath), Parent: parent, } if ed.Options != nil { enums[i].setOptions(ed.Options) } subCtx := ContextWithEnumDescriptor(ctx, enums[i]) enums[i].Values = parseEnumValues(subCtx, ed.GetValue()) } return enums } func parseEnumValues(ctx context.Context, protos []*descriptor.EnumValueDescriptorProto) []*EnumValueDescriptor { values := make([]*EnumValueDescriptor, len(protos)) file, _ := FileDescriptorFromContext(ctx) enum, _ := EnumDescriptorFromContext(ctx) for i, vd := range protos { longName := fmt.Sprintf("%s.%s", enum.GetLongName(), vd.GetName()) values[i] = &EnumValueDescriptor{ common: newCommon(file, "", longName), EnumValueDescriptorProto: vd, Enum: enum, Comments: file.comments.Get(fmt.Sprintf("%s.%d.%d", enum.path, enumValueCommentPath, i)), } if vd.Options != nil { values[i].setOptions(vd.Options) } } return values } func parseExtensions(ctx context.Context, protos []*descriptor.FieldDescriptorProto) []*ExtensionDescriptor { exts := make([]*ExtensionDescriptor, len(protos)) file, _ := FileDescriptorFromContext(ctx) parent, hasParent := DescriptorFromContext(ctx) for i, ext := range protos { commentPath := fmt.Sprintf("%d.%d", extensionCommentPath, i) longName := fmt.Sprintf("%s.%s", ext.GetExtendee(), ext.GetName()) if strings.Contains(longName, file.GetPackage()) { parts := strings.Split(ext.GetExtendee(), ".") longName = fmt.Sprintf("%s.%s", parts[len(parts)-1], ext.GetName()) } if hasParent { commentPath = fmt.Sprintf("%s.%d.%d", parent.path, messageExtensionCommentPath, i) } exts[i] = &ExtensionDescriptor{ common: newCommon(file, commentPath, longName), FieldDescriptorProto: ext, Comments: file.comments.Get(commentPath), Parent: parent, } if ext.Options != nil { exts[i].setOptions(ext.Options) } } return exts } func parseImports(fd *FileDescriptor, allFiles map[string]*FileDescriptor) { fd.Imports = make([]*ImportedDescriptor, 0) for _, index := range fd.GetPublicDependency() { file := allFiles[fd.GetDependency()[index]] for _, d := range file.GetMessages() { // skip map entry objects if !d.GetOptions().GetMapEntry() { fd.Imports = append(fd.Imports, &ImportedDescriptor{d.common}) } } for _, e := range file.GetEnums() { fd.Imports = append(fd.Imports, &ImportedDescriptor{e.common}) } for _, ext := range file.GetExtensions() { fd.Imports = append(fd.Imports, &ImportedDescriptor{ext.common}) } } } func parseMessages(ctx context.Context, protos []*descriptor.DescriptorProto) []*Descriptor { msgs := make([]*Descriptor, len(protos)) file, _ := FileDescriptorFromContext(ctx) parent, hasParent := DescriptorFromContext(ctx) for i, md := range protos { longName := md.GetName() commentPath := fmt.Sprintf("%d.%d", messageCommentPath, i) if hasParent { longName = fmt.Sprintf("%s.%s", parent.GetLongName(), longName) commentPath = fmt.Sprintf("%s.%d.%d", parent.path, messageMessageCommentPath, i) } msgs[i] = &Descriptor{ common: newCommon(file, commentPath, longName), DescriptorProto: md, Comments: file.comments.Get(commentPath), Parent: parent, } if md.Options != nil { msgs[i].setOptions(md.Options) } msgCtx := ContextWithDescriptor(ctx, msgs[i]) msgs[i].Enums = parseEnums(msgCtx, md.GetEnumType()) msgs[i].Extensions = parseExtensions(msgCtx, md.GetExtension()) msgs[i].Fields = parseMessageFields(msgCtx, md.GetField()) msgs[i].Messages = parseMessages(msgCtx, md.GetNestedType()) } return msgs } func parseMessageFields(ctx context.Context, protos []*descriptor.FieldDescriptorProto) []*FieldDescriptor { fields := make([]*FieldDescriptor, len(protos)) file, _ := FileDescriptorFromContext(ctx) message, _ := DescriptorFromContext(ctx) for i, fd := range protos { longName := fmt.Sprintf("%s.%s", message.GetLongName(), fd.GetName()) fields[i] = &FieldDescriptor{ common: newCommon(file, "", longName), FieldDescriptorProto: fd, Comments: file.comments.Get(fmt.Sprintf("%s.%d.%d", message.path, messageFieldCommentPath, i)), Message: message, } if fd.Options != nil { fields[i].setOptions(fd.Options) } } return fields } func parseServices(ctx context.Context, protos []*descriptor.ServiceDescriptorProto) []*ServiceDescriptor { svcs := make([]*ServiceDescriptor, len(protos)) file, _ := FileDescriptorFromContext(ctx) for i, sd := range protos { longName := sd.GetName() commentPath := fmt.Sprintf("%d.%d", serviceCommentPath, i) svcs[i] = &ServiceDescriptor{ common: newCommon(file, commentPath, longName), ServiceDescriptorProto: sd, Comments: file.comments.Get(commentPath), } if sd.Options != nil { svcs[i].setOptions(sd.Options) } svcCtx := ContextWithServiceDescriptor(ctx, svcs[i]) svcs[i].Methods = parseServiceMethods(svcCtx, sd.GetMethod()) } return svcs } func parseServiceMethods(ctx context.Context, protos []*descriptor.MethodDescriptorProto) []*MethodDescriptor { methods := make([]*MethodDescriptor, len(protos)) file, _ := FileDescriptorFromContext(ctx) svc, _ := ServiceDescriptorFromContext(ctx) for i, md := range protos { longName := fmt.Sprintf("%s.%s", svc.GetLongName(), md.GetName()) methods[i] = &MethodDescriptor{ common: newCommon(file, "", longName), MethodDescriptorProto: md, Service: svc, Comments: file.comments.Get(fmt.Sprintf("%s.%d.%d", svc.path, serviceMethodCommentPath, i)), } if md.Options != nil { methods[i].setOptions(md.Options) } } return methods } golang-github-pseudomuto-protokit-0.2.1/parser_bench_test.go000066400000000000000000000005611515743464700243570ustar00rootroot00000000000000package protokit_test import ( "testing" "github.com/pseudomuto/protokit" "github.com/pseudomuto/protokit/utils" ) func BenchmarkParseCodeGenRequest(b *testing.B) { fds, _ := utils.LoadDescriptorSet("fixtures", "fileset.pb") req := utils.CreateGenRequest(fds, "booking.proto", "todo.proto") for i := 0; i < b.N; i++ { protokit.ParseCodeGenRequest(req) } } golang-github-pseudomuto-protokit-0.2.1/parser_test.go000066400000000000000000000332671515743464700232310ustar00rootroot00000000000000package protokit_test import ( "github.com/golang/protobuf/proto" "github.com/golang/protobuf/protoc-gen-go/descriptor" "github.com/stretchr/testify/suite" "testing" "github.com/pseudomuto/protokit" "github.com/pseudomuto/protokit/utils" ) var ( proto2 *protokit.FileDescriptor proto3 *protokit.FileDescriptor ) type ParserTest struct { suite.Suite } func TestParser(t *testing.T) { suite.Run(t, new(ParserTest)) } func (assert *ParserTest) SetupSuite() { registerTestExtensions() set, err := utils.LoadDescriptorSet("fixtures", "fileset.pb") assert.NoError(err) req := utils.CreateGenRequest(set, "booking.proto", "todo.proto") files := protokit.ParseCodeGenRequest(req) proto2 = files[0] proto3 = files[1] } func registerTestExtensions() { var E_ExtendFile = &proto.ExtensionDesc{ ExtendedType: (*descriptor.FileOptions)(nil), ExtensionType: (*bool)(nil), Field: 20000, Name: "com.pseudomuto.protokit.v1.extend_file", Tag: "varint,20000,opt,name=extend_file,json=extendFile", Filename: "extend.proto", } var E_ExtendService = &proto.ExtensionDesc{ ExtendedType: (*descriptor.ServiceOptions)(nil), ExtensionType: (*bool)(nil), Field: 20000, Name: "com.pseudomuto.protokit.v1.extend_service", Tag: "varint,20000,opt,name=extend_service,json=extendService", Filename: "extend.proto", } var E_ExtendMethod = &proto.ExtensionDesc{ ExtendedType: (*descriptor.MethodOptions)(nil), ExtensionType: (*bool)(nil), Field: 20000, Name: "com.pseudomuto.protokit.v1.extend_method", Tag: "varint,20000,opt,name=extend_method,json=extendMethod", Filename: "extend.proto", } var E_ExtendEnum = &proto.ExtensionDesc{ ExtendedType: (*descriptor.EnumOptions)(nil), ExtensionType: (*bool)(nil), Field: 20000, Name: "com.pseudomuto.protokit.v1.extend_enum", Tag: "varint,20000,opt,name=extend_enum,json=extendEnum", Filename: "extend.proto", } var E_ExtendEnumValue = &proto.ExtensionDesc{ ExtendedType: (*descriptor.EnumValueOptions)(nil), ExtensionType: (*bool)(nil), Field: 20000, Name: "com.pseudomuto.protokit.v1.extend_enum_value", Tag: "varint,20000,opt,name=extend_enum_value,json=extendEnumValue", Filename: "extend.proto", } var E_ExtendMessage = &proto.ExtensionDesc{ ExtendedType: (*descriptor.MessageOptions)(nil), ExtensionType: (*bool)(nil), Field: 20000, Name: "com.pseudomuto.protokit.v1.extend_message", Tag: "varint,20000,opt,name=extend_message,json=extendMessage", Filename: "extend.proto", } var E_ExtendField = &proto.ExtensionDesc{ ExtendedType: (*descriptor.FieldOptions)(nil), ExtensionType: (*bool)(nil), Field: 20000, Name: "com.pseudomuto.protokit.v1.extend_field", Tag: "varint,20000,opt,name=extend_field,json=extendField", Filename: "extend.proto", } proto.RegisterExtension(E_ExtendFile) proto.RegisterExtension(E_ExtendService) proto.RegisterExtension(E_ExtendMethod) proto.RegisterExtension(E_ExtendEnum) proto.RegisterExtension(E_ExtendEnumValue) proto.RegisterExtension(E_ExtendMessage) proto.RegisterExtension(E_ExtendField) } func (assert *ParserTest) TestFileParsing() { assert.True(proto3.IsProto3()) assert.Equal("Top-level comments are attached to the syntax directive.", proto3.GetSyntaxComments().String()) assert.Contains(proto3.GetPackageComments().String(), "The official documentation for the Todo API.\n\n") assert.Len(proto3.GetExtensions(), 0) // no extensions in proto3 assert.False(proto2.IsProto3()) assert.Len(proto2.GetExtensions(), 1) } func (assert *ParserTest) TestFileImports() { assert.Require().Len(proto3.GetImports(), 2) imp := proto3.GetImports()[0] assert.NotNil(imp.GetFile()) assert.Equal("ListItemDetails", imp.GetLongName()) assert.Equal("com.pseudomuto.protokit.v1.ListItemDetails", imp.GetFullName()) } func (assert *ParserTest) TestFileEnums() { assert.Len(proto3.GetEnums(), 1) assert.Nil(proto3.GetEnum("swingandamiss")) enum := proto3.GetEnum("ListType") assert.Equal("ListType", enum.GetLongName()) assert.Equal("com.pseudomuto.protokit.v1.ListType", enum.GetFullName()) assert.True(enum.IsProto3()) assert.Nil(enum.GetParent()) assert.NotNil(enum.GetFile()) assert.Equal("An enumeration of list types", enum.GetComments().String()) assert.Equal("com.pseudomuto.protokit.v1", enum.GetPackage()) assert.Len(enum.GetValues(), 2) assert.Equal("REMINDERS", enum.GetValues()[0].GetName()) assert.Equal(enum, enum.GetValues()[0].GetEnum()) assert.Equal("The reminders type.", enum.GetNamedValue("REMINDERS").GetComments().String()) assert.Nil(enum.GetNamedValue("whodis")) } func (assert *ParserTest) TestFileExtensions() { ext := proto2.GetExtensions()[0] assert.Nil(ext.GetParent()) assert.Equal("country", ext.GetName()) assert.Equal("BookingStatus.country", ext.GetLongName()) assert.Equal("com.pseudomuto.protokit.v1.BookingStatus.country", ext.GetFullName()) assert.Equal("The country the booking occurred in.", ext.GetComments().String()) } func (assert *ParserTest) TestServices() { assert.Len(proto3.GetServices(), 1) assert.Nil(proto3.GetService("swingandamiss")) svc := proto3.GetService("Todo") assert.Equal("Todo", svc.GetLongName()) assert.Equal("com.pseudomuto.protokit.v1.Todo", svc.GetFullName()) assert.NotNil(svc.GetFile()) assert.True(svc.IsProto3()) assert.Contains(svc.GetComments().String(), "A service for managing \"todo\" items.\n\n") assert.Equal("com.pseudomuto.protokit.v1", svc.GetPackage()) assert.Len(svc.GetMethods(), 2) m := svc.GetNamedMethod("CreateList") assert.Equal("CreateList", m.GetName()) assert.Equal("Todo.CreateList", m.GetLongName()) assert.Equal("com.pseudomuto.protokit.v1.Todo.CreateList", m.GetFullName()) assert.NotNil(m.GetFile()) assert.Equal(svc, m.GetService()) assert.Equal("Create a new todo list", m.GetComments().String()) m = svc.GetNamedMethod("Todo.AddItem") assert.Equal("Add an item to your list\n\nAdds a new item to the specified list.", m.GetComments().String()) assert.Nil(svc.GetNamedMethod("wat")) } func (assert *ParserTest) TestFileMessages() { assert.Len(proto3.GetMessages(), 6) assert.Nil(proto3.GetMessage("swingandamiss")) m := proto3.GetMessage("AddItemRequest") assert.Equal("AddItemRequest", m.GetName()) assert.Equal("AddItemRequest", m.GetLongName()) assert.Equal("com.pseudomuto.protokit.v1.AddItemRequest", m.GetFullName()) assert.NotNil(m.GetFile()) assert.Nil(m.GetParent()) assert.Equal("A request message for adding new items.", m.GetComments().String()) assert.Equal("com.pseudomuto.protokit.v1", m.GetPackage()) assert.Len(m.GetMessageFields(), 3) assert.Nil(m.GetMessageField("swingandamiss")) // no extensions in proto3 assert.Len(m.GetExtensions(), 0) f := m.GetMessageField("completed") assert.Equal("completed", f.GetName()) assert.Equal("AddItemRequest.completed", f.GetLongName()) assert.Equal("com.pseudomuto.protokit.v1.AddItemRequest.completed", f.GetFullName()) assert.NotNil(f.GetFile()) assert.Equal(m, f.GetMessage()) assert.Equal("Whether or not the item is completed.", f.GetComments().String()) // just making sure google.protobuf.Any fields aren't special m = proto3.GetMessage("List") f = m.GetMessageField("details") assert.Equal("details", f.GetName()) assert.Equal("List.details", f.GetLongName()) assert.Equal("com.pseudomuto.protokit.v1.List.details", f.GetFullName()) // oneof fields should just expand to fields m = proto2.GetMessage("Booking") assert.NotNil(m.GetMessageField("reference_num")) assert.NotNil(m.GetMessageField("reference_tag")) assert.Equal("the numeric reference number", m.GetMessageField("reference_num").GetComments().String()) } func (assert *ParserTest) TestMessageEnums() { m := proto3.GetMessage("Item") assert.NotNil(m.GetFile()) assert.Len(m.GetEnums(), 1) assert.Nil(m.GetEnum("whodis")) e := m.GetEnum("Status") assert.Equal("Status", e.GetName()) assert.Equal("Item.Status", e.GetLongName()) assert.Equal("com.pseudomuto.protokit.v1.Item.Status", e.GetFullName()) assert.NotNil(e.GetFile()) assert.Equal(m, e.GetParent()) assert.Equal(e, m.GetEnum("Item.Status")) assert.Equal("An enumeration of possible statuses", e.GetComments().String()) assert.Len(e.GetValues(), 2) val := e.GetNamedValue("COMPLETED") assert.Equal("COMPLETED", val.GetName()) assert.Equal("Item.Status.COMPLETED", val.GetLongName()) assert.Equal("com.pseudomuto.protokit.v1.Item.Status.COMPLETED", val.GetFullName()) assert.Equal("The completed status.", val.GetComments().String()) assert.NotNil(val.GetFile()) } func (assert *ParserTest) TestMessageExtensions() { m := proto2.GetMessage("Booking") ext := m.GetExtensions()[0] assert.Equal(m, ext.GetParent()) assert.Equal(int32(101), ext.GetNumber()) assert.Equal("optional_field_1", ext.GetName()) assert.Equal("BookingStatus.optional_field_1", ext.GetLongName()) assert.Equal("com.pseudomuto.protokit.v1.BookingStatus.optional_field_1", ext.GetFullName()) assert.Equal("An optional field to be used however you please.", ext.GetComments().String()) } func (assert *ParserTest) TestNestedMessages() { m := proto3.GetMessage("CreateListResponse") assert.Len(m.GetMessages(), 1) assert.Nil(m.GetMessage("whodis")) n := m.GetMessage("Status") assert.Equal(n, m.GetMessage("CreateListResponse.Status")) // no extensions in proto3 assert.Len(n.GetExtensions(), 0) assert.Equal("Status", n.GetName()) assert.Equal("CreateListResponse.Status", n.GetLongName()) assert.Equal("com.pseudomuto.protokit.v1.CreateListResponse.Status", n.GetFullName()) assert.Equal("An internal status message", n.GetComments().String()) assert.NotNil(n.GetFile()) assert.Equal(m, n.GetParent()) f := n.GetMessageField("code") assert.Equal("CreateListResponse.Status.code", f.GetLongName()) assert.Equal("com.pseudomuto.protokit.v1.CreateListResponse.Status.code", f.GetFullName()) assert.NotNil(f.GetFile()) assert.Equal("The status code.", f.GetComments().String()) } func (assert *ParserTest) TestExtendedOptions() { assert.Contains(proto2.OptionExtensions, "com.pseudomuto.protokit.v1.extend_file") extendedValue, ok := proto2.OptionExtensions["com.pseudomuto.protokit.v1.extend_file"].(*bool) assert.True(ok) assert.True(*extendedValue) service := proto2.GetService("BookingService") assert.Contains(service.OptionExtensions, "com.pseudomuto.protokit.v1.extend_service") extendedValue, ok = service.OptionExtensions["com.pseudomuto.protokit.v1.extend_service"].(*bool) assert.True(ok) assert.True(*extendedValue) method := service.GetNamedMethod("BookVehicle") assert.Contains(method.OptionExtensions, "com.pseudomuto.protokit.v1.extend_method") extendedValue, ok = method.OptionExtensions["com.pseudomuto.protokit.v1.extend_method"].(*bool) assert.True(ok) assert.True(*extendedValue) message := proto2.GetMessage("Booking") assert.Contains(message.OptionExtensions, "com.pseudomuto.protokit.v1.extend_message") extendedValue, ok = message.OptionExtensions["com.pseudomuto.protokit.v1.extend_message"].(*bool) assert.True(ok) assert.True(*extendedValue) field := message.GetMessageField("payment_received") assert.Contains(field.OptionExtensions, "com.pseudomuto.protokit.v1.extend_field") extendedValue, ok = field.OptionExtensions["com.pseudomuto.protokit.v1.extend_field"].(*bool) assert.True(ok) assert.True(*extendedValue) enum := proto2.GetEnum("BookingType") assert.Contains(enum.OptionExtensions, "com.pseudomuto.protokit.v1.extend_enum") extendedValue, ok = enum.OptionExtensions["com.pseudomuto.protokit.v1.extend_enum"].(*bool) assert.True(ok) assert.True(*extendedValue) enumValue := enum.GetNamedValue("FUTURE") assert.Contains(enumValue.OptionExtensions, "com.pseudomuto.protokit.v1.extend_enum_value") extendedValue, ok = enumValue.OptionExtensions["com.pseudomuto.protokit.v1.extend_enum_value"].(*bool) assert.True(ok) assert.True(*extendedValue) assert.Contains(proto3.OptionExtensions, "com.pseudomuto.protokit.v1.extend_file") extendedValue, ok = proto3.OptionExtensions["com.pseudomuto.protokit.v1.extend_file"].(*bool) assert.True(ok) assert.True(*extendedValue) service = proto3.GetService("Todo") assert.Contains(service.OptionExtensions, "com.pseudomuto.protokit.v1.extend_service") extendedValue, ok = service.OptionExtensions["com.pseudomuto.protokit.v1.extend_service"].(*bool) assert.True(ok) assert.True(*extendedValue) method = service.GetNamedMethod("CreateList") assert.Contains(method.OptionExtensions, "com.pseudomuto.protokit.v1.extend_method") extendedValue, ok = method.OptionExtensions["com.pseudomuto.protokit.v1.extend_method"].(*bool) assert.True(ok) assert.True(*extendedValue) message = proto3.GetMessage("List") assert.Contains(message.OptionExtensions, "com.pseudomuto.protokit.v1.extend_message") extendedValue, ok = message.OptionExtensions["com.pseudomuto.protokit.v1.extend_message"].(*bool) assert.True(ok) assert.True(*extendedValue) field = message.GetMessageField("name") assert.Contains(field.OptionExtensions, "com.pseudomuto.protokit.v1.extend_field") extendedValue, ok = field.OptionExtensions["com.pseudomuto.protokit.v1.extend_field"].(*bool) assert.True(ok) assert.True(*extendedValue) enum = proto3.GetEnum("ListType") assert.Contains(enum.OptionExtensions, "com.pseudomuto.protokit.v1.extend_enum") extendedValue, ok = enum.OptionExtensions["com.pseudomuto.protokit.v1.extend_enum"].(*bool) assert.True(ok) assert.True(*extendedValue) enumValue = enum.GetNamedValue("CHECKLIST") assert.Contains(enumValue.OptionExtensions, "com.pseudomuto.protokit.v1.extend_enum_value") extendedValue, ok = enumValue.OptionExtensions["com.pseudomuto.protokit.v1.extend_enum_value"].(*bool) assert.True(ok) assert.True(*extendedValue) } golang-github-pseudomuto-protokit-0.2.1/plugin.go000066400000000000000000000026531515743464700221670ustar00rootroot00000000000000package protokit import ( "github.com/golang/protobuf/proto" "github.com/golang/protobuf/protoc-gen-go/plugin" "fmt" "io" "io/ioutil" "os" ) // Plugin describes an interface for running protoc code generator plugins type Plugin interface { Generate(req *plugin_go.CodeGeneratorRequest) (*plugin_go.CodeGeneratorResponse, error) } // RunPlugin runs the supplied plugin by reading input from stdin and generating output to stdout. func RunPlugin(p Plugin) error { return RunPluginWithIO(p, os.Stdin, os.Stdout) } // RunPluginWithIO runs the supplied plugin using the supplied reader and writer for IO. func RunPluginWithIO(p Plugin, r io.Reader, w io.Writer) error { req, err := readRequest(r) if err != nil { return err } resp, err := p.Generate(req) if err != nil { return err } return writeResponse(w, resp) } func readRequest(r io.Reader) (*plugin_go.CodeGeneratorRequest, error) { data, err := ioutil.ReadAll(r) if err != nil { return nil, err } req := new(plugin_go.CodeGeneratorRequest) if err = proto.Unmarshal(data, req); err != nil { return nil, err } if len(req.GetFileToGenerate()) == 0 { return nil, fmt.Errorf("no files were supplied to the generator") } return req, nil } func writeResponse(w io.Writer, resp *plugin_go.CodeGeneratorResponse) error { data, err := proto.Marshal(resp) if err != nil { return err } if _, err := w.Write(data); err != nil { return err } return nil } golang-github-pseudomuto-protokit-0.2.1/plugin_test.go000066400000000000000000000046771515743464700232360ustar00rootroot00000000000000package protokit_test import ( "github.com/golang/protobuf/proto" "github.com/golang/protobuf/protoc-gen-go/plugin" "github.com/stretchr/testify/suite" "bytes" "errors" "testing" "github.com/pseudomuto/protokit" "github.com/pseudomuto/protokit/utils" ) type PluginTest struct { suite.Suite } func TestPlugin(t *testing.T) { suite.Run(t, new(PluginTest)) } func (assert *PluginTest) TestRunPlugin() { fds, err := utils.LoadDescriptorSet("fixtures", "fileset.pb") assert.NoError(err) req := utils.CreateGenRequest(fds, "booking.proto", "todo.proto") data, err := proto.Marshal(req) assert.NoError(err) in := bytes.NewBuffer(data) out := new(bytes.Buffer) assert.NoError(protokit.RunPluginWithIO(new(OkPlugin), in, out)) assert.NotEmpty(out) } func (assert *PluginTest) TestRunPluginInputError() { in := bytes.NewBufferString("Not a codegen request") out := new(bytes.Buffer) err := protokit.RunPluginWithIO(nil, in, out) assert.EqualError(err, "proto: can't skip unknown wire type 6 for plugin_go.CodeGeneratorRequest") assert.Empty(out) } func (assert *PluginTest) TestRunPluginNoFilesToGenerate() { fds, err := utils.LoadDescriptorSet("fixtures", "fileset.pb") assert.NoError(err) req := utils.CreateGenRequest(fds) data, err := proto.Marshal(req) assert.NoError(err) in := bytes.NewBuffer(data) out := new(bytes.Buffer) err = protokit.RunPluginWithIO(new(ErrorPlugin), in, out) assert.EqualError(err, "no files were supplied to the generator") assert.Empty(out) } func (assert *PluginTest) TestRunPluginGeneratorError() { fds, err := utils.LoadDescriptorSet("fixtures", "fileset.pb") assert.NoError(err) req := utils.CreateGenRequest(fds, "booking.proto", "todo.proto") data, err := proto.Marshal(req) assert.NoError(err) in := bytes.NewBuffer(data) out := new(bytes.Buffer) err = protokit.RunPluginWithIO(new(ErrorPlugin), in, out) assert.EqualError(err, "generator error") assert.Empty(out) } type ErrorPlugin struct{} func (ep *ErrorPlugin) Generate(r *plugin_go.CodeGeneratorRequest) (*plugin_go.CodeGeneratorResponse, error) { return nil, errors.New("generator error") } type OkPlugin struct{} func (op *OkPlugin) Generate(r *plugin_go.CodeGeneratorRequest) (*plugin_go.CodeGeneratorResponse, error) { resp := new(plugin_go.CodeGeneratorResponse) resp.File = append(resp.File, &plugin_go.CodeGeneratorResponse_File{ Name: proto.String("myfile.out"), Content: proto.String("someoutput"), }) return resp, nil } golang-github-pseudomuto-protokit-0.2.1/types.go000066400000000000000000000243051515743464700220330ustar00rootroot00000000000000package protokit import ( "github.com/golang/protobuf/proto" "github.com/golang/protobuf/protoc-gen-go/descriptor" "fmt" "strings" ) type common struct { file *FileDescriptor path string LongName string FullName string OptionExtensions map[string]interface{} } func newCommon(f *FileDescriptor, path, longName string) common { fn := longName if !strings.HasPrefix(fn, ".") { fn = fmt.Sprintf("%s.%s", f.GetPackage(), longName) } return common{ file: f, path: path, LongName: longName, FullName: fn, } } // GetFile returns the FileDescriptor that contains this object func (c *common) GetFile() *FileDescriptor { return c.file } // GetPackage returns the package this object is in func (c *common) GetPackage() string { return c.file.GetPackage() } // GetLongName returns the name prefixed with the dot-separated parent descriptor's name (if any) func (c *common) GetLongName() string { return c.LongName } // GetFullName returns the `LongName` prefixed with the package this object is in func (c *common) GetFullName() string { return c.FullName } // IsProto3 returns whether or not this is a proto3 object func (c *common) IsProto3() bool { return c.file.GetSyntax() == "proto3" } func getOptions(options proto.Message) (m map[string]interface{}) { for _, extension := range proto.RegisteredExtensions(options) { if !proto.HasExtension(options, extension) { continue } ext, err := proto.GetExtension(options, extension) if err != nil { continue } if m == nil { m = make(map[string]interface{}) } m[extension.Name] = ext } return m } func (c *common) setOptions(options proto.Message) { if opts := getOptions(options); len(opts) > 0 { if c.OptionExtensions == nil { c.OptionExtensions = opts return } for k, v := range opts { c.OptionExtensions[k] = v } } } // An ImportedDescriptor describes a type that was imported by a FileDescriptor. type ImportedDescriptor struct { common } // A FileDescriptor describes a single proto file with all of its messages, enums, services, etc. type FileDescriptor struct { comments Comments *descriptor.FileDescriptorProto Comments *Comment // Deprecated: see PackageComments PackageComments *Comment SyntaxComments *Comment Enums []*EnumDescriptor Extensions []*ExtensionDescriptor Imports []*ImportedDescriptor Messages []*Descriptor Services []*ServiceDescriptor OptionExtensions map[string]interface{} } // IsProto3 returns whether or not this file is a proto3 file func (f *FileDescriptor) IsProto3() bool { return f.GetSyntax() == "proto3" } // GetComments returns the file's package comments. // // Deprecated: please see GetPackageComments func (f *FileDescriptor) GetComments() *Comment { return f.Comments } // GetPackageComments returns the file's package comments func (f *FileDescriptor) GetPackageComments() *Comment { return f.PackageComments } // GetSyntaxComments returns the file's syntax comments func (f *FileDescriptor) GetSyntaxComments() *Comment { return f.SyntaxComments } // GetEnums returns the top-level enumerations defined in this file func (f *FileDescriptor) GetEnums() []*EnumDescriptor { return f.Enums } // GetExtensions returns the top-level (file) extensions defined in this file func (f *FileDescriptor) GetExtensions() []*ExtensionDescriptor { return f.Extensions } // GetImports returns the proto files imported by this file func (f *FileDescriptor) GetImports() []*ImportedDescriptor { return f.Imports } // GetMessages returns the top-level messages defined in this file func (f *FileDescriptor) GetMessages() []*Descriptor { return f.Messages } // GetServices returns the services defined in this file func (f *FileDescriptor) GetServices() []*ServiceDescriptor { return f.Services } // GetEnum returns the enumeration with the specified name (returns `nil` if not found) func (f *FileDescriptor) GetEnum(name string) *EnumDescriptor { for _, e := range f.GetEnums() { if e.GetName() == name || e.GetLongName() == name { return e } } return nil } // GetMessage returns the message with the specified name (returns `nil` if not found) func (f *FileDescriptor) GetMessage(name string) *Descriptor { for _, m := range f.GetMessages() { if m.GetName() == name || m.GetLongName() == name { return m } } return nil } // GetService returns the service with the specified name (returns `nil` if not found) func (f *FileDescriptor) GetService(name string) *ServiceDescriptor { for _, s := range f.GetServices() { if s.GetName() == name || s.GetLongName() == name { return s } } return nil } func (f *FileDescriptor) setOptions(options proto.Message) { if opts := getOptions(options); len(opts) > 0 { if f.OptionExtensions == nil { f.OptionExtensions = opts return } for k, v := range opts { f.OptionExtensions[k] = v } } } // An EnumDescriptor describe an enum type type EnumDescriptor struct { common *descriptor.EnumDescriptorProto Parent *Descriptor Values []*EnumValueDescriptor Comments *Comment } // GetComments returns a description of this enum func (e *EnumDescriptor) GetComments() *Comment { return e.Comments } // GetParent returns the parent message (if any) that contains this enum func (e *EnumDescriptor) GetParent() *Descriptor { return e.Parent } // GetValues returns the available values for this enum func (e *EnumDescriptor) GetValues() []*EnumValueDescriptor { return e.Values } // GetNamedValue returns the value with the specified name (returns `nil` if not found) func (e *EnumDescriptor) GetNamedValue(name string) *EnumValueDescriptor { for _, v := range e.GetValues() { if v.GetName() == name { return v } } return nil } // An EnumValueDescriptor describes an enum value type EnumValueDescriptor struct { common *descriptor.EnumValueDescriptorProto Enum *EnumDescriptor Comments *Comment } // GetComments returns a description of the value func (v *EnumValueDescriptor) GetComments() *Comment { return v.Comments } // GetEnum returns the parent enumeration that contains this value func (v *EnumValueDescriptor) GetEnum() *EnumDescriptor { return v.Enum } // An ExtensionDescriptor describes a protobuf extension. If it's a top-level extension it's parent will be `nil` type ExtensionDescriptor struct { common *descriptor.FieldDescriptorProto Parent *Descriptor Comments *Comment } // GetComments returns a description of the extension func (e *ExtensionDescriptor) GetComments() *Comment { return e.Comments } // GetParent returns the descriptor that defined this extension (if any) func (e *ExtensionDescriptor) GetParent() *Descriptor { return e.Parent } // A Descriptor describes a message type Descriptor struct { common *descriptor.DescriptorProto Parent *Descriptor Comments *Comment Enums []*EnumDescriptor Extensions []*ExtensionDescriptor Fields []*FieldDescriptor Messages []*Descriptor } // GetComments returns a description of the message func (m *Descriptor) GetComments() *Comment { return m.Comments } // GetParent returns the parent descriptor (if any) that defines this descriptor func (m *Descriptor) GetParent() *Descriptor { return m.Parent } // GetEnums returns the nested enumerations within the message func (m *Descriptor) GetEnums() []*EnumDescriptor { return m.Enums } // GetExtensions returns the message-level extensions defined by this message func (m *Descriptor) GetExtensions() []*ExtensionDescriptor { return m.Extensions } // GetMessages returns the nested messages within the message func (m *Descriptor) GetMessages() []*Descriptor { return m.Messages } // GetMessageFields returns the message fields func (m *Descriptor) GetMessageFields() []*FieldDescriptor { return m.Fields } // GetEnum returns the enum with the specified name. The name can be either simple, or fully qualified (returns `nil` if // not found) func (m *Descriptor) GetEnum(name string) *EnumDescriptor { for _, e := range m.GetEnums() { // can lookup by name or message prefixed name (qualified) if e.GetName() == name || e.GetLongName() == name { return e } } return nil } // GetMessage returns the nested message with the specified name. The name can be simple or fully qualified (returns // `nil` if not found) func (m *Descriptor) GetMessage(name string) *Descriptor { for _, msg := range m.GetMessages() { // can lookup by name or message prefixed name (qualified) if msg.GetName() == name || msg.GetLongName() == name { return msg } } return nil } // GetMessageField returns the field with the specified name (returns `nil` if not found) func (m *Descriptor) GetMessageField(name string) *FieldDescriptor { for _, f := range m.GetMessageFields() { if f.GetName() == name || f.GetLongName() == name { return f } } return nil } // A FieldDescriptor describes a message field type FieldDescriptor struct { common *descriptor.FieldDescriptorProto Comments *Comment Message *Descriptor } // GetComments returns a description of the field func (mf *FieldDescriptor) GetComments() *Comment { return mf.Comments } // GetMessage returns the descriptor that defines this field func (mf *FieldDescriptor) GetMessage() *Descriptor { return mf.Message } // A ServiceDescriptor describes a service type ServiceDescriptor struct { common *descriptor.ServiceDescriptorProto Comments *Comment Methods []*MethodDescriptor } // GetComments returns a description of the service func (s *ServiceDescriptor) GetComments() *Comment { return s.Comments } // GetMethods returns the methods for the service func (s *ServiceDescriptor) GetMethods() []*MethodDescriptor { return s.Methods } // GetNamedMethod returns the method with the specified name (if found) func (s *ServiceDescriptor) GetNamedMethod(name string) *MethodDescriptor { for _, m := range s.GetMethods() { if m.GetName() == name || m.GetLongName() == name { return m } } return nil } // A MethodDescriptor describes a method in a service type MethodDescriptor struct { common *descriptor.MethodDescriptorProto Comments *Comment Service *ServiceDescriptor } // GetComments returns a description of the method func (m *MethodDescriptor) GetComments() *Comment { return m.Comments } // GetService returns the service descriptor that defines this method func (m *MethodDescriptor) GetService() *ServiceDescriptor { return m.Service } golang-github-pseudomuto-protokit-0.2.1/utils/000077500000000000000000000000001515743464700214745ustar00rootroot00000000000000golang-github-pseudomuto-protokit-0.2.1/utils/protobuf.go000066400000000000000000000051141515743464700236640ustar00rootroot00000000000000package utils import ( "github.com/golang/protobuf/proto" "github.com/golang/protobuf/protoc-gen-go/descriptor" "github.com/golang/protobuf/protoc-gen-go/plugin" "errors" "io/ioutil" "path/filepath" ) // CreateGenRequest creates a codegen request from a `FileDescriptorSet` func CreateGenRequest(fds *descriptor.FileDescriptorSet, filesToGen ...string) *plugin_go.CodeGeneratorRequest { req := new(plugin_go.CodeGeneratorRequest) req.ProtoFile = fds.GetFile() for _, f := range req.GetProtoFile() { if InStringSlice(filesToGen, f.GetName()) { req.FileToGenerate = append(req.FileToGenerate, f.GetName()) } } return req } // FilesToGenerate iterates through the proto files in the request and returns only the ones that were requested on the // command line. Only these protos should be generated by a codegen plugin. func FilesToGenerate(req *plugin_go.CodeGeneratorRequest) []*descriptor.FileDescriptorProto { protos := make([]*descriptor.FileDescriptorProto, 0) OUTERLOOP: for _, name := range req.GetFileToGenerate() { for _, f := range req.GetProtoFile() { if f.GetName() == name { protos = append(protos, f) continue OUTERLOOP } } } return protos } // LoadDescriptorSet loads a `FileDescriptorSet` from a file on disk. Such a file can be generated using the // `--descriptor_set_out` flag with `protoc`. // // Example: // protoc --descriptor_set_out=fileset.pb --include_imports --include_source_info ./booking.proto ./todo.proto func LoadDescriptorSet(pathSegments ...string) (*descriptor.FileDescriptorSet, error) { f, err := ioutil.ReadFile(filepath.Join(pathSegments...)) if err != nil { return nil, err } set := new(descriptor.FileDescriptorSet) if err = proto.Unmarshal(f, set); err != nil { return nil, err } return set, nil } // FindDescriptor finds the named descriptor in the given set. Only base names are searched. The first match is // returned, on `nil` if not found func FindDescriptor(set *descriptor.FileDescriptorSet, name string) *descriptor.FileDescriptorProto { for _, pf := range set.GetFile() { if filepath.Base(pf.GetName()) == name { return pf } } return nil } // LoadDescriptor loads file descriptor protos from a file on disk, and returns the named proto descriptor. This is // useful mostly for testing purposes. func LoadDescriptor(name string, pathSegments ...string) (*descriptor.FileDescriptorProto, error) { set, err := LoadDescriptorSet(pathSegments...) if err != nil { return nil, err } if pf := FindDescriptor(set, name); pf != nil { return pf, nil } return nil, errors.New("FileDescriptor not found") } golang-github-pseudomuto-protokit-0.2.1/utils/protobuf_test.go000066400000000000000000000054411515743464700247260ustar00rootroot00000000000000package utils_test import ( "github.com/stretchr/testify/suite" "testing" "github.com/pseudomuto/protokit/utils" ) type UtilsTest struct { suite.Suite } func TestUtils(t *testing.T) { suite.Run(t, new(UtilsTest)) } func (assert *UtilsTest) TestCreateGenRequest() { fds, err := utils.LoadDescriptorSet("..", "fixtures", "fileset.pb") assert.NoError(err) req := utils.CreateGenRequest(fds, "booking.proto", "todo.proto") assert.Equal([]string{"booking.proto", "todo.proto"}, req.GetFileToGenerate()) expectedProtos := []string{ "booking.proto", "google/protobuf/any.proto", "google/protobuf/descriptor.proto", "google/protobuf/timestamp.proto", "extend.proto", "todo.proto", "todo_import.proto", } for _, pf := range req.GetProtoFile() { assert.True(utils.InStringSlice(expectedProtos, pf.GetName())) } } func (assert *UtilsTest) TestFilesToGenerate() { fds, err := utils.LoadDescriptorSet("..", "fixtures", "fileset.pb") assert.NoError(err) req := utils.CreateGenRequest(fds, "booking.proto") protos := utils.FilesToGenerate(req) assert.Len(protos, 1) assert.Equal("booking.proto", protos[0].GetName()) } func (assert *UtilsTest) TestLoadDescriptorSet() { set, err := utils.LoadDescriptorSet("..", "fixtures", "fileset.pb") assert.NoError(err) assert.Len(set.GetFile(), 7) assert.NotNil(utils.FindDescriptor(set, "todo.proto")) assert.Nil(utils.FindDescriptor(set, "whodis.proto")) } func (assert *UtilsTest) TestLoadDescriptorSetFileNotFound() { set, err := utils.LoadDescriptorSet("..", "fixtures", "notgonnadoit.pb") assert.Nil(set) assert.EqualError(err, "open ../fixtures/notgonnadoit.pb: no such file or directory") } func (assert *UtilsTest) TestLoadDescriptorSetMarshalError() { set, err := utils.LoadDescriptorSet("..", "fixtures", "todo.proto") assert.Nil(set) assert.EqualError(err, "proto: can't skip unknown wire type 7 for descriptor.FileDescriptorSet") } func (assert *UtilsTest) TestLoadDescriptor() { proto, err := utils.LoadDescriptor("todo.proto", "..", "fixtures", "fileset.pb") assert.NotNil(proto) assert.NoError(err) } func (assert *UtilsTest) TestLoadDescriptorFileNotFound() { proto, err := utils.LoadDescriptor("todo.proto", "..", "fixtures", "notgonnadoit.pb") assert.Nil(proto) assert.EqualError(err, "open ../fixtures/notgonnadoit.pb: no such file or directory") } func (assert *UtilsTest) TestLoadDescriptorMarshalError() { proto, err := utils.LoadDescriptor("todo.proto", "..", "fixtures", "todo.proto") assert.Nil(proto) assert.EqualError(err, "proto: can't skip unknown wire type 7 for descriptor.FileDescriptorSet") } func (assert *UtilsTest) TestLoadDescriptorDescriptorNotFound() { proto, err := utils.LoadDescriptor("nothere.proto", "..", "fixtures", "fileset.pb") assert.Nil(proto) assert.EqualError(err, "FileDescriptor not found") } golang-github-pseudomuto-protokit-0.2.1/utils/strings.go000066400000000000000000000003341515743464700235140ustar00rootroot00000000000000package utils // InStringSlice returns whether or not the supplied value is in the slice func InStringSlice(sl []string, val string) bool { for _, s := range sl { if s == val { return true } } return false } golang-github-pseudomuto-protokit-0.2.1/utils/strings_test.go000066400000000000000000000006351515743464700245570ustar00rootroot00000000000000package utils_test import ( "github.com/stretchr/testify/suite" "testing" "github.com/pseudomuto/protokit/utils" ) type StringsTest struct { suite.Suite } func TestStrings(t *testing.T) { suite.Run(t, new(StringsTest)) } func (assert *StringsTest) TestInStringSlice() { vals := []string{"val1", "val2"} assert.True(utils.InStringSlice(vals, "val1")) assert.False(utils.InStringSlice(vals, "wat")) } golang-github-pseudomuto-protokit-0.2.1/version.go000066400000000000000000000001521515743464700223460ustar00rootroot00000000000000package protokit // Version describes the current version of protokit being used const Version = "0.2.1"