diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml deleted file mode 100644 index 6dec0d1..0000000 --- a/.github/workflows/pre-commit.yml +++ /dev/null @@ -1,17 +0,0 @@ -name: pre-commit -on: - push: - branches: - - master - pull_request: - branches: - - master -jobs: - pre-commit: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: actions/setup-go@v2 - with: - go-version: '1.17' - - uses: pre-commit/action@v3.0.0 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 94a0a72..b16df07 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -26,7 +26,7 @@ jobs: - name: Set Up Go uses: actions/setup-go@v2 with: - go-version: '1.17' + go-version: '1.14.6' - run: mkdir -p $GOPATH/bin - run: wget "https://github.com/protocolbuffers/protobuf/releases/download/v${{ matrix.protoc-version }}/protoc-${{ matrix.protoc-version }}-linux-x86_64.zip" -O /tmp/protoc.zip - run: unzip /tmp/protoc.zip -d /tmp @@ -34,6 +34,5 @@ jobs: - run: sudo mv /tmp/include/google /usr/local/include/google - name: Generate Testdata run: make testdata - - name: Run Tests - run: make tests - + - name: Lint + run: make lint tests diff --git a/.golangci.yml b/.golangci.yml deleted file mode 100644 index dddcca4..0000000 --- a/.golangci.yml +++ /dev/null @@ -1,26 +0,0 @@ -linters: - disable-all: true - enable: - - deadcode - - goconst - - gocyclo - - gofmt - - goimports - - gosimple - - govet - - ineffassign - - misspell - - revive - - structcheck - - typecheck - - unconvert - - unparam - - unused - - varcheck -issues: - max-per-linter: 0 - max-same-issues: 0 -run: - build-tags: - - integration - deadline: 5m diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml deleted file mode 100644 index 77c83de..0000000 --- a/.pre-commit-config.yaml +++ /dev/null @@ -1,7 +0,0 @@ -default_language_version: - python: python3.8 -repos: - - repo: https://github.com/golangci/golangci-lint - rev: v1.42.1 - hooks: - - id: golangci-lint diff --git a/Makefile b/Makefile index d07b59c..c679944 100644 --- a/Makefile +++ b/Makefile @@ -5,6 +5,16 @@ PROTOC_VER := $(shell protoc --version | cut -d' ' -f2) .PHONY: bootstrap bootstrap: testdata # set up the project for development +.PHONY: lint +lint: # lints the package for common code smells + set -e; for f in `find . -name "*.go" -not -name "*.pb.go"`; do \ + out=`gofmt -s -d $$f`; \ + test -z "$$out" || (echo $$out && exit 1); \ + done + which golint || go get -u golang.org/x/lint/golint + golint -set_exit_status ./... + go vet -all + .PHONY: quick quick: testdata # runs all tests without the race detector or coverage ifeq ($(PROTOC_VER), 3.17.0) diff --git a/artifact_test.go b/artifact_test.go index ce5338c..5e87638 100644 --- a/artifact_test.go +++ b/artifact_test.go @@ -13,8 +13,6 @@ var ( artifactTpl = template.Must(template.New("foo").Parse("{{ . }}")) ) -const fName = "foo" - func TestGeneratorFile_ProtoFile(t *testing.T) { t.Parallel() @@ -27,7 +25,7 @@ func TestGeneratorFile_ProtoFile(t *testing.T) { assert.Error(t, err) assert.Nil(t, pb) - f.Name = fName + f.Name = "foo" pb, err = f.ProtoFile() assert.NoError(t, err) assert.Equal(t, f.Name, pb.GetName()) @@ -49,7 +47,7 @@ func TestGeneratorTemplateFile_ProtoFile(t *testing.T) { assert.Error(t, err) assert.Nil(t, pb) - f.Name = fName + f.Name = "foo" pb, err = f.ProtoFile() assert.Error(t, err) assert.Nil(t, pb) @@ -73,7 +71,7 @@ func TestGeneratorAppend_ProtoFile(t *testing.T) { assert.Error(t, err) assert.Nil(t, pb) - f.FileName = fName + f.FileName = "foo" pb, err = f.ProtoFile() assert.NoError(t, err) assert.Empty(t, pb.GetName()) @@ -95,7 +93,7 @@ func TestGeneratorTemplateAppend_ProtoFile(t *testing.T) { assert.Error(t, err) assert.Nil(t, pb) - f.FileName = fName + f.FileName = "foo" pb, err = f.ProtoFile() assert.Error(t, err) assert.Nil(t, pb) @@ -120,7 +118,7 @@ func TestGeneratorInjection_ProtoFile(t *testing.T) { assert.Error(t, err) assert.Nil(t, pb) - f.FileName = fName + f.FileName = "foo" pb, err = f.ProtoFile() assert.NoError(t, err) assert.Equal(t, f.FileName, pb.GetName()) @@ -144,7 +142,7 @@ func TestGeneratorTemplateInjection_ProtoFile(t *testing.T) { assert.Error(t, err) assert.Nil(t, pb) - f.FileName = fName + f.FileName = "foo" pb, err = f.ProtoFile() assert.Error(t, err) assert.Nil(t, pb) diff --git a/field_test.go b/field_test.go index aae8612..a243cbc 100644 --- a/field_test.go +++ b/field_test.go @@ -251,7 +251,8 @@ func dummyOneOfField(synthetic bool) *field { m := dummyMsg() o := dummyOneof() str := descriptor.FieldDescriptorProto_TYPE_STRING - var oIndex int32 = 1 + var oIndex int32 + oIndex = 1 f := &field{desc: &descriptor.FieldDescriptorProto{ Name: proto.String("field"), Type: &str, diff --git a/field_type_test.go b/field_type_test.go index dcb78a8..66a043d 100644 --- a/field_type_test.go +++ b/field_type_test.go @@ -304,8 +304,9 @@ func TestMapT_Key(t *testing.T) { type mockT struct { FieldType - i []File - f Field + i []File + f Field + err error } func (t *mockT) Imports() []File { return t.i } diff --git a/lang/go/name.go b/lang/go/name.go index 8daef74..d31a965 100644 --- a/lang/go/name.go +++ b/lang/go/name.go @@ -22,7 +22,7 @@ func (c context) Name(node pgs.Node) pgs.Name { return c.PackageName(en) case ChildEntity: // Message or Enum types, which may be nested if p, ok := en.Parent().(pgs.Message); ok { - return joinChild(c.Name(p), en.Name()) + return pgs.Name(joinChild(c.Name(p), en.Name())) } return PGGUpperCamelCase(en.Name()) case pgs.Field: // field names cannot conflict with other generated methods @@ -31,9 +31,9 @@ func (c context) Name(node pgs.Node) pgs.Name { return replaceProtected(PGGUpperCamelCase(en.Name())) case pgs.EnumValue: // EnumValue are prefixed with the enum name if _, ok := en.Enum().Parent().(pgs.File); ok { - return joinNames(c.Name(en.Enum()), en.Name()) + return pgs.Name(joinNames(c.Name(en.Enum()), en.Name())) } - return joinNames(c.Name(en.Enum().Parent()), en.Name()) + return pgs.Name(joinNames(c.Name(en.Enum().Parent()), en.Name())) case pgs.Service: // always return the server name return c.ServerName(en) case pgs.Entity: // any other entity should be just upper-camel-cased @@ -44,7 +44,7 @@ func (c context) Name(node pgs.Node) pgs.Name { } func (c context) OneofOption(field pgs.Field) pgs.Name { - n := joinNames(c.Name(field.Message()), c.Name(field)) + n := pgs.Name(joinNames(c.Name(field.Message()), c.Name(field))) for _, msg := range field.Message().Messages() { if c.Name(msg) == n { diff --git a/lang/go/type_name_p2_presence_test.go b/lang/go/type_name_p2_presence_test.go index 03b98e6..c09cd7d 100644 --- a/lang/go/type_name_p2_presence_test.go +++ b/lang/go/type_name_p2_presence_test.go @@ -1,4 +1,3 @@ -//go:build !proto3_presence // +build !proto3_presence package pgsgo diff --git a/method.go b/method.go index ff45731..f9aeffa 100644 --- a/method.go +++ b/method.go @@ -93,4 +93,4 @@ func (m *method) childAtPath(path []int32) Entity { func (m *method) addSourceCodeInfo(info SourceCodeInfo) { m.info = info } -var _ Method = (*method)(nil) +var m Method = (*method)(nil) diff --git a/v2/artifact.go b/v2/artifact.go deleted file mode 100644 index 7fd1018..0000000 --- a/v2/artifact.go +++ /dev/null @@ -1,322 +0,0 @@ -package pgs - -import ( - "bytes" - "errors" - "io" - "os" - "path/filepath" - "strings" - - "github.com/golang/protobuf/proto" - plugin_go "github.com/golang/protobuf/protoc-gen-go/plugin" -) - -// An Artifact describes the output for a Module. Typically this is the creation -// of a file either directly against the file system or via protoc. -type Artifact interface { - artifact() -} - -// A Template to use for rendering artifacts. Either text/template or -// html/template Template types satisfy this interface. -type Template interface { - Execute(w io.Writer, data interface{}) error -} - -// GeneratorArtifact describes an Artifact that uses protoc for code generation. -// GeneratorArtifacts must be valid UTF8. To create binary files, use one of -// the "custom" Artifact types. -type GeneratorArtifact interface { - Artifact - - // ProtoFile converts the GeneratorArtifact to a CodeGeneratorResponse_File, - // which is handed to protoc to actually write the file to disk. An error is - // returned if Artifact cannot be converted. - ProtoFile() (*plugin_go.CodeGeneratorResponse_File, error) -} - -// TemplateArtifact contains the shared logic used by Artifacts that render -// their contents using a Template. -type TemplateArtifact struct { - // The Template to use for rendering. Either text/template or html/template - // Template types are supported. - Template Template - - // Data is arbitrary data passed into the Template's Execute method. - Data interface{} -} - -func (ta TemplateArtifact) render() (string, error) { - buf := &bytes.Buffer{} - - if err := ta.Template.Execute(buf, ta.Data); err != nil { - return "", err - } - - return buf.String(), nil -} - -// A GeneratorFile Artifact describes a file to be generated using protoc. -type GeneratorFile struct { - GeneratorArtifact - - // Name of the file to generate, relative to the protoc-plugin's generation - // output directory. - Name string - - // Contents are the body of the file. - Contents string - - // Overwrite specifies whether or not this file should replace another file - // with the same name if a prior Plugin or Module has created one. - Overwrite bool -} - -// ProtoFile satisfies the GeneratorArtifact interface. An error is returned if -// the name field is not a path relative to and within the protoc-plugin's -// generation output directory. -func (f GeneratorFile) ProtoFile() (*plugin_go.CodeGeneratorResponse_File, error) { - name, err := cleanGeneratorFileName(f.Name) - if err != nil { - return nil, err - } - - return &plugin_go.CodeGeneratorResponse_File{ - Name: proto.String(name), - Content: proto.String(f.Contents), - }, nil -} - -// A GeneratorTemplateFile describes a file to be generated using protoc from -// a Template. -type GeneratorTemplateFile struct { - GeneratorArtifact - TemplateArtifact - - // Name of the file to generate, relative to the protoc-plugin's generation - // output directory. - Name string - - // Overwrite specifies whether or not this file should replace another file - // with the same name if a prior Plugin or Module has created one. - Overwrite bool -} - -// ProtoFile satisfies the GeneratorArtifact interface. An error is returned if -// the name field is not a path relative to and within the protoc-plugin's -// generation output directory or if there is an error executing the Template. -func (f GeneratorTemplateFile) ProtoFile() (*plugin_go.CodeGeneratorResponse_File, error) { - name, err := cleanGeneratorFileName(f.Name) - if err != nil { - return nil, err - } - - content, err := f.render() - if err != nil { - return nil, err - } - - return &plugin_go.CodeGeneratorResponse_File{ - Name: proto.String(name), - Content: proto.String(content), - }, nil -} - -// A GeneratorAppend Artifact appends content to the end of the specified protoc -// generated file. This Artifact can only be used if another Module generates a -// file with the same name. -type GeneratorAppend struct { - GeneratorArtifact - - // Filename of the file to append to, relative to the protoc-plugin's generation - // output directory. - FileName string - - // Contents to be appended to the file - Contents string -} - -// ProtoFile satisfies the GeneratorArtifact interface. An error is returned if -// the name field is not a path relative to and within the protoc-plugin's -// generation output directory. -func (f GeneratorAppend) ProtoFile() (*plugin_go.CodeGeneratorResponse_File, error) { - if _, err := cleanGeneratorFileName(f.FileName); err != nil { - return nil, err - } - - return &plugin_go.CodeGeneratorResponse_File{ - Content: proto.String(f.Contents), - }, nil -} - -// A GeneratorTemplateAppend appends content to a protoc-generated file from a -// Template. See GeneratorAppend for limitations. -type GeneratorTemplateAppend struct { - GeneratorArtifact - TemplateArtifact - - // Filename of the file to append to, relative to the protoc-plugin's generation - // output directory. - FileName string -} - -// ProtoFile satisfies the GeneratorArtifact interface. An error is returned if -// the name field is not a path relative to and within the protoc-plugin's -// generation output directory or if there is an error executing the Template. -func (f GeneratorTemplateAppend) ProtoFile() (*plugin_go.CodeGeneratorResponse_File, error) { - if _, err := cleanGeneratorFileName(f.FileName); err != nil { - return nil, err - } - - content, err := f.render() - if err != nil { - return nil, err - } - - return &plugin_go.CodeGeneratorResponse_File{ - Content: proto.String(content), - }, nil -} - -// A GeneratorInjection Artifact inserts content into a protoc-generated file -// at the specified insertion point. The target file does not need to be -// generated by this protoc-plugin but must be generated by a prior plugin -// executed by protoc. -type GeneratorInjection struct { - GeneratorArtifact - - // Filename of the file to inject into, relative to the protoc-plugin's - // generation output directory. - FileName string - - // The name of the insertion point to inject into - InsertionPoint string - - // Contents to be inject into the file - Contents string -} - -// ProtoFile satisfies the GeneratorArtifact interface. An error is returned if -// the name field is not a path relative to and within the protoc-plugin's -// generation output directory. -func (f GeneratorInjection) ProtoFile() (*plugin_go.CodeGeneratorResponse_File, error) { - name, err := cleanGeneratorFileName(f.FileName) - if err != nil { - return nil, err - } - - return &plugin_go.CodeGeneratorResponse_File{ - Name: proto.String(name), - InsertionPoint: proto.String(f.InsertionPoint), - Content: proto.String(f.Contents), - }, nil -} - -// A GeneratorTemplateInjection Artifact inserts content rendered from a -// Template into protoc-generated file at the specified insertion point. The -// target file does not need to be generated by this protoc-plugin but must be -// generated by a prior plugin executed by protoc. -type GeneratorTemplateInjection struct { - GeneratorArtifact - TemplateArtifact - - // Filename of the file to inject into, relative to the protoc-plugin's - // generation output directory. - FileName string - - // The name of the insertion point to inject into - InsertionPoint string -} - -// ProtoFile satisfies the GeneratorArtifact interface. An error is returned if -// the name field is not a path relative to and within the protoc-plugin's -// generation output directory or if there is an error executing the Template. -func (f GeneratorTemplateInjection) ProtoFile() (*plugin_go.CodeGeneratorResponse_File, error) { - name, err := cleanGeneratorFileName(f.FileName) - if err != nil { - return nil, err - } - - content, err := f.render() - if err != nil { - return nil, err - } - - return &plugin_go.CodeGeneratorResponse_File{ - Name: proto.String(name), - InsertionPoint: proto.String(f.InsertionPoint), - Content: proto.String(content), - }, nil -} - -// CustomFile Artifacts are files generated directly against the file system, -// and do not use protoc for the generation. CustomFiles should be used over -// GeneratorFiles when custom permissions need to be set (such as executable -// scripts or read-only configs) or when the file needs to be created outside -// of the protoc-plugin's generation output directory. -type CustomFile struct { - Artifact - - // Name of the file to generate. If relative, the file is created relative to - // the directory in which protoc is executed. If absolute, the file is - // created as specified. - Name string - - // Contents are the body of the file. - Contents string - - // Perms are the file permission to generate the file with. Note that the - // umask of the process will be applied against these permissions. - Perms os.FileMode - - // Overwrite indicates if an existing file on disk should be overwritten by - // this file. - Overwrite bool -} - -// CustomTemplateFile Artifacts are files generated from a Template directly -// against the file system, and do not use protoc for the generation. -// CustomFiles should be used over GeneratorFiles when custom permissions need -// to be set (such as executable scripts or read-only configs) or when the file -// needs to be created outside of the protoc-plugin's generation output -// directory. -type CustomTemplateFile struct { - Artifact - TemplateArtifact - - // Name of the file to generate. If relative, the file is created relative to - // the directory in which protoc is executed. If absolute, the file is - // created as specified. - Name string - - // Perms are the file permission to generate the file with. Note that the - // umask of the process will be applied against these permissions. - Perms os.FileMode - - // Overwrite indicates if an existing file on disk should be overwritten by - // this file. - Overwrite bool -} - -func cleanGeneratorFileName(name string) (string, error) { - if filepath.IsAbs(name) { - return "", errors.New("generator file names must be relative paths") - } - - if name = filepath.ToSlash(filepath.Clean(name)); name == "." || strings.HasPrefix(name, "..") { - return "", errors.New("generator file names must be not contain . or .. within them") - } - - return name, nil -} - -// GeneratorError Artifacts are strings describing errors that happened in the -// code generation, but have not been fatal. They'll be used to populate the -// CodeGeneratorResponse's `error` field. Since that field is a string, multiple -// GeneratorError Artifacts will be concatenated. -type GeneratorError struct { - Artifact - - Message string -} diff --git a/v2/artifact_test.go b/v2/artifact_test.go deleted file mode 100644 index ce5338c..0000000 --- a/v2/artifact_test.go +++ /dev/null @@ -1,158 +0,0 @@ -package pgs - -import ( - "testing" - - "text/template" - - "github.com/stretchr/testify/assert" -) - -var ( - badArtifactTpl = template.Must(template.New("bad").Parse("{{ .NonExistentField }}")) - artifactTpl = template.Must(template.New("foo").Parse("{{ . }}")) -) - -const fName = "foo" - -func TestGeneratorFile_ProtoFile(t *testing.T) { - t.Parallel() - - f := GeneratorFile{ - Name: "..", - Contents: "bar", - } - - pb, err := f.ProtoFile() - assert.Error(t, err) - assert.Nil(t, pb) - - f.Name = fName - pb, err = f.ProtoFile() - assert.NoError(t, err) - assert.Equal(t, f.Name, pb.GetName()) - assert.Equal(t, f.Contents, pb.GetContent()) -} - -func TestGeneratorTemplateFile_ProtoFile(t *testing.T) { - t.Parallel() - - f := GeneratorTemplateFile{ - Name: ".", - TemplateArtifact: TemplateArtifact{ - Template: badArtifactTpl, - Data: "bar", - }, - } - - pb, err := f.ProtoFile() - assert.Error(t, err) - assert.Nil(t, pb) - - f.Name = fName - pb, err = f.ProtoFile() - assert.Error(t, err) - assert.Nil(t, pb) - - f.Template = artifactTpl - pb, err = f.ProtoFile() - assert.NoError(t, err) - assert.Equal(t, f.Name, pb.GetName()) - assert.Equal(t, "bar", pb.GetContent()) -} - -func TestGeneratorAppend_ProtoFile(t *testing.T) { - t.Parallel() - - f := GeneratorAppend{ - FileName: ".", - Contents: "bar", - } - - pb, err := f.ProtoFile() - assert.Error(t, err) - assert.Nil(t, pb) - - f.FileName = fName - pb, err = f.ProtoFile() - assert.NoError(t, err) - assert.Empty(t, pb.GetName()) - assert.Equal(t, f.Contents, pb.GetContent()) -} - -func TestGeneratorTemplateAppend_ProtoFile(t *testing.T) { - t.Parallel() - - f := GeneratorTemplateAppend{ - FileName: "/tmp", - TemplateArtifact: TemplateArtifact{ - Template: badArtifactTpl, - Data: "bar", - }, - } - - pb, err := f.ProtoFile() - assert.Error(t, err) - assert.Nil(t, pb) - - f.FileName = fName - pb, err = f.ProtoFile() - assert.Error(t, err) - assert.Nil(t, pb) - - f.Template = artifactTpl - pb, err = f.ProtoFile() - assert.NoError(t, err) - assert.Empty(t, pb.GetName()) - assert.Equal(t, "bar", pb.GetContent()) -} - -func TestGeneratorInjection_ProtoFile(t *testing.T) { - t.Parallel() - - f := GeneratorInjection{ - FileName: "..", - Contents: "bar", - InsertionPoint: "baz", - } - - pb, err := f.ProtoFile() - assert.Error(t, err) - assert.Nil(t, pb) - - f.FileName = fName - pb, err = f.ProtoFile() - assert.NoError(t, err) - assert.Equal(t, f.FileName, pb.GetName()) - assert.Equal(t, f.Contents, pb.GetContent()) - assert.Equal(t, f.InsertionPoint, pb.GetInsertionPoint()) -} - -func TestGeneratorTemplateInjection_ProtoFile(t *testing.T) { - t.Parallel() - - f := GeneratorTemplateInjection{ - FileName: ".", - InsertionPoint: "baz", - TemplateArtifact: TemplateArtifact{ - Template: badArtifactTpl, - Data: "bar", - }, - } - - pb, err := f.ProtoFile() - assert.Error(t, err) - assert.Nil(t, pb) - - f.FileName = fName - pb, err = f.ProtoFile() - assert.Error(t, err) - assert.Nil(t, pb) - - f.Template = artifactTpl - pb, err = f.ProtoFile() - assert.NoError(t, err) - assert.Equal(t, f.FileName, pb.GetName()) - assert.Equal(t, "bar", pb.GetContent()) - assert.Equal(t, f.InsertionPoint, pb.GetInsertionPoint()) -} diff --git a/v2/ast.go b/v2/ast.go deleted file mode 100644 index acb1276..0000000 --- a/v2/ast.go +++ /dev/null @@ -1,476 +0,0 @@ -package pgs - -import ( - "github.com/golang/protobuf/protoc-gen-go/descriptor" - plugin_go "github.com/golang/protobuf/protoc-gen-go/plugin" -) - -// AST encapsulates the entirety of the input CodeGeneratorRequest from protoc, -// parsed to build the Entity graph used by PG*. -type AST interface { - // Targets returns a map of the files specified in the protoc execution. For - // all Entities contained in these files, BuildTarget will return true. - Targets() map[string]File - - // Packages returns all the imported packages (including those for the target - // Files). This is limited to just the files that were imported by target - // protos, either directly or transitively. - Packages() map[string]Package - - // Lookup allows getting an Entity from the graph by its fully-qualified name - // (FQN). The FQN uses dot notation of the form ".{package}.{entity}", or the - // input path for Files. - Lookup(name string) (Entity, bool) -} - -type graph struct { - d Debugger - - targets map[string]File - packages map[string]Package - entities map[string]Entity - extensions []Extension -} - -func (g *graph) Targets() map[string]File { return g.targets } - -func (g *graph) Packages() map[string]Package { return g.packages } - -func (g *graph) Lookup(name string) (Entity, bool) { - e, ok := g.entities[name] - return e, ok -} - -// ProcessDescriptors is deprecated; use ProcessCodeGeneratorRequest instead -func ProcessDescriptors(debug Debugger, req *plugin_go.CodeGeneratorRequest) AST { - return ProcessCodeGeneratorRequest(debug, req) -} - -// ProcessCodeGeneratorRequest converts a CodeGeneratorRequest from protoc into a fully -// connected AST entity graph. An error is returned if the input is malformed. -func ProcessCodeGeneratorRequest(debug Debugger, req *plugin_go.CodeGeneratorRequest) AST { - g := &graph{ - d: debug, - targets: make(map[string]File, len(req.GetFileToGenerate())), - packages: make(map[string]Package), - entities: make(map[string]Entity), - extensions: []Extension{}, - } - - for _, f := range req.GetFileToGenerate() { - g.targets[f] = nil - } - - for _, f := range req.GetProtoFile() { - pkg := g.hydratePackage(f) - pkg.addFile(g.hydrateFile(pkg, f)) - } - - for _, e := range g.extensions { - e.addType(g.hydrateFieldType(e)) - extendee := g.mustSeen(e.Descriptor().GetExtendee()).(Message) - e.setExtendee(extendee) - if extendee != nil { - extendee.addExtension(e) - } - } - - return g -} - -// ProcessCodeGeneratorRequestBidirectional has the same functionality as -// ProcessCodeGeneratorRequest, but builds the AST so that files, messages, -// and enums have references to any files or messages that directly or -// transitively depend on them. -func ProcessCodeGeneratorRequestBidirectional(debug Debugger, req *plugin_go.CodeGeneratorRequest) AST { - g := ProcessCodeGeneratorRequest(debug, req) - for _, pkg := range g.Packages() { - for _, f := range pkg.Files() { - for _, m := range f.AllMessages() { - for _, field := range m.Fields() { - assignDependent(field.Type(), m) - } - } - } - } - return g -} - -// ProcessFileDescriptorSet converts a FileDescriptorSet from protoc into a -// fully connected AST entity graph. An error is returned if the input is -// malformed or missing dependencies. To generate a self-contained -// FileDescriptorSet, run the following command: -// -// protoc -o path/to/fdset.bin --include_imports $PROTO_FILES -// -// The emitted AST will have no values in the Targets map, but Packages will be -// populated. If used for testing purposes, the Targets map can be manually -// populated. -func ProcessFileDescriptorSet(debug Debugger, fdset *descriptor.FileDescriptorSet) AST { - req := plugin_go.CodeGeneratorRequest{ProtoFile: fdset.File} - return ProcessCodeGeneratorRequest(debug, &req) -} - -// ProcessFileDescriptorSetBidirectional has the same functionality as -// ProcessFileDescriptorSet, but builds the AST so that files, messages, -// and enums have references to any files or messages that directly or -// transitively depend on them. -func ProcessFileDescriptorSetBidirectional(debug Debugger, fdset *descriptor.FileDescriptorSet) AST { - req := plugin_go.CodeGeneratorRequest{ProtoFile: fdset.File} - return ProcessCodeGeneratorRequestBidirectional(debug, &req) -} - -func (g *graph) hydratePackage(f *descriptor.FileDescriptorProto) Package { - lookup := f.GetPackage() - if pkg, exists := g.packages[lookup]; exists { - return pkg - } - - p := &pkg{fd: f} - g.packages[lookup] = p - - return p -} - -func (g *graph) hydrateFile(pkg Package, f *descriptor.FileDescriptorProto) File { - fl := &file{ - pkg: pkg, - desc: f, - } - if pkg := f.GetPackage(); pkg != "" { - fl.fqn = "." + pkg - } else { - fl.fqn = "" - } - g.add(fl) - - for _, dep := range f.GetDependency() { - // the AST is built in topological order so a file's dependencies are always hydrated first - d := g.mustSeen(dep).(File) - fl.addFileDependency(d) - d.addDependent(fl) - } - - if _, fl.buildTarget = g.targets[f.GetName()]; fl.buildTarget { - g.targets[f.GetName()] = fl - } - - enums := f.GetEnumType() - fl.enums = make([]Enum, 0, len(enums)) - for _, e := range enums { - fl.addEnum(g.hydrateEnum(fl, e)) - } - - exts := f.GetExtension() - fl.defExts = make([]Extension, 0, len(exts)) - for _, ext := range exts { - e := g.hydrateExtension(fl, ext) - fl.addDefExtension(e) - } - - msgs := f.GetMessageType() - fl.msgs = make([]Message, 0, len(f.GetMessageType())) - for _, msg := range msgs { - fl.addMessage(g.hydrateMessage(fl, msg)) - } - - srvs := f.GetService() - fl.srvs = make([]Service, 0, len(srvs)) - for _, sd := range srvs { - fl.addService(g.hydrateService(fl, sd)) - } - - for _, m := range fl.AllMessages() { - for _, me := range m.MapEntries() { - for _, fld := range me.Fields() { - fld.addType(g.hydrateFieldType(fld)) - } - } - - for _, fld := range m.Fields() { - fld.addType(g.hydrateFieldType(fld)) - } - } - - g.hydrateSourceCodeInfo(fl, f) - - return fl -} - -func (g *graph) hydrateSourceCodeInfo(f File, fd *descriptor.FileDescriptorProto) { - for _, loc := range fd.GetSourceCodeInfo().GetLocation() { - info := sci{desc: loc} - path := loc.GetPath() - - if len(path) == 1 { - switch path[0] { - case syntaxPath: - f.addSourceCodeInfo(info) - case packagePath: - f.addPackageSourceCodeInfo(info) - default: - continue - } - } - - if e := f.childAtPath(path); e != nil { - e.addSourceCodeInfo(info) - } - } -} - -func (g *graph) hydrateEnum(p ParentEntity, ed *descriptor.EnumDescriptorProto) Enum { - e := &enum{ - desc: ed, - parent: p, - } - e.fqn = fullyQualifiedName(p, e) - g.add(e) - - vals := ed.GetValue() - e.vals = make([]EnumValue, 0, len(vals)) - for _, vd := range vals { - e.addValue(g.hydrateEnumValue(e, vd)) - } - - return e -} - -func (g *graph) hydrateEnumValue(e Enum, vd *descriptor.EnumValueDescriptorProto) EnumValue { - ev := &enumVal{ - desc: vd, - enum: e, - } - ev.fqn = fullyQualifiedName(e, ev) - g.add(ev) - - return ev -} - -func (g *graph) hydrateService(f File, sd *descriptor.ServiceDescriptorProto) Service { - s := &service{ - desc: sd, - file: f, - } - s.fqn = fullyQualifiedName(f, s) - g.add(s) - - for _, md := range sd.GetMethod() { - s.addMethod(g.hydrateMethod(s, md)) - } - - return s -} - -func (g *graph) hydrateMethod(s Service, md *descriptor.MethodDescriptorProto) Method { - m := &method{ - desc: md, - service: s, - } - m.fqn = fullyQualifiedName(s, m) - g.add(m) - - m.in = g.mustSeen(md.GetInputType()).(Message) - m.out = g.mustSeen(md.GetOutputType()).(Message) - - return m -} - -func (g *graph) hydrateMessage(p ParentEntity, md *descriptor.DescriptorProto) Message { - m := &msg{ - desc: md, - parent: p, - } - m.fqn = fullyQualifiedName(p, m) - g.add(m) - - for _, ed := range md.GetEnumType() { - m.addEnum(g.hydrateEnum(m, ed)) - } - - m.preservedMsgs = make([]Message, len(md.GetNestedType())) - for i, nmd := range md.GetNestedType() { - nm := g.hydrateMessage(m, nmd) - if nm.IsMapEntry() { - m.addMapEntry(nm) - } else { - m.addMessage(nm) - } - m.preservedMsgs[i] = nm - } - - for _, od := range md.GetOneofDecl() { - m.addOneOf(g.hydrateOneOf(m, od)) - } - - for _, fd := range md.GetField() { - fld := g.hydrateField(m, fd) - m.addField(fld) - - if idx := fld.Descriptor().OneofIndex; idx != nil { - m.oneofs[*idx].addField(fld) - } - } - - exts := md.GetExtension() - m.defExts = make([]Extension, 0, len(exts)) - for _, ext := range md.GetExtension() { - e := g.hydrateExtension(m, ext) - m.addDefExtension(e) - } - - return m -} - -func (g *graph) hydrateField(m Message, fd *descriptor.FieldDescriptorProto) Field { - f := &field{ - desc: fd, - msg: m, - } - f.fqn = fullyQualifiedName(f.msg, f) - g.add(f) - - return f -} - -func (g *graph) hydrateOneOf(m Message, od *descriptor.OneofDescriptorProto) OneOf { - o := &oneof{ - desc: od, - msg: m, - } - o.fqn = fullyQualifiedName(m, o) - g.add(o) - - return o -} - -func (g *graph) hydrateExtension(parent ParentEntity, fd *descriptor.FieldDescriptorProto) Extension { - ext := &ext{ - parent: parent, - } - ext.desc = fd - ext.fqn = fullyQualifiedName(parent, ext) - g.add(ext) - g.extensions = append(g.extensions, ext) - - return ext -} - -func (g *graph) hydrateFieldType(fld Field) FieldType { - s := &scalarT{fld: fld} - - switch { - case s.ProtoType() == GroupT: - g.d.Fail("group types are deprecated and unsupported. Use an embedded message instead.") - return nil - case s.ProtoLabel() == Repeated: - return g.hydrateRepeatedFieldType(s) - case s.ProtoType() == EnumT: - return g.hydrateEnumFieldType(s) - case s.ProtoType() == MessageT: - return g.hydrateEmbedFieldType(s) - default: - return s - } -} - -func (g *graph) hydrateEnumFieldType(s *scalarT) FieldType { - return &enumT{ - scalarT: s, - enum: g.mustSeen(s.fld.Descriptor().GetTypeName()).(Enum), - } -} - -func (g *graph) hydrateEmbedFieldType(s *scalarT) FieldType { - return &embedT{ - scalarT: s, - msg: g.mustSeen(s.fld.Descriptor().GetTypeName()).(Message), - } -} - -func (g *graph) hydrateRepeatedFieldType(s *scalarT) FieldType { - r := &repT{ - scalarT: s, - } - r.el = &scalarE{ - typ: r, - ptype: r.ProtoType(), - } - - switch s.ProtoType() { - case EnumT: - r.el = &enumE{ - scalarE: r.el.(*scalarE), - enum: g.mustSeen(s.fld.Descriptor().GetTypeName()).(Enum), - } - case MessageT: - m := g.mustSeen(s.fld.Descriptor().GetTypeName()).(Message) - if m.IsMapEntry() { - return g.hydrateMapFieldType(r, m) - } - - r.el = &embedE{ - scalarE: r.el.(*scalarE), - msg: m, - } - } - - return r -} - -func (g *graph) hydrateMapFieldType(r *repT, m Message) FieldType { - mt := &mapT{repT: r} - - mt.key = m.Fields()[0].Type().toElem() - mt.key.setType(mt) - - mt.el = m.Fields()[1].Type().toElem() - mt.el.setType(mt) - - return mt -} - -func (g *graph) mustSeen(fqn string) Entity { - if existing, seen := g.entities[fqn]; seen { - return existing - } - - g.d.Failf("expected entity %q has not been hydrated", fqn) - return nil -} - -func (g *graph) add(e Entity) { - g.entities[g.resolveFQN(e)] = e -} - -func (g *graph) resolveFQN(e Entity) string { - if f, ok := e.(File); ok { - return f.Name().String() - } - - return e.FullyQualifiedName() -} - -func assignDependent(ft FieldType, parent Message) { - if ft.IsEnum() { - ft.Enum().addDependent(parent) - } else if ft.IsEmbed() { - ft.Embed().addDependent(parent) - } else if ft.IsRepeated() || ft.IsMap() { - if ft.Element().IsEnum() { - ft.Element().Enum().addDependent(parent) - } else if ft.Element().IsEmbed() { - ft.Element().Embed().addDependent(parent) - } - - if ft.IsMap() { - if ft.Key().IsEnum() { - ft.Key().Enum().addDependent(parent) - } else if ft.Key().IsEmbed() { - ft.Key().Embed().addDependent(parent) - } - } - } -} - -var _ AST = (*graph)(nil) diff --git a/v2/ast_test.go b/v2/ast_test.go deleted file mode 100644 index 0aae53f..0000000 --- a/v2/ast_test.go +++ /dev/null @@ -1,489 +0,0 @@ -package pgs - -import ( - "io/ioutil" - "path/filepath" - "testing" - - "github.com/golang/protobuf/protoc-gen-go/descriptor" - - "github.com/golang/protobuf/proto" - plugin_go "github.com/golang/protobuf/protoc-gen-go/plugin" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func readCodeGenReq(t *testing.T, dir string) *plugin_go.CodeGeneratorRequest { - filename := filepath.Join("testdata", "graph", dir, "code_generator_request.pb.bin") - - data, err := ioutil.ReadFile(filename) - require.NoError(t, err, "unable to read CDR at %q", filename) - - req := &plugin_go.CodeGeneratorRequest{} - err = proto.Unmarshal(data, req) - require.NoError(t, err, "unable to unmarshal CDR data at %q", filename) - - return req -} - -func readFileDescSet(t *testing.T, filename string) *descriptor.FileDescriptorSet { - data, err := ioutil.ReadFile(filename) - require.NoError(t, err, "unable to read FDS at %q", filename) - - fdset := &descriptor.FileDescriptorSet{} - err = proto.Unmarshal(data, fdset) - require.NoError(t, err, "unable to unmarshal FDS data at %q", filename) - - return fdset -} - -func buildGraph(t *testing.T, dir string) AST { - d := InitMockDebugger() - ast := ProcessCodeGeneratorRequest(d, readCodeGenReq(t, dir)) - require.False(t, d.Failed(), "failed to build graph (see previous log statements)") - return ast -} - -func TestGraph_FDSet(t *testing.T) { - fdset := readFileDescSet(t, "testdata/fdset.bin") - d := InitMockDebugger() - ast := ProcessFileDescriptorSet(d, fdset) - - require.False(t, d.Failed(), "failed to build graph from FDSet") - msg, found := ast.Lookup(".kitchen.Sink") - assert.True(t, found) - assert.Implements(t, (*Message)(nil), msg) -} - -func TestGraph_Messages(t *testing.T) { - t.Parallel() - g := buildGraph(t, "messages") - - tests := []struct { - lookup string - fldCt int - isMap, isRepeated, isEmbed, isEnum bool - }{ - { - lookup: ".graph.messages.Scalars", - fldCt: 15, - }, - { - lookup: ".graph.messages.Embedded", - fldCt: 6, - isEmbed: true, - }, - { - lookup: ".graph.messages.Enums", - fldCt: 6, - isEnum: true, - }, - { - lookup: ".graph.messages.Repeated", - fldCt: 13, - isRepeated: true, - }, - { - lookup: ".graph.messages.Maps", - fldCt: 13, - isMap: true, - }, - { - lookup: ".graph.messages.Recursive", - fldCt: 1, - isEmbed: true, - }, - } - - for _, test := range tests { - tc := test - t.Run(tc.lookup, func(t *testing.T) { - t.Parallel() - - ent, ok := g.Lookup(tc.lookup) - require.True(t, ok, "unknown entity lookup") - msg, ok := ent.(Message) - require.True(t, ok, "entity is not a message") - - flds := msg.Fields() - assert.Len(t, flds, tc.fldCt, "unexpected number of fields on the message") - - for _, fld := range flds { - t.Run(fld.Name().String(), func(t *testing.T) { - typ := fld.Type() - assert.Equal(t, tc.isMap, typ.IsMap(), "should not be a map") - assert.Equal(t, tc.isRepeated, typ.IsRepeated(), "should not be repeated") - assert.Equal(t, tc.isEmbed, typ.IsEmbed(), "should not be embedded") - assert.Equal(t, tc.isEnum, typ.IsEnum(), "should not be an enum") - }) - } - }) - } - - t.Run("oneof", func(t *testing.T) { - t.Parallel() - - ent, ok := g.Lookup(".graph.messages.OneOfs") - require.True(t, ok) - msg, ok := ent.(Message) - require.True(t, ok) - - flds := msg.Fields() - oneofFlds := msg.OneOfFields() - notOneofFlds := msg.NonOneOfFields() - - assert.Len(t, flds, 3) - assert.Len(t, oneofFlds, 1) - assert.Len(t, notOneofFlds, 2) - - oneofs := msg.OneOfs() - require.Len(t, oneofs, 1) - - oo := oneofs[0] - require.Len(t, oo.Fields(), 1) - assert.Equal(t, int32(2), oo.Fields()[0].Descriptor().GetNumber()) - assert.Equal(t, oneofFlds, oo.Fields()) - }) -} - -func TestGraph_Services(t *testing.T) { - t.Parallel() - - g := buildGraph(t, "services") - - t.Run("empty", func(t *testing.T) { - t.Parallel() - - ent, ok := g.Lookup(".graph.services.Empty") - require.True(t, ok) - svc, ok := ent.(Service) - require.True(t, ok) - - assert.Empty(t, svc.Methods()) - }) - - t.Run("unary", func(t *testing.T) { - t.Parallel() - - ent, ok := g.Lookup(".graph.services.Unary") - require.True(t, ok) - svc, ok := ent.(Service) - require.True(t, ok) - - mtds := svc.Methods() - assert.Len(t, mtds, 2) - - for _, mtd := range mtds { - assert.False(t, mtd.ClientStreaming(), mtd.FullyQualifiedName()) - assert.False(t, mtd.ServerStreaming(), mtd.FullyQualifiedName()) - } - }) - - t.Run("streaming", func(t *testing.T) { - t.Parallel() - - ent, ok := g.Lookup(".graph.services.Streaming") - require.True(t, ok) - svc, ok := ent.(Service) - require.True(t, ok) - - mtds := svc.Methods() - assert.Len(t, mtds, 3) - - tests := []struct{ client, server bool }{ - {true, false}, - {false, true}, - {true, true}, - } - - for i, mtd := range mtds { - assert.Equal(t, tests[i].client, mtd.ClientStreaming(), mtd.FullyQualifiedName()) - assert.Equal(t, tests[i].server, mtd.ServerStreaming(), mtd.FullyQualifiedName()) - } - }) -} - -func TestGraph_SourceCodeInfo(t *testing.T) { - t.Parallel() - - g := buildGraph(t, "info") - - tests := map[string]string{ - "Info": "root message", - "Info.Before": "before message", - "Info.BeforeEnum.BEFORE": "before enum value", - "Info.field": "field", - "Info.Middle": "middle message", - "Info.Middle.inner": "inner field", - "Info.other_field": "other field", - "Info.After": "after message", - "Info.AfterEnum": "after enum", - "Info.AfterEnum.AFTER": "after enum value", - "Info.OneOf": "oneof", - "Info.oneof_field": "oneof field", - "Enum": "root enum comment", - "Enum.ROOT": "root enum value", - "Service": "service", - "Service.Method": "method", - } - - for lookup, expected := range tests { - t.Run(lookup, func(t *testing.T) { - lo := ".graph.info." + lookup - ent, ok := g.Lookup(lo) - require.True(t, ok, "cannot find entity: %s", lo) - info := ent.SourceCodeInfo() - require.NotNil(t, info, "source code info is nil") - assert.Contains(t, info.LeadingComments(), expected, "invalid leading comment") - }) - } - - t.Run("file", func(t *testing.T) { - f, ok := g.Targets()["info/info.proto"] - require.True(t, ok, "cannot find file") - - info := f.SyntaxSourceCodeInfo() - require.NotNil(t, info, "no source code info on syntax") - assert.Contains(t, info.LeadingComments(), "syntax") - assert.Equal(t, info, f.SourceCodeInfo(), "SourceCodeInfo should return SyntaxSourceCodeInfo") - - info = f.PackageSourceCodeInfo() - require.NotNil(t, info, "no source code info on package") - assert.Contains(t, info.LeadingComments(), "package") - }) -} - -func TestGraph_MustSeen(t *testing.T) { - t.Parallel() - - md := InitMockDebugger() - g := &graph{ - d: md, - entities: make(map[string]Entity), - } - - f := dummyFile() - g.add(f) - - assert.Equal(t, f, g.mustSeen(g.resolveFQN(f))) - assert.Nil(t, g.mustSeen(".foo.bar.baz")) - assert.True(t, md.Failed()) -} - -func TestGraph_HydrateFieldType_Group(t *testing.T) { - t.Parallel() - - md := InitMockDebugger() - g := &graph{d: md} - - f := dummyField() - f.Descriptor().Type = GroupT.ProtoPtr() - - assert.Nil(t, g.hydrateFieldType(f)) - assert.True(t, md.Failed()) -} - -func TestGraph_Packageless(t *testing.T) { - t.Parallel() - - g := buildGraph(t, "packageless") - - tests := []struct { - name string - entityIFace interface{} - }{ - {".RootMessage", (*Message)(nil)}, - {".RootEnum", (*Enum)(nil)}, - {".RootMessage.field", (*Field)(nil)}, - {".RootEnum.VALUE", (*EnumValue)(nil)}, - {".RootMessage.NestedMsg", (*Message)(nil)}, - {".RootMessage.NestedEnum", (*Enum)(nil)}, - } - - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - ent, ok := g.Lookup(tc.name) - assert.True(t, ok) - assert.NotNil(t, ent) - assert.Implements(t, tc.entityIFace, ent) - }) - } -} - -func TestGraph_Extensions(t *testing.T) { - t.Parallel() - - g := buildGraph(t, "extensions") - assert.NotNil(t, g) - - ent, ok := g.Lookup("extensions/ext/data.proto") - assert.True(t, ok) - assert.NotNil(t, ent.(File).DefinedExtensions()) - assert.Len(t, ent.(File).DefinedExtensions(), 6) - - ent, ok = g.Lookup("extensions/everything.proto") - assert.True(t, ok) - assert.NotNil(t, ent.(File).DefinedExtensions()) - assert.Len(t, ent.(File).Imports(), 4) - - ent, ok = g.Lookup(".extensions.Request") - assert.True(t, ok) - assert.NotNil(t, ent.(Message).DefinedExtensions()) - assert.Len(t, ent.(Message).DefinedExtensions(), 1) - - ent, ok = g.Lookup(".google.protobuf.MessageOptions") - assert.True(t, ok) - assert.NotNil(t, ent.(Message).Extensions()) - assert.Len(t, ent.(Message).Extensions(), 1) -} - -func TestGraph_Bidirectional(t *testing.T) { - t.Parallel() - - fdset := readFileDescSet(t, "testdata/fdset.bin") - d := InitMockDebugger() - ast := ProcessFileDescriptorSetBidirectional(d, fdset) - require.False(t, d.Failed(), "failed to build graph from FDSet") - - t.Run("nested", func(t *testing.T) { - t.Parallel() - - finish, ok := ast.Lookup(".kitchen.Sink.Material.Finish") - require.True(t, ok) - deps := finish.(Enum).Dependents() - - kitchen, ok := ast.Lookup(".kitchen.Kitchen") - require.True(t, ok) - sink, ok := ast.Lookup(".kitchen.Sink") - require.True(t, ok) - - require.Len(t, deps, 3) - assert.Contains(t, deps, finish.(Enum).Parent()) - assert.Contains(t, deps, sink) - assert.Contains(t, deps, kitchen) - }) - - t.Run("files", func(t *testing.T) { - t.Parallel() - - timestamp, ok := ast.Lookup("google/protobuf/timestamp.proto") - require.True(t, ok) - deps := timestamp.(File).Dependents() - - sinkProto, ok := ast.Lookup("kitchen/sink.proto") - require.True(t, ok) - kitchenProto, ok := ast.Lookup("kitchen/kitchen.proto") - require.True(t, ok) - - assert.Len(t, deps, 2) - assert.Contains(t, deps, sinkProto) - assert.Contains(t, deps, kitchenProto) - }) -} - -func TestGraph_Bidirectional_Messages_Enums(t *testing.T) { - t.Parallel() - - d := InitMockDebugger() - graph := ProcessCodeGeneratorRequestBidirectional(d, readCodeGenReq(t, "messages")) - require.False(t, d.Failed(), "failed to build graph (see previous log statements)") - - t.Run("repeated", func(t *testing.T) { - t.Parallel() - - beforeRepMsg, ok := graph.Lookup(".graph.messages.BeforeRepMsg") - require.True(t, ok) - repeated, ok := graph.Lookup(".graph.messages.Repeated") - require.True(t, ok) - deps := beforeRepMsg.(Message).Dependents() - - require.Len(t, deps, 1) - assert.Contains(t, deps, repeated) - - beforeRepEnum, ok := graph.Lookup(".graph.messages.BeforeRepEnum") - require.True(t, ok) - deps = beforeRepEnum.(Enum).Dependents() - - require.Len(t, deps, 1) - assert.Contains(t, deps, repeated) - }) - - t.Run("message cycle", func(t *testing.T) { - t.Parallel() - - recursiveMsg, ok := graph.Lookup(".graph.messages.Recursive") - require.True(t, ok) - assert.Empty(t, recursiveMsg.(Message).Dependents()) - }) - - t.Run("maps", func(t *testing.T) { - t.Parallel() - - beforeMapMsg, ok := graph.Lookup(".graph.messages.BeforeMapMsg") - require.True(t, ok) - maps, ok := graph.Lookup(".graph.messages.Maps") - require.True(t, ok) - deps := beforeMapMsg.(Message).Dependents() - - require.Len(t, deps, 1) - assert.Contains(t, deps, maps) - - beforeMapEnum, ok := graph.Lookup(".graph.messages.BeforeMapEnum") - require.True(t, ok) - deps = beforeMapEnum.(Enum).Dependents() - - require.Len(t, deps, 1) - assert.Contains(t, deps, maps) - }) -} - -func TestGraph_Bidirectional_Recursive(t *testing.T) { - t.Parallel() - - d := InitMockDebugger() - graph := ProcessCodeGeneratorRequestBidirectional(d, readCodeGenReq(t, "messages")) - require.False(t, d.Failed(), "failed to build graph (see previous log statements)") - - tests := []struct { - fqn string - expected []string - }{ - { - fqn: ".graph.messages.Recursive", - expected: []string{}, - }, - { - fqn: ".graph.messages.Circular.Rock", - expected: []string{ - ".graph.messages.Circular.Paper", - ".graph.messages.Circular.Scissors"}, - }, - { - fqn: ".graph.messages.RepeatedRecursive", - expected: []string{}, - }, - } - - for _, test := range tests { - tc := test - t.Run(tc.fqn, func(t *testing.T) { - t.Parallel() - - m, ok := graph.Lookup(tc.fqn) - require.True(t, ok) - deps := m.(Message).Dependents() - - require.Len(t, deps, len(tc.expected), - "wanted %v, but got %v", tc.expected, deps) - - set := make(map[string]bool) - for _, name := range tc.expected { - set[name] = true - } - - for _, dep := range deps { - assert.Contains(t, set, dep.FullyQualifiedName()) - } - }) - } -} diff --git a/v2/build_context.go b/v2/build_context.go deleted file mode 100644 index b48d0b7..0000000 --- a/v2/build_context.go +++ /dev/null @@ -1,134 +0,0 @@ -package pgs - -import "path/filepath" - -// BuildContext tracks code generation relative to an output path. By default, -// BuildContext's path is relative to the output location specified when -// executing protoc (an absolute path to this location is not available within -// protoc plugins). Specifying a custom output path permits using an absolute -// path and or a different location from protoc's designated output location. -type BuildContext interface { - DebuggerCommon - - // OutputPath is the path where files should be generated to. This path may - // be relative or absolute, if it is relative, the path is based off the - // (unknown) output destination specified during execution of protoc. If it - // is absolute, the path may be outside of the target directory for protoc. - OutputPath() string - - // JoinPath returns name relative to the value of OutputPath. - JoinPath(name ...string) string - - // Push adds an arbitrary prefix to the Debugger output. The Outpath value is - // unchanged. - Push(prefix string) BuildContext - - // PushDir changes the BuildContext's OutputPath to dir. If dir is relative, - // it is applied relative to the current value of OutputPath. - PushDir(dir string) BuildContext - - // Pop returns the previous state of the BuildContext. This may or may not - // change the value of OutputPath. This method will cause the plugin to fail - // if the root context is popped. - Pop() BuildContext - - // PopDir behaves like Pop but returns the last previous state of OutputPath, - // skipping over any prefix changes in-between. If at the root context, this - // method will always return the root context. - PopDir() BuildContext - - // Parameters returns the command line parameters passed in from protoc, - // mutated with any provided ParamMutators via InitOptions. - Parameters() Parameters -} - -// Context creates a new BuildContext with the provided debugger and initial -// output path. For protoc-gen-go plugins, output is typically ".", while -// Module's may use a custom path. -func Context(d Debugger, params Parameters, output string) BuildContext { - return rootContext{ - dirContext: dirContext{ - prefixContext: prefixContext{parent: nil, d: d}, - p: filepath.Clean(output), - }, - params: params, - } -} - -func initPrefixContext(c BuildContext, d Debugger, prefix string) prefixContext { - return prefixContext{ - parent: c, - d: d.Push(prefix), - } -} - -func (c prefixContext) Log(v ...interface{}) { c.d.Log(v...) } -func (c prefixContext) Logf(format string, v ...interface{}) { c.d.Logf(format, v...) } -func (c prefixContext) Debug(v ...interface{}) { c.d.Debug(v...) } -func (c prefixContext) Debugf(format string, v ...interface{}) { c.d.Debugf(format, v...) } -func (c prefixContext) Fail(v ...interface{}) { c.d.Fail(v...) } -func (c prefixContext) Failf(format string, v ...interface{}) { c.d.Failf(format, v...) } -func (c prefixContext) CheckErr(err error, v ...interface{}) { c.d.CheckErr(err, v...) } -func (c prefixContext) Assert(expr bool, v ...interface{}) { c.d.Assert(expr, v...) } -func (c prefixContext) Exit(code int) { c.d.Exit(code) } - -func (c prefixContext) Parameters() Parameters { return c.parent.Parameters() } -func (c prefixContext) OutputPath() string { return c.parent.OutputPath() } -func (c prefixContext) JoinPath(name ...string) string { return c.parent.JoinPath(name...) } -func (c prefixContext) PushDir(dir string) BuildContext { return initDirContext(c, c.d, dir) } -func (c prefixContext) Push(prefix string) BuildContext { return initPrefixContext(c, c.d, prefix) } -func (c prefixContext) Pop() BuildContext { return c.parent } -func (c prefixContext) PopDir() BuildContext { return c.parent.PopDir() } - -type dirContext struct { - prefixContext - p string -} - -func initDirContext(c BuildContext, d Debugger, dir string) dirContext { - dc := dirContext{ - prefixContext: prefixContext{parent: c, d: d}, - p: filepath.Clean(dir), - } - - c.Debug("push:", dc.parent.OutputPath(), "→", dc.OutputPath()) - - return dc -} - -func (c dirContext) OutputPath() string { return filepath.Join(c.parent.OutputPath(), c.p) } -func (c dirContext) PushDir(dir string) BuildContext { return initDirContext(c, c.d, dir) } -func (c dirContext) Push(prefix string) BuildContext { return initPrefixContext(c, c.d, prefix) } -func (c dirContext) PopDir() BuildContext { return c.Pop() } -func (c dirContext) Pop() BuildContext { - c.Debug("pop:", c.OutputPath(), "→", c.parent.OutputPath()) - return c.parent -} - -func (c dirContext) JoinPath(name ...string) string { - return filepath.Join(append([]string{c.OutputPath()}, name...)...) -} - -type prefixContext struct { - parent BuildContext - d Debugger -} - -type rootContext struct { - dirContext - params Parameters -} - -func (c rootContext) OutputPath() string { return c.p } -func (c rootContext) PushDir(dir string) BuildContext { return initDirContext(c, c.d, dir) } -func (c rootContext) Push(prefix string) BuildContext { return initPrefixContext(c, c.d, prefix) } -func (c rootContext) Parameters() Parameters { return c.params } -func (c rootContext) PopDir() BuildContext { return c } -func (c rootContext) Pop() BuildContext { - c.Fail("attempted to pop the root build context") - return nil -} - -func (c rootContext) JoinPath(name ...string) string { - return filepath.Join(append([]string{c.OutputPath()}, name...)...) -} diff --git a/v2/build_context_test.go b/v2/build_context_test.go deleted file mode 100644 index f651a35..0000000 --- a/v2/build_context_test.go +++ /dev/null @@ -1,247 +0,0 @@ -package pgs - -import ( - "errors" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestPrefixContext_Log(t *testing.T) { - t.Parallel() - - l := newMockLogger() - c := initPrefixContext(nil, &rootDebugger{l: l}, "foo") - - c.Log("bar") - assert.Equal(t, "[foo] bar\n", l.buf.String()) -} - -func TestPrefixContext_Logf(t *testing.T) { - t.Parallel() - - l := newMockLogger() - c := initPrefixContext(nil, &rootDebugger{l: l}, "foo") - - c.Logf("bar %s", "baz") - assert.Equal(t, "[foo] bar baz\n", l.buf.String()) -} - -func TestPrefixContext_Debug(t *testing.T) { - t.Parallel() - - l := newMockLogger() - c := initPrefixContext(nil, &rootDebugger{l: l, logDebugs: true}, "foo") - - c.Debug("bar") - assert.Equal(t, "[foo] bar\n", l.buf.String()) -} - -func TestPrefixContext_Debugf(t *testing.T) { - t.Parallel() - - l := newMockLogger() - c := initPrefixContext(nil, &rootDebugger{l: l, logDebugs: true}, "foo") - - c.Debugf("bar %s", "baz") - assert.Equal(t, "[foo] bar baz\n", l.buf.String()) -} - -func TestPrefixContext_Fail(t *testing.T) { - t.Parallel() - - d := InitMockDebugger() - c := initPrefixContext(nil, d, "foo") - - c.Fail("bar") - assert.True(t, d.Failed()) -} - -func TestPrefixContext_Failf(t *testing.T) { - t.Parallel() - - d := InitMockDebugger() - c := initPrefixContext(nil, d, "foo") - - c.Failf("bar %s", "baz") - assert.True(t, d.Failed()) -} - -func TestPrefixContext_CheckErr(t *testing.T) { - t.Parallel() - - d := InitMockDebugger() - c := initPrefixContext(nil, d, "foo") - - c.CheckErr(nil) - assert.False(t, d.Failed()) - err := errors.New("bar") - c.CheckErr(err) - assert.True(t, d.Exited()) - assert.Equal(t, d.Err(), err) -} - -func TestPrefixContext_Assert(t *testing.T) { - t.Parallel() - - d := InitMockDebugger() - c := initPrefixContext(nil, d, "foo") - - c.Assert(true) - assert.False(t, d.Failed()) - c.Assert(false) - assert.True(t, d.Failed()) -} - -func TestPrefixContext_OutputPath(t *testing.T) { - t.Parallel() - - d := Context(InitMockDebugger(), Parameters{}, "foo/bar") - c := initPrefixContext(d, InitMockDebugger(), "") - assert.Equal(t, c.OutputPath(), d.OutputPath()) -} - -func TestPrefixContext_PushPop(t *testing.T) { - t.Parallel() - - r := Context(InitMockDebugger(), Parameters{}, "foo/bar") - p := initPrefixContext(r, InitMockDebugger(), "baz") - - c := p.Push("fizz") - assert.IsType(t, prefixContext{}, c) - assert.IsType(t, rootContext{}, c.Pop().Pop()) -} - -func TestPrefixContext_PushPopDir(t *testing.T) { - t.Parallel() - - r := Context(InitMockDebugger(), Parameters{}, "foo/bar") - p := initPrefixContext(r, InitMockDebugger(), "fizz") - c := p.PushDir("baz") - - assert.Equal(t, "foo/bar/baz", c.OutputPath()) - assert.Equal(t, "foo/bar", c.Push("buzz").PopDir().OutputPath()) -} - -func TestPrefixContext_Parameters(t *testing.T) { - t.Parallel() - - p := Parameters{"foo": "bar"} - r := Context(InitMockDebugger(), p, ".") - c := r.Push("foo") - - assert.Equal(t, p, c.Parameters()) -} - -func TestDirContext_OutputPath(t *testing.T) { - t.Parallel() - - r := Context(InitMockDebugger(), Parameters{}, "foo/bar") - d := initDirContext(r, InitMockDebugger(), "baz") - assert.Equal(t, "foo/bar/baz", d.OutputPath()) -} - -func TestDirContext_Push(t *testing.T) { - t.Parallel() - - r := Context(InitMockDebugger(), Parameters{}, "foo/bar") - d := initDirContext(r, InitMockDebugger(), "baz") - c := d.Push("fizz") - - assert.Equal(t, d.OutputPath(), c.OutputPath()) - assert.IsType(t, prefixContext{}, c) -} - -func TestDirContext_PushPopDir(t *testing.T) { - t.Parallel() - - r := Context(InitMockDebugger(), Parameters{}, "foo") - d := initDirContext(r, InitMockDebugger(), "bar") - c := d.PushDir("baz") - - assert.Equal(t, "foo/bar/baz", c.OutputPath()) - c = c.PopDir() - assert.Equal(t, "foo/bar", c.OutputPath()) - c = c.PopDir() - assert.Equal(t, "foo", c.OutputPath()) -} - -func TestRootContext_OutputPath(t *testing.T) { - t.Parallel() - - r := Context(InitMockDebugger(), Parameters{}, "foo") - assert.Equal(t, "foo", r.OutputPath()) -} - -func TestRootContext_PushPop(t *testing.T) { - t.Parallel() - - d := InitMockDebugger() - r := Context(d, Parameters{}, "foo") - - c := r.Push("bar") - assert.Equal(t, "foo", c.OutputPath()) - c = c.Pop() - - assert.False(t, d.Failed()) - c.Pop() - assert.True(t, d.Failed()) -} - -func TestRootContext_PushPopDir(t *testing.T) { - t.Parallel() - - r := Context(InitMockDebugger(), Parameters{}, "foo") - c := r.PushDir("bar") - assert.Equal(t, "foo/bar", c.OutputPath()) - - c = c.PopDir() - assert.Equal(t, "foo", c.OutputPath()) - - c = c.PopDir() - assert.Equal(t, "foo", c.OutputPath()) -} - -func TestRootContext_Parameters(t *testing.T) { - t.Parallel() - - p := Parameters{"foo": "bar"} - r := Context(InitMockDebugger(), p, "foo") - assert.Equal(t, p, r.Parameters()) -} - -func TestRootContext_JoinPath(t *testing.T) { - t.Parallel() - - r := Context(InitMockDebugger(), Parameters{}, "foo") - assert.Equal(t, "foo/bar", r.JoinPath("bar")) -} - -func TestDirContext_JoinPath(t *testing.T) { - t.Parallel() - - r := Context(InitMockDebugger(), Parameters{}, "foo") - c := r.PushDir("bar") - - assert.Equal(t, "foo/bar/baz", c.JoinPath("baz")) -} - -func TestPrefixContext_JoinPath(t *testing.T) { - t.Parallel() - - r := Context(InitMockDebugger(), Parameters{}, "foo") - c := r.Push("baz") - - assert.Equal(t, "foo/bar", c.JoinPath("bar")) -} - -func TestPrefixContext_Exit(t *testing.T) { - t.Parallel() - - d := InitMockDebugger() - r := Context(d, Parameters{}, "") - r.Exit(123) - - assert.True(t, d.Exited()) - assert.Equal(t, 123, d.ExitCode()) -} diff --git a/v2/comment.go b/v2/comment.go deleted file mode 100644 index 2ca50f5..0000000 --- a/v2/comment.go +++ /dev/null @@ -1,68 +0,0 @@ -package pgs - -import ( - "bufio" - "bytes" - "fmt" - "strings" - "unicode" - "unicode/utf8" -) - -const commentPrefix = "//" - -// C returns a comment block, wrapping when the line's length will exceed wrap. -func C(wrap int, args ...interface{}) string { - s := commentScanner(wrap, args...) - buf := &bytes.Buffer{} - - for s.Scan() { - fmt.Fprintln(buf, commentPrefix, s.Text()) - } - - return buf.String() -} - -// C80 is an alias for C(80, args...) -func C80(args ...interface{}) string { return C(80, args...) } - -func commentScanner(wrap int, args ...interface{}) *bufio.Scanner { - s := bufio.NewScanner(strings.NewReader(fmt.Sprint(args...))) - s.Split(splitComment(wrap - 3)) - return s -} - -func splitComment(w int) bufio.SplitFunc { - return func(data []byte, atEOF bool) (advance int, token []byte, err error) { - var r rune - - start := 0 - lastSpace := 0 - - for width := 0; start < len(data); start += width { - r, width = utf8.DecodeRune(data[start:]) - if !unicode.IsSpace(r) { - break - } - } - - for width, i := 0, start; i < len(data); i += width { - r, width = utf8.DecodeRune(data[i:]) - if unicode.IsSpace(r) { - if i >= w { // we are at our max comment width - if lastSpace == 0 { // the token cannot be broken down further, allow it to break the limit - return i + width, data[start:i], nil - } - return lastSpace, data[start:lastSpace], nil - } - lastSpace = i - } - } - - if atEOF && len(data) > start { - return len(data), bytes.TrimSpace(data[start:]), nil - } - - return start, nil, nil - } -} diff --git a/v2/comment_test.go b/v2/comment_test.go deleted file mode 100644 index 0753f6f..0000000 --- a/v2/comment_test.go +++ /dev/null @@ -1,48 +0,0 @@ -package pgs - -import ( - "strconv" - "strings" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestC(t *testing.T) { - t.Parallel() - - tests := []struct { - in []interface{} - ex string - }{ - { - []interface{}{"foo", " bar", " baz"}, - "// foo bar baz\n", - }, - { - in: []interface{}{"the quick brown fox jumps over the lazy dog"}, - ex: "// the quick brown\n// fox jumps over\n// the lazy dog\n", - }, - { - in: []interface{}{"supercalifragilisticexpialidocious"}, - ex: "// supercalifragilisticexpialidocious\n", - }, - { - in: []interface{}{"1234567890123456789012345 foo"}, - ex: "// 1234567890123456789012345\n// foo\n", - }, - } - - for i, test := range tests { - tc := test - t.Run(strconv.Itoa(i), func(t *testing.T) { - assert.Equal(t, tc.ex, C(20, tc.in...)) - }) - } -} - -func TestC80(t *testing.T) { - t.Parallel() - ex := "// foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo\n// foo\n" - assert.Equal(t, ex, C80(strings.Repeat("foo ", 20))) -} diff --git a/v2/debug.go b/v2/debug.go deleted file mode 100644 index 5955d3e..0000000 --- a/v2/debug.go +++ /dev/null @@ -1,286 +0,0 @@ -package pgs - -import ( - "bytes" - "fmt" - "io" - "log" - "os" - "strings" -) - -// DebuggerCommon contains shared features of Debugger and Debugger-like types -// (such as BuildContext). -type DebuggerCommon interface { - // Log writes v to the underlying logging location (typically, os.Stderr). It - // uses the same behavior as log.Print, with all prefixes already attached. - Log(v ...interface{}) - - // Logf formats v and writes it to the underlying logging location - // (typically, os.Stderr). It uses the same behavior as log.Printf, with all - // prefixes already attached. - Logf(format string, v ...interface{}) - - // Debug behaves the same as Log, but only writes its output if debugging is - // enabled for this Debugger. - Debug(v ...interface{}) - - // Debugf behaves the same as Logf, but only writes its output if debugging - // is enabled for this Debugger. - Debugf(format string, v ...interface{}) - - // Fail behaves the same as Log, but also terminates the process. This method - // should be used if an un-recoverable error is encountered. - Fail(v ...interface{}) - - // Failf behaves the same as Logf, but also terminates the process. This - // method should be used if an un-recoverable error is encountered. - Failf(format string, v ...interface{}) - - // CheckErr ensures that err is nil. If err is not nil, Fail is called with - // err and the provided v. - CheckErr(err error, v ...interface{}) - - // Assert ensures that expr evaluates to true. If expr is false, Fail is - // called with the provided v. - Assert(expr bool, v ...interface{}) - - // Exit should terminate the current process with the provided code. - Exit(code int) -} - -// A Debugger provides utility methods to provide context-aware logging, -// error-checking, and assertions. The Debugger is used extensively within the -// protoc-gen-star generator, and is provided in a module's build context. -type Debugger interface { - DebuggerCommon - - // Push returns a new Debugger with the provided prefix. When entering a new - // context, this method should be used. - Push(prefix string) Debugger - - // Pop returns the parent for the current Debugger. When exiting a context, - // this method should be used. - Pop() Debugger -} - -type logger interface { - Println(...interface{}) - Printf(string, ...interface{}) -} - -type errFunc func(err error, msgs ...interface{}) - -type failFunc func(msgs ...interface{}) - -type exitFunc func(code int) - -type rootDebugger struct { - err errFunc - fail failFunc - exit exitFunc - l logger - logDebugs bool -} - -func initDebugger(d bool, l logger) Debugger { - rd := rootDebugger{ - logDebugs: d, - l: l, - exit: os.Exit, - } - - rd.fail = failFunc(rd.defaultFail) - rd.err = errFunc(rd.defaultErr) - - return rd -} - -func (d rootDebugger) defaultErr(err error, msg ...interface{}) { - if err != nil { - d.l.Printf("[error] %s: %v\n", fmt.Sprint(msg...), err) - d.exit(1) - } -} - -func (d rootDebugger) defaultFail(msg ...interface{}) { - d.l.Println(msg...) - d.exit(1) -} - -func (d rootDebugger) Log(v ...interface{}) { d.l.Println(v...) } -func (d rootDebugger) Logf(format string, v ...interface{}) { d.l.Printf(format, v...) } -func (d rootDebugger) Fail(v ...interface{}) { d.fail(fmt.Sprint(v...)) } -func (d rootDebugger) Failf(format string, v ...interface{}) { d.fail(fmt.Sprintf(format, v...)) } -func (d rootDebugger) Exit(code int) { d.exit(code) } - -func (d rootDebugger) Debug(v ...interface{}) { - if d.logDebugs { - d.Log(v...) - } -} - -func (d rootDebugger) Debugf(format string, v ...interface{}) { - if d.logDebugs { - d.Logf(format, v...) - } -} - -func (d rootDebugger) CheckErr(err error, v ...interface{}) { - if err != nil { - d.err(err, fmt.Sprint(v...)) - } -} - -func (d rootDebugger) Assert(expr bool, v ...interface{}) { - if !expr { - d.Fail(fmt.Sprint(v...)) - } -} - -func (d rootDebugger) Push(prefix string) Debugger { - return prefixedDebugger{ - parent: d, - prefix: fmt.Sprintf("[%s]", prefix), - } -} - -func (d rootDebugger) Pop() Debugger { - d.Fail("attempted to pop the root debugger") - return nil -} - -type prefixedDebugger struct { - parent Debugger - prefix string -} - -func (d prefixedDebugger) prepend(v []interface{}) []interface{} { - return append([]interface{}{d.prefix}, v...) -} - -func (d prefixedDebugger) prependFormat(format string) string { - if strings.HasPrefix(format, "[") { - return d.prefix + format - } - - return d.prefix + " " + format -} - -func (d prefixedDebugger) Log(v ...interface{}) { - d.parent.Log(d.prepend(v)...) -} - -func (d prefixedDebugger) Logf(format string, v ...interface{}) { - d.parent.Logf(d.prependFormat(format), v...) -} - -func (d prefixedDebugger) Debug(v ...interface{}) { - d.parent.Debug(d.prepend(v)...) -} - -func (d prefixedDebugger) Debugf(format string, v ...interface{}) { - d.parent.Debugf(d.prependFormat(format), v...) -} - -func (d prefixedDebugger) Fail(v ...interface{}) { - d.parent.Fail(d.prepend(v)...) -} - -func (d prefixedDebugger) Failf(format string, v ...interface{}) { - d.parent.Failf(d.prependFormat(format), v...) -} - -func (d prefixedDebugger) CheckErr(err error, v ...interface{}) { - d.parent.CheckErr(err, d.prepend(v)...) -} - -func (d prefixedDebugger) Assert(expr bool, v ...interface{}) { - d.parent.Assert(expr, d.prepend(v)...) -} - -func (d prefixedDebugger) Exit(code int) { d.parent.Exit(code) } - -func (d prefixedDebugger) Push(prefix string) Debugger { - return prefixedDebugger{ - parent: d, - prefix: "[" + prefix + "]", - } -} - -func (d prefixedDebugger) Pop() Debugger { - return d.parent -} - -// MockDebugger serves as a root Debugger instance for usage in tests. Unlike -// an actual Debugger, MockDebugger will not exit the program, but will track -// failures, checked errors, and exit codes. -type MockDebugger interface { - Debugger - - // Output returns a reader of all logged data. - Output() io.Reader - - // Failed returns true if Fail or Failf has been called on this debugger or a - // descendant of it (via Push). - Failed() bool - - // Err returns the error passed to CheckErr. - Err() error - - // Exited returns true if this Debugger (or a descendant of it) would have - // called os.Exit. - Exited() bool - - // ExitCode returns the code this Debugger (or a descendant of it) passed to - // os.Exit. If Exited() returns false, this value is meaningless. - ExitCode() int -} - -type mockDebugger struct { - Debugger - - buf bytes.Buffer - failed bool - err error - exited bool - code int -} - -// InitMockDebugger creates a new MockDebugger for usage in tests. -func InitMockDebugger() MockDebugger { - md := &mockDebugger{} - d := initDebugger(true, log.New(&md.buf, "", 0)).(rootDebugger) - - d.fail = func(msgs ...interface{}) { - md.failed = true - d.defaultFail(msgs...) - } - - d.err = func(err error, msgs ...interface{}) { - if err != nil { - md.err = err - } - d.defaultErr(err, msgs...) - } - - d.exit = func(code int) { - md.exited = true - md.code = code - } - - md.Debugger = d - return md -} - -func (d *mockDebugger) Output() io.Reader { return &d.buf } -func (d *mockDebugger) Failed() bool { return d.failed } -func (d *mockDebugger) Err() error { return d.err } -func (d *mockDebugger) Exited() bool { return d.exited } -func (d *mockDebugger) ExitCode() int { return d.code } - -var ( - _ Debugger = rootDebugger{} - _ Debugger = prefixedDebugger{} - _ MockDebugger = &mockDebugger{} -) diff --git a/v2/debug_test.go b/v2/debug_test.go deleted file mode 100644 index c5e8a37..0000000 --- a/v2/debug_test.go +++ /dev/null @@ -1,397 +0,0 @@ -package pgs - -import ( - "bytes" - "fmt" - "io/ioutil" - "testing" - - "errors" - - "github.com/stretchr/testify/assert" -) - -type mockLogger struct { - buf *bytes.Buffer -} - -func newMockLogger() *mockLogger { return &mockLogger{&bytes.Buffer{}} } -func (l *mockLogger) Println(v ...interface{}) { fmt.Fprintln(l.buf, v...) } -func (l *mockLogger) Printf(format string, v ...interface{}) { - fmt.Fprintln(l.buf, fmt.Sprintf(format, v...)) -} - -func TestRootDebugger_Log(t *testing.T) { - t.Parallel() - - l := newMockLogger() - rd := rootDebugger{l: l} - rd.Log("foo", "bar") - assert.Equal(t, "foo bar\n", l.buf.String()) -} - -func TestRootDebugger_Logf(t *testing.T) { - t.Parallel() - - l := newMockLogger() - rd := rootDebugger{l: l} - rd.Logf("foo%s", "bar") - assert.Equal(t, "foobar\n", l.buf.String()) -} - -func TestRootDebugger_Fail(t *testing.T) { - t.Parallel() - - var failed bool - - fail := func(msgs ...interface{}) { - assert.Equal(t, "foobar", msgs[0]) - failed = true - } - - rd := rootDebugger{l: newMockLogger(), fail: fail} - rd.Fail("foo", "bar") - - assert.True(t, failed) -} - -func TestRootDebugger_Failf(t *testing.T) { - t.Parallel() - - var failed bool - - fail := func(msgs ...interface{}) { - assert.Equal(t, "fizz buzz", msgs[0]) - failed = true - } - - rd := rootDebugger{l: newMockLogger(), fail: fail} - rd.Failf("fizz %s", "buzz") - - assert.True(t, failed) -} - -func TestRootDebugger_Debug(t *testing.T) { - t.Parallel() - - l := newMockLogger() - rd := rootDebugger{l: l} - - rd.Debug("foo") - assert.Empty(t, l.buf.String()) - - rd.logDebugs = true - - rd.Debug("bar") - assert.Contains(t, l.buf.String(), "bar") -} - -func TestRootDebugger_Debugf(t *testing.T) { - t.Parallel() - - l := newMockLogger() - rd := rootDebugger{l: l} - - rd.Debug("foo") - assert.Empty(t, l.buf.String()) - - rd.logDebugs = true - - rd.Debug("bar") - assert.Contains(t, l.buf.String(), "bar") -} - -func TestRootDebugger_CheckErr(t *testing.T) { - t.Parallel() - - e := errors.New("bad error") - errd := false - - errfn := func(err error, msg ...interface{}) { - assert.Equal(t, e, err) - assert.Equal(t, "foo", msg[0]) - errd = true - } - - rd := rootDebugger{err: errfn} - rd.CheckErr(nil, "fizz") - assert.False(t, errd) - - rd.CheckErr(e, "foo") - assert.True(t, errd) -} - -func TestRootDebugger_Assert(t *testing.T) { - t.Parallel() - - failed := false - - fail := func(msgs ...interface{}) { - assert.Equal(t, "foo", msgs[0]) - failed = true - } - - rd := rootDebugger{fail: fail} - rd.Assert(true, "fizz") - assert.False(t, failed) - - rd.Assert(false, "foo") - assert.True(t, failed) -} - -func TestRootDebugger_Exit(t *testing.T) { - t.Parallel() - - var code int - - rd := rootDebugger{exit: func(c int) { code = c }} - rd.Exit(123) - assert.Equal(t, 123, code) -} - -func TestRootDebugger_Push(t *testing.T) { - t.Parallel() - - rd := rootDebugger{} - - d := rd.Push("foo") - assert.NotNil(t, d) - assert.NotEqual(t, rd, d) -} - -func TestRootDebugger_Pop(t *testing.T) { - t.Parallel() - - rd := rootDebugger{} - assert.Panics(t, func() { rd.Pop() }) -} - -func TestRootDebugger_DefaultErr(t *testing.T) { - t.Parallel() - - exited := false - code := 0 - l := newMockLogger() - rd := rootDebugger{ - l: l, - exit: func(c int) { - code = c - exited = true - }, - } - - rd.defaultErr(nil, "nothing") - - assert.False(t, exited) - assert.Empty(t, l.buf.String()) - - rd.defaultErr(errors.New("some error"), "something") - assert.True(t, exited) - assert.Equal(t, 1, code) - assert.Contains(t, l.buf.String(), "something") -} - -func TestRootDebugger_DefaultFail(t *testing.T) { - t.Parallel() - - exited := false - code := 0 - l := newMockLogger() - rd := rootDebugger{ - l: l, - exit: func(c int) { - code = c - exited = true - }, - } - - rd.defaultFail("something") - assert.True(t, exited) - assert.Equal(t, 1, code) - assert.Contains(t, l.buf.String(), "something") -} - -func TestPrefixedDebugger_Log(t *testing.T) { - t.Parallel() - - l := newMockLogger() - d := rootDebugger{l: l}.Push("FIZZ") - d.Log("foo", "bar") - assert.Contains(t, l.buf.String(), "FIZZ") - assert.Contains(t, l.buf.String(), "foo bar") -} - -func TestPrefixedDebugger_Logf(t *testing.T) { - t.Parallel() - - l := newMockLogger() - d := rootDebugger{l: l}.Push("FIZZ") - d.Logf("foo%s", "bar") - assert.Contains(t, l.buf.String(), "FIZZ") - assert.Contains(t, l.buf.String(), "foobar") -} - -func TestPrefixedDebugger_Fail(t *testing.T) { - t.Parallel() - - var failed bool - - fail := func(msgs ...interface{}) { - assert.Contains(t, msgs[0], "FIZZ") - assert.Contains(t, msgs[0], "foobar") - failed = true - } - - d := rootDebugger{l: newMockLogger(), fail: fail}.Push("FIZZ") - d.Fail("foo", "bar") - - assert.True(t, failed) -} - -func TestPrefixedDebugger_Failf(t *testing.T) { - t.Parallel() - - var failed bool - - fail := func(msgs ...interface{}) { - assert.Contains(t, msgs[0], "FIZZ") - assert.Contains(t, msgs[0], "foo bar") - failed = true - } - - d := rootDebugger{l: newMockLogger(), fail: fail}.Push("FIZZ") - d.Failf("foo %s", "bar") - - assert.True(t, failed) -} - -func TestPrefixedDebugger_Debug(t *testing.T) { - t.Parallel() - - l := newMockLogger() - rd := rootDebugger{l: l} - d := rd.Push("FIZZ") - - d.Debug("foo") - assert.Empty(t, l.buf.String()) - - rd.logDebugs = true - d = rd.Push("FIZZ") - - d.Debug("bar") - assert.Contains(t, l.buf.String(), "bar") - assert.Contains(t, l.buf.String(), "FIZZ") -} - -func TestPrefixedDebugger_Debugf(t *testing.T) { - t.Parallel() - - l := newMockLogger() - rd := rootDebugger{l: l} - d := rd.Push("FIZZ") - - d.Debugf("foo%s", "bar") - assert.Empty(t, l.buf.String()) - - rd.logDebugs = true - d = rd.Push("FIZZ") - - d.Debugf("foo%s", "bar") - assert.Contains(t, l.buf.String(), "foobar") - assert.Contains(t, l.buf.String(), "FIZZ") -} - -func TestPrefixedDebugger_CheckErr(t *testing.T) { - t.Parallel() - - e := errors.New("bad error") - errd := false - - errfn := func(err error, msg ...interface{}) { - assert.Equal(t, e, err) - assert.Contains(t, msg[0], "foo") - assert.Contains(t, msg[0], "FIZZ") - errd = true - } - - d := rootDebugger{err: errfn}.Push("FIZZ") - d.CheckErr(nil, "fizz") - assert.False(t, errd) - - d.CheckErr(e, "foo") - assert.True(t, errd) -} - -func TestPrefixedDebugger_Assert(t *testing.T) { - t.Parallel() - - failed := false - - fail := func(msgs ...interface{}) { - assert.Contains(t, msgs[0], "FIZZ") - assert.Contains(t, msgs[0], "foo") - failed = true - } - - d := rootDebugger{fail: fail}.Push("FIZZ") - d.Assert(1 == 1, "fizz") - assert.False(t, failed) - - d.Assert(1 == 0, "foo") - assert.True(t, failed) -} - -func TestPrefixedDebugger_Pop(t *testing.T) { - t.Parallel() - - rd := rootDebugger{} - d := rd.Push("FOO") - assert.Equal(t, rd, d.Pop()) -} - -func TestPrefixedDebugger_Push(t *testing.T) { - t.Parallel() - - l := newMockLogger() - rd := rootDebugger{l: l} - d := rd.Push("FOO").Push("BAR") - d.Log("fizz") - assert.Contains(t, l.buf.String(), "FOO") - assert.Contains(t, l.buf.String(), "BAR") -} - -func TestPrefixedDebugger_Push_Format(t *testing.T) { - t.Parallel() - - l := newMockLogger() - d := rootDebugger{l: l}.Push("foo").Push("bar") - d.Logf("%s", "baz") - - assert.Equal(t, "[foo][bar] baz\n", l.buf.String()) -} - -func TestPrefixedDebugger_Exit(t *testing.T) { - t.Parallel() - - md := InitMockDebugger() - d := &prefixedDebugger{parent: md} - d.Exit(123) - - assert.True(t, md.Exited()) - assert.Equal(t, 123, md.ExitCode()) -} - -func TestInitDebugger(t *testing.T) { - t.Parallel() - d := initDebugger(true, nil) - assert.NotNil(t, d) -} - -func TestMockDebugger_Output(t *testing.T) { - t.Parallel() - - md := InitMockDebugger() - md.Log("foobar") - b, _ := ioutil.ReadAll(md.Output()) - assert.Equal(t, "foobar\n", string(b)) -} diff --git a/v2/docs.go b/v2/docs.go deleted file mode 100644 index 20a2bb8..0000000 --- a/v2/docs.go +++ /dev/null @@ -1,2 +0,0 @@ -// Package pgs provides a library for building protoc plugins -package pgs diff --git a/v2/entity.go b/v2/entity.go deleted file mode 100644 index fcb4992..0000000 --- a/v2/entity.go +++ /dev/null @@ -1,85 +0,0 @@ -package pgs - -import ( - "github.com/golang/protobuf/proto" -) - -// Entity describes any member of the proto AST that is extensible via -// options. All components of a File are considered entities. -type Entity interface { - Node - - // The Name of the entity - Name() Name - - // The fully qualified name of the entity. For example, a message - // 'HelloRequest' in a 'helloworld' package takes the form of - // '.helloworld.HelloRequest'. - FullyQualifiedName() string - - // Syntax identifies whether this entity is encoded with proto2 or proto3 - // syntax. - Syntax() Syntax - - // Package returns the container package for this entity. - Package() Package - - // Imports includes external files directly required by this entity. Call - // TransitiveImports on File to get all transitive dependencies. - Imports() []File - - // File returns the File containing this entity. - File() File - - // Extension extracts an extension from the entity's options, described by - // desc and populates the value ext. Ext must be a pointer type. An error - // will only be returned if there is a type mismatch between desc and ext. - // The ok value will be true if the extension was found. If the extension - // is NOT found, ok will be false and err will be nil. - Extension(desc *proto.ExtensionDesc, ext interface{}) (ok bool, err error) - - // BuildTarget identifies whether or not generation should be performed on - // this entity. Use this flag to determine if the file was targeted in the - // protoc run or if it was loaded as an external dependency. - BuildTarget() bool - - // SourceCodeInfo returns the SourceCodeInfo associated with the entity. - // Primarily, this struct contains the comments associated with the Entity. - SourceCodeInfo() SourceCodeInfo - - childAtPath(path []int32) Entity - addSourceCodeInfo(info SourceCodeInfo) -} - -// A ParentEntity is any Entity type that can contain messages and/or enums. -// File and Message types implement ParentEntity. -type ParentEntity interface { - Entity - - // Messages returns the top-level messages from this entity. Nested - // messages are not included. - Messages() []Message - - // AllMessages returns all the top-level and nested messages from this Entity. - AllMessages() []Message - - // MapEntries returns the MapEntry message types contained within this - // Entity. These messages are not returned by the Messages or AllMessages - // methods. Map Entry messages are typically not exposed to the end user. - MapEntries() []Message - - // Enums returns the top-level enums from this entity. Nested enums - // are not included. - Enums() []Enum - - // AllEnums returns all top-level and nested enums from this entity. - AllEnums() []Enum - - // DefinedExtensions returns all Extensions defined on this entity. - DefinedExtensions() []Extension - - addMessage(m Message) - addMapEntry(m Message) - addEnum(e Enum) - addDefExtension(e Extension) -} diff --git a/v2/enum.go b/v2/enum.go deleted file mode 100644 index b3acac0..0000000 --- a/v2/enum.go +++ /dev/null @@ -1,119 +0,0 @@ -package pgs - -import ( - "github.com/golang/protobuf/proto" - "github.com/golang/protobuf/protoc-gen-go/descriptor" -) - -// Enum describes an enumeration type. Its parent can be either a Message or a -// File. -type Enum interface { - Entity - - // Descriptor returns the proto descriptor for this Enum - Descriptor() *descriptor.EnumDescriptorProto - - // Parent resolves to either a Message or File that directly contains this - // Enum. - Parent() ParentEntity - - // Values returns each defined enumeration value. - Values() []EnumValue - - // Dependents returns all of the messages where Enum is directly or - // transitively used. - Dependents() []Message - - addValue(v EnumValue) - addDependent(m Message) - setParent(p ParentEntity) -} - -type enum struct { - desc *descriptor.EnumDescriptorProto - parent ParentEntity - vals []EnumValue - info SourceCodeInfo - fqn string - dependents []Message - dependentsCache map[string]Message -} - -func (e *enum) Name() Name { return Name(e.desc.GetName()) } -func (e *enum) FullyQualifiedName() string { return e.fqn } -func (e *enum) Syntax() Syntax { return e.parent.Syntax() } -func (e *enum) Package() Package { return e.parent.Package() } -func (e *enum) File() File { return e.parent.File() } -func (e *enum) BuildTarget() bool { return e.parent.BuildTarget() } -func (e *enum) SourceCodeInfo() SourceCodeInfo { return e.info } -func (e *enum) Descriptor() *descriptor.EnumDescriptorProto { return e.desc } -func (e *enum) Parent() ParentEntity { return e.parent } -func (e *enum) Imports() []File { return nil } -func (e *enum) Values() []EnumValue { return e.vals } - -func (e *enum) populateDependentsCache() { - if e.dependentsCache != nil { - return - } - - e.dependentsCache = map[string]Message{} - for _, dep := range e.dependents { - e.dependentsCache[dep.FullyQualifiedName()] = dep - dep.getDependents(e.dependentsCache) - } -} - -func (e *enum) Dependents() []Message { - e.populateDependentsCache() - return messageSetToSlice("", e.dependentsCache) -} - -func (e *enum) Extension(desc *proto.ExtensionDesc, ext interface{}) (bool, error) { - return extension(e.desc.GetOptions(), desc, &ext) -} - -func (e *enum) accept(v Visitor) (err error) { - if v == nil { - return nil - } - - if v, err = v.VisitEnum(e); err != nil || v == nil { - return - } - - for _, ev := range e.vals { - if err = ev.accept(v); err != nil { - return - } - } - - return -} - -func (e *enum) addDependent(m Message) { - e.dependents = append(e.dependents, m) -} - -func (e *enum) addValue(v EnumValue) { - v.setEnum(e) - e.vals = append(e.vals, v) -} - -func (e *enum) setParent(p ParentEntity) { e.parent = p } - -func (e *enum) childAtPath(path []int32) Entity { - switch { - case len(path) == 0: - return e - case len(path)%2 != 0: - return nil - case path[0] == enumTypeValuePath: - return e.vals[path[1]].childAtPath(path[2:]) - default: - return nil - } -} - -func (e *enum) addSourceCodeInfo(info SourceCodeInfo) { e.info = info } - -var _ Enum = (*enum)(nil) diff --git a/v2/enum_test.go b/v2/enum_test.go deleted file mode 100644 index 231d53e..0000000 --- a/v2/enum_test.go +++ /dev/null @@ -1,218 +0,0 @@ -package pgs - -import ( - "errors" - "testing" - - "github.com/golang/protobuf/proto" - "github.com/golang/protobuf/protoc-gen-go/descriptor" - "github.com/stretchr/testify/assert" -) - -func TestEnum_Name(t *testing.T) { - t.Parallel() - - e := &enum{desc: &descriptor.EnumDescriptorProto{Name: proto.String("foo")}} - assert.Equal(t, "foo", e.Name().String()) -} - -func TestEnum_FullyQualifiedName(t *testing.T) { - t.Parallel() - - e := &enum{fqn: "enum"} - assert.Equal(t, e.fqn, e.FullyQualifiedName()) -} - -func TestEnum_Syntax(t *testing.T) { - t.Parallel() - - e := &enum{} - f := dummyFile() - f.addEnum(e) - - assert.Equal(t, f.Syntax(), e.Syntax()) -} - -func TestEnum_Package(t *testing.T) { - t.Parallel() - - e := &enum{} - f := dummyFile() - f.addEnum(e) - - assert.NotNil(t, e.Package()) - assert.Equal(t, f.Package(), e.Package()) -} - -func TestEnum_File(t *testing.T) { - t.Parallel() - - e := &enum{} - m := dummyMsg() - m.addEnum(e) - - assert.NotNil(t, e.File()) - assert.Equal(t, m.File(), e.File()) -} - -func TestEnum_BuildTarget(t *testing.T) { - t.Parallel() - - e := &enum{} - f := dummyFile() - f.addEnum(e) - - assert.False(t, e.BuildTarget()) - f.buildTarget = true - assert.True(t, e.BuildTarget()) -} - -func TestEnum_Descriptor(t *testing.T) { - t.Parallel() - - e := &enum{desc: &descriptor.EnumDescriptorProto{}} - assert.Equal(t, e.desc, e.Descriptor()) -} - -func TestEnum_Parent(t *testing.T) { - t.Parallel() - - e := &enum{} - f := dummyFile() - f.addEnum(e) - - assert.Equal(t, f, e.Parent()) -} - -func TestEnum_Imports(t *testing.T) { - t.Parallel() - assert.Nil(t, (&enum{}).Imports()) -} - -func TestEnum_Values(t *testing.T) { - t.Parallel() - - e := &enum{} - assert.Empty(t, e.Values()) - e.addValue(&enumVal{}) - assert.Len(t, e.Values(), 1) -} - -func TestEnum_Dependents(t *testing.T) { - t.Parallel() - - t.Run("enum in file", func(t *testing.T) { - t.Parallel() - - e := &enum{} - f := dummyFile() - f.addEnum(e) - - assert.Empty(t, e.Dependents()) - }) - - t.Run("enum in message", func(t *testing.T) { - t.Parallel() - - e := &enum{} - m := dummyMsg() - m.addEnum(e) - - assert.Empty(t, e.Dependents()) - }) - - t.Run("external dependents", func(t *testing.T) { - t.Parallel() - - pkg := dummyPkg() - f := &file{ - pkg: pkg, - desc: &descriptor.FileDescriptorProto{ - Package: proto.String(pkg.ProtoName().String()), - Syntax: proto.String(string(Proto3)), - Name: proto.String("test_file.proto"), - }, - } - - e := &enum{} - e.fqn = fullyQualifiedName(f, e) - m := dummyMsg() - m.fqn = fullyQualifiedName(f, m) - e.addDependent(m) - deps := e.Dependents() - - assert.Len(t, deps, 1) - assert.Contains(t, deps, m) - }) -} - -func TestEnum_Extension(t *testing.T) { - // cannot be parallel - - e := &enum{desc: &descriptor.EnumDescriptorProto{}} - assert.NotPanics(t, func() { e.Extension(nil, nil) }) -} - -func TestEnum_Accept(t *testing.T) { - t.Parallel() - - e := &enum{} - e.addValue(&enumVal{}) - - assert.NoError(t, e.accept(nil)) - - v := &mockVisitor{} - assert.NoError(t, e.accept(v)) - assert.Equal(t, 1, v.enum) - assert.Zero(t, v.enumvalue) - - v.Reset() - v.err = errors.New("") - v.v = v - assert.Error(t, e.accept(v)) - assert.Equal(t, 1, v.enum) - assert.Zero(t, v.enumvalue) - - v.Reset() - assert.NoError(t, e.accept(v)) - assert.Equal(t, 1, v.enum) - assert.Equal(t, 1, v.enumvalue) - - v.Reset() - e.addValue(&mockEnumValue{err: errors.New("")}) - assert.Error(t, e.accept(v)) - assert.Equal(t, 1, v.enum) - assert.Equal(t, 2, v.enumvalue) -} - -func TestEnum_ChildAtPath(t *testing.T) { - t.Parallel() - - e := &enum{} - assert.Equal(t, e, e.childAtPath(nil)) - assert.Nil(t, e.childAtPath([]int32{1})) - assert.Nil(t, e.childAtPath([]int32{999, 123})) -} - -type mockEnum struct { - Enum - p ParentEntity - err error -} - -func (e *mockEnum) setParent(p ParentEntity) { e.p = p } - -func (e *mockEnum) accept(v Visitor) error { - _, err := v.VisitEnum(e) - if e.err != nil { - return e.err - } - return err -} - -func dummyEnum() *enum { - f := dummyFile() - e := &enum{desc: &descriptor.EnumDescriptorProto{Name: proto.String("enum")}} - f.addEnum(e) - return e -} diff --git a/v2/enum_value.go b/v2/enum_value.go deleted file mode 100644 index a3ad14b..0000000 --- a/v2/enum_value.go +++ /dev/null @@ -1,69 +0,0 @@ -package pgs - -import ( - "github.com/golang/protobuf/proto" - "github.com/golang/protobuf/protoc-gen-go/descriptor" -) - -// An EnumValue describes a name-value pair for an entry in an enum. -type EnumValue interface { - Entity - - // Descriptor returns the proto descriptor for this Enum Value - Descriptor() *descriptor.EnumValueDescriptorProto - - // Enum returns the parent Enum for this value - Enum() Enum - - // Value returns the numeric enum value associated with this type - Value() int32 - - setEnum(e Enum) -} - -type enumVal struct { - desc *descriptor.EnumValueDescriptorProto - enum Enum - fqn string - - info SourceCodeInfo -} - -func (ev *enumVal) Name() Name { return Name(ev.desc.GetName()) } -func (ev *enumVal) FullyQualifiedName() string { return ev.fqn } -func (ev *enumVal) Syntax() Syntax { return ev.enum.Syntax() } -func (ev *enumVal) Package() Package { return ev.enum.Package() } -func (ev *enumVal) File() File { return ev.enum.File() } -func (ev *enumVal) BuildTarget() bool { return ev.enum.BuildTarget() } -func (ev *enumVal) SourceCodeInfo() SourceCodeInfo { return ev.info } -func (ev *enumVal) Descriptor() *descriptor.EnumValueDescriptorProto { return ev.desc } -func (ev *enumVal) Enum() Enum { return ev.enum } -func (ev *enumVal) Value() int32 { return ev.desc.GetNumber() } -func (ev *enumVal) Imports() []File { return nil } - -func (ev *enumVal) Extension(desc *proto.ExtensionDesc, ext interface{}) (bool, error) { - return extension(ev.desc.GetOptions(), desc, &ext) -} - -func (ev *enumVal) accept(v Visitor) (err error) { - if v == nil { - return nil - } - - _, err = v.VisitEnumValue(ev) - - return -} - -func (ev *enumVal) setEnum(e Enum) { ev.enum = e } - -func (ev *enumVal) childAtPath(path []int32) Entity { - if len(path) == 0 { - return ev - } - return nil -} - -func (ev *enumVal) addSourceCodeInfo(info SourceCodeInfo) { ev.info = info } - -var _ EnumValue = (*enumVal)(nil) diff --git a/v2/enum_value_test.go b/v2/enum_value_test.go deleted file mode 100644 index a66c854..0000000 --- a/v2/enum_value_test.go +++ /dev/null @@ -1,126 +0,0 @@ -package pgs - -import ( - "errors" - "testing" - - "github.com/golang/protobuf/proto" - "github.com/golang/protobuf/protoc-gen-go/descriptor" - "github.com/stretchr/testify/assert" -) - -func TestEnumVal_Name(t *testing.T) { - t.Parallel() - ev := &enumVal{desc: &descriptor.EnumValueDescriptorProto{Name: proto.String("eval")}} - assert.Equal(t, "eval", ev.Name().String()) -} - -func TestEnumVal_FullyQualifiedName(t *testing.T) { - t.Parallel() - - ev := &enumVal{fqn: "ev"} - assert.Equal(t, ev.fqn, ev.FullyQualifiedName()) -} - -func TestEnumVal_Syntax(t *testing.T) { - t.Parallel() - ev := &enumVal{} - e := dummyEnum() - e.addValue(ev) - assert.Equal(t, e.Syntax(), ev.Syntax()) -} - -func TestEnumVal_Package(t *testing.T) { - t.Parallel() - ev := &enumVal{} - e := dummyEnum() - e.addValue(ev) - assert.NotNil(t, ev.Package()) - assert.Equal(t, e.Package(), ev.Package()) -} - -func TestEnumVal_File(t *testing.T) { - t.Parallel() - ev := &enumVal{} - e := dummyEnum() - e.addValue(ev) - assert.NotNil(t, ev.File()) - assert.Equal(t, e.File(), ev.File()) -} - -func TestEnumVal_BuildTarget(t *testing.T) { - t.Parallel() - ev := &enumVal{} - e := dummyEnum() - e.addValue(ev) - assert.False(t, ev.BuildTarget()) - e.parent = &file{buildTarget: true} - assert.True(t, ev.BuildTarget()) -} - -func TestEnumVal_Descriptor(t *testing.T) { - t.Parallel() - ev := &enumVal{desc: &descriptor.EnumValueDescriptorProto{}} - assert.Equal(t, ev.desc, ev.Descriptor()) -} - -func TestEnumVal_Enum(t *testing.T) { - t.Parallel() - ev := &enumVal{} - e := dummyEnum() - e.addValue(ev) - assert.Equal(t, e, ev.Enum()) -} - -func TestEnumVal_Value(t *testing.T) { - t.Parallel() - ev := &enumVal{desc: &descriptor.EnumValueDescriptorProto{Number: proto.Int32(123)}} - assert.Equal(t, int32(123), ev.Value()) -} - -func TestEnumVal_Imports(t *testing.T) { - t.Parallel() - assert.Nil(t, (&enumVal{}).Imports()) -} - -func TestEnumVal_Extension(t *testing.T) { - // cannot be parallel - - ev := &enumVal{desc: &descriptor.EnumValueDescriptorProto{}} - assert.NotPanics(t, func() { ev.Extension(nil, nil) }) -} - -func TestEnumVal_Accept(t *testing.T) { - t.Parallel() - - ev := &enumVal{} - assert.NoError(t, ev.accept(nil)) - - v := &mockVisitor{err: errors.New("")} - assert.Error(t, ev.accept(v)) - assert.Equal(t, 1, v.enumvalue) -} - -func TestEnumVal_ChildAtPath(t *testing.T) { - t.Parallel() - - ev := &enumVal{} - assert.Equal(t, ev, ev.childAtPath(nil)) - assert.Nil(t, ev.childAtPath([]int32{1})) -} - -type mockEnumValue struct { - EnumValue - e Enum - err error -} - -func (ev *mockEnumValue) setEnum(e Enum) { ev.e = e } - -func (ev *mockEnumValue) accept(v Visitor) error { - _, err := v.VisitEnumValue(ev) - if ev.err != nil { - return ev.err - } - return err -} diff --git a/v2/extension.go b/v2/extension.go deleted file mode 100644 index 52474d6..0000000 --- a/v2/extension.go +++ /dev/null @@ -1,122 +0,0 @@ -package pgs - -import ( - "errors" - "fmt" - "reflect" - - "github.com/golang/protobuf/proto" -) - -// An Extension is a custom option annotation that can be applied to an Entity to provide additional -// semantic details and metadata about the Entity. -type Extension interface { - Field - - // ParentEntity returns the ParentEntity where the Extension is defined - DefinedIn() ParentEntity - - // Extendee returns the Message that the Extension is extending - Extendee() Message - - setExtendee(m Message) -} - -type ext struct { - field - - parent ParentEntity - extendee Message - fqn string -} - -func (e *ext) FullyQualifiedName() string { return e.fqn } -func (e *ext) Syntax() Syntax { return e.parent.Syntax() } -func (e *ext) Package() Package { return e.parent.Package() } -func (e *ext) File() File { return e.parent.File() } -func (e *ext) BuildTarget() bool { return e.parent.BuildTarget() } -func (e *ext) DefinedIn() ParentEntity { return e.parent } -func (e *ext) Extendee() Message { return e.extendee } -func (e *ext) Message() Message { return nil } -func (e *ext) InOneOf() bool { return false } -func (e *ext) OneOf() OneOf { return nil } -func (e *ext) setMessage(m Message) {} // noop -func (e *ext) setOneOf(o OneOf) {} // noop -func (e *ext) setExtendee(m Message) { e.extendee = m } - -func (e *ext) accept(v Visitor) (err error) { - if v == nil { - return - } - - _, err = v.VisitExtension(e) - return -} - -var extractor extExtractor - -func init() { extractor = protoExtExtractor{} } - -type extExtractor interface { - HasExtension(proto.Message, *proto.ExtensionDesc) bool - GetExtension(proto.Message, *proto.ExtensionDesc) (interface{}, error) -} - -type protoExtExtractor struct{} - -func (e protoExtExtractor) HasExtension(pb proto.Message, ext *proto.ExtensionDesc) bool { - return proto.HasExtension(pb, ext) -} - -func (e protoExtExtractor) GetExtension(pb proto.Message, ext *proto.ExtensionDesc) (interface{}, error) { - return proto.GetExtension(pb, ext) -} - -func extension(opts proto.Message, e *proto.ExtensionDesc, out interface{}) (bool, error) { - if opts == nil || reflect.ValueOf(opts).IsNil() { - return false, nil - } - - if e == nil { - return false, errors.New("nil *proto.ExtensionDesc parameter provided") - } - - if out == nil { - return false, errors.New("nil extension output parameter provided") - } - - o := reflect.ValueOf(out) - if o.Kind() != reflect.Ptr { - return false, errors.New("out parameter must be a pointer type") - } - - if !extractor.HasExtension(opts, e) { - return false, nil - } - - val, err := extractor.GetExtension(opts, e) - if err != nil || val == nil { - return false, err - } - - v := reflect.ValueOf(val) - for v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface { - v = v.Elem() - } - - for o.Kind() == reflect.Ptr || o.Kind() == reflect.Interface { - if o.Kind() == reflect.Ptr && o.IsNil() { - o.Set(reflect.New(o.Type().Elem())) - } - o = o.Elem() - } - - if v.Type().AssignableTo(o.Type()) { - o.Set(v) - return true, nil - } - - return true, fmt.Errorf("cannot assign extension type %q to output type %q", - v.Type().String(), - o.Type().String()) -} diff --git a/v2/extension_test.go b/v2/extension_test.go deleted file mode 100644 index 08a81f1..0000000 --- a/v2/extension_test.go +++ /dev/null @@ -1,207 +0,0 @@ -package pgs - -import ( - "bytes" - "errors" - "testing" - - "github.com/golang/protobuf/proto" - "github.com/stretchr/testify/assert" -) - -func TestExt_FullyQualifiedName(t *testing.T) { - t.Parallel() - - e := &ext{fqn: "foo"} - assert.Equal(t, e.fqn, e.FullyQualifiedName()) -} - -func TestExt_Syntax(t *testing.T) { - t.Parallel() - - msg := dummyMsg() - e := &ext{parent: msg} - assert.Equal(t, msg.Syntax(), e.Syntax()) -} - -func TestExt_Package(t *testing.T) { - t.Parallel() - - msg := dummyMsg() - e := &ext{parent: msg} - assert.Equal(t, msg.Package(), e.Package()) -} - -func TestExt_File(t *testing.T) { - t.Parallel() - - msg := dummyMsg() - e := &ext{parent: msg} - assert.Equal(t, msg.File(), e.File()) -} - -func TestExt_BuildTarget(t *testing.T) { - t.Parallel() - - msg := dummyMsg() - e := &ext{parent: msg} - assert.Equal(t, msg.BuildTarget(), e.BuildTarget()) -} - -func TestExt_ParentEntity(t *testing.T) { - t.Parallel() - - msg := dummyMsg() - e := &ext{parent: msg} - assert.Equal(t, msg, e.DefinedIn()) -} - -func TestExt_Extendee(t *testing.T) { - t.Parallel() - - msg := dummyMsg() - e := &ext{} - e.setExtendee(msg) - assert.Equal(t, msg, e.Extendee()) -} - -func TestExt_Message(t *testing.T) { - t.Parallel() - - e := &ext{} - assert.Nil(t, e.Message()) -} - -func TestExt_InOneOf(t *testing.T) { - t.Parallel() - - e := &ext{} - assert.False(t, e.InOneOf()) -} - -func TestExt_OneOf(t *testing.T) { - t.Parallel() - - e := &ext{} - assert.Nil(t, e.OneOf()) -} - -func TestExt_Accept(t *testing.T) { - t.Parallel() - - e := &ext{} - - assert.NoError(t, e.accept(nil)) - - v := &mockVisitor{err: errors.New("")} - assert.Error(t, e.accept(v)) - assert.Equal(t, 1, v.extension) -} - -type mockExtractor struct { - has bool - get interface{} - err error -} - -func (e *mockExtractor) HasExtension(proto.Message, *proto.ExtensionDesc) bool { return e.has } - -func (e *mockExtractor) GetExtension(proto.Message, *proto.ExtensionDesc) (interface{}, error) { - return e.get, e.err -} - -var testExtractor = &mockExtractor{} - -func init() { extractor = testExtractor } - -func TestExtension(t *testing.T) { - // cannot be parallel - - defer func() { testExtractor.get = nil }() - - found, err := extension(nil, nil, nil) - assert.False(t, found) - assert.NoError(t, err) - - found, err = extension(proto.Message(nil), nil, nil) - assert.False(t, found) - assert.NoError(t, err) - - opts := &struct{ proto.Message }{} - - found, err = extension(opts, nil, nil) - assert.False(t, found) - assert.Error(t, err) - - desc := &proto.ExtensionDesc{} - - found, err = extension(opts, desc, nil) - assert.False(t, found) - assert.Error(t, err) - - type myExt struct{ Name string } - - found, err = extension(opts, desc, &myExt{}) - assert.False(t, found) - assert.NoError(t, err) - - testExtractor.has = true - - found, err = extension(opts, desc, &myExt{}) - assert.False(t, found) - assert.NoError(t, err) - - testExtractor.err = errors.New("foo") - - found, err = extension(opts, desc, &myExt{}) - assert.False(t, found) - assert.Error(t, err) - - testExtractor.err = nil - testExtractor.get = &myExt{"bar"} - - out := myExt{} - - found, err = extension(opts, desc, out) - assert.False(t, found) - assert.Error(t, err) - - found, err = extension(opts, desc, &out) - assert.True(t, found) - assert.NoError(t, err) - assert.Equal(t, "bar", out.Name) - - var ref *myExt - found, err = extension(opts, desc, &ref) - assert.True(t, found) - assert.NoError(t, err) - assert.Equal(t, "bar", ref.Name) - - found, err = extension(opts, desc, &bytes.Buffer{}) - assert.True(t, found) - assert.Error(t, err) -} - -func TestProtoExtExtractor(t *testing.T) { - e := protoExtExtractor{} - assert.NotPanics(t, func() { e.HasExtension(nil, nil) }) - assert.NotPanics(t, func() { e.GetExtension(nil, nil) }) -} - -// needed to wrapped since there is a Extension method -type mExt interface { - Extension -} - -type mockExtension struct { - mExt - err error -} - -func (e *mockExtension) accept(v Visitor) error { - _, err := v.VisitExtension(e) - if e.err != nil { - return e.err - } - return err -} diff --git a/v2/field.go b/v2/field.go deleted file mode 100644 index 38bc1b4..0000000 --- a/v2/field.go +++ /dev/null @@ -1,138 +0,0 @@ -package pgs - -import ( - "github.com/golang/protobuf/proto" - "github.com/golang/protobuf/protoc-gen-go/descriptor" -) - -// A Field describes a member of a Message. A field may also be a member of a -// OneOf on the Message. -type Field interface { - Entity - - // Descriptor returns the proto descriptor for this field - Descriptor() *descriptor.FieldDescriptorProto - - // Message returns the Message containing this Field. - Message() Message - - // InOneOf returns true if the field is in a OneOf of the parent Message. - // This will return true for synthetic oneofs (proto3 field presence) as well. - InOneOf() bool - - // InRealOneOf returns true if the field is in a OneOf of the parent Message. - // This will return false for synthetic oneofs, and will only include 'real' oneofs. - // See: https://github.com/protocolbuffers/protobuf/blob/v3.17.0/docs/field_presence.md - InRealOneOf() bool - - // OneOf returns the OneOf that this field is a part of. Nil is returned if - // the field is not within a OneOf. - OneOf() OneOf - - // Type returns the FieldType of this Field. - Type() FieldType - - // HasPresence returns true for all fields that have explicit presence as defined by: - // See: https://github.com/protocolbuffers/protobuf/blob/v3.17.0/docs/field_presence.md - HasPresence() bool - - // HasOptionalKeyword returns whether the field is labeled as optional. - HasOptionalKeyword() bool - - // Required returns whether the field is labeled as required. This - // will only be true if the syntax is proto2. - Required() bool - - setMessage(m Message) - setOneOf(o OneOf) - addType(t FieldType) -} - -type field struct { - desc *descriptor.FieldDescriptorProto - fqn string - msg Message - oneof OneOf - typ FieldType - - info SourceCodeInfo -} - -func (f *field) Name() Name { return Name(f.desc.GetName()) } -func (f *field) FullyQualifiedName() string { return f.fqn } -func (f *field) Syntax() Syntax { return f.msg.Syntax() } -func (f *field) Package() Package { return f.msg.Package() } -func (f *field) Imports() []File { return f.typ.Imports() } -func (f *field) File() File { return f.msg.File() } -func (f *field) BuildTarget() bool { return f.msg.BuildTarget() } -func (f *field) SourceCodeInfo() SourceCodeInfo { return f.info } -func (f *field) Descriptor() *descriptor.FieldDescriptorProto { return f.desc } -func (f *field) Message() Message { return f.msg } -func (f *field) InOneOf() bool { return f.oneof != nil } -func (f *field) OneOf() OneOf { return f.oneof } -func (f *field) Type() FieldType { return f.typ } -func (f *field) setMessage(m Message) { f.msg = m } -func (f *field) setOneOf(o OneOf) { f.oneof = o } - -func (f *field) InRealOneOf() bool { - return f.InOneOf() && !f.desc.GetProto3Optional() -} - -func (f *field) HasPresence() bool { - if f.InOneOf() { - return true - } - - if f.Type().IsEmbed() { - return true - } - - if !f.Type().IsRepeated() && !f.Type().IsMap() { - if f.Syntax() == Proto2 { - return true - } - return f.HasOptionalKeyword() - } - return false -} - -func (f *field) HasOptionalKeyword() bool { - if f.Syntax() == Proto3 { - return f.desc.GetProto3Optional() - } - return f.desc.GetLabel() == descriptor.FieldDescriptorProto_LABEL_OPTIONAL -} - -func (f *field) Required() bool { - return f.Syntax().SupportsRequiredPrefix() && - f.desc.GetLabel() == descriptor.FieldDescriptorProto_LABEL_REQUIRED -} - -func (f *field) addType(t FieldType) { - t.setField(f) - f.typ = t -} - -func (f *field) Extension(desc *proto.ExtensionDesc, ext interface{}) (ok bool, err error) { - return extension(f.desc.GetOptions(), desc, &ext) -} - -func (f *field) accept(v Visitor) (err error) { - if v == nil { - return - } - - _, err = v.VisitField(f) - return -} - -func (f *field) childAtPath(path []int32) Entity { - if len(path) == 0 { - return f - } - return nil -} - -func (f *field) addSourceCodeInfo(info SourceCodeInfo) { f.info = info } - -var _ Field = (*field)(nil) diff --git a/v2/field_test.go b/v2/field_test.go deleted file mode 100644 index aae8612..0000000 --- a/v2/field_test.go +++ /dev/null @@ -1,267 +0,0 @@ -package pgs - -import ( - "errors" - "testing" - - "github.com/golang/protobuf/proto" - "github.com/golang/protobuf/protoc-gen-go/descriptor" - "github.com/stretchr/testify/assert" -) - -func TestField_Name(t *testing.T) { - t.Parallel() - - f := &field{desc: &descriptor.FieldDescriptorProto{Name: proto.String("foo")}} - - assert.Equal(t, "foo", f.Name().String()) -} - -func TestField_FullyQualifiedName(t *testing.T) { - t.Parallel() - - f := &field{fqn: "field"} - assert.Equal(t, f.fqn, f.FullyQualifiedName()) -} - -func TestField_Syntax(t *testing.T) { - t.Parallel() - - f := &field{} - m := dummyMsg() - m.addField(f) - - assert.Equal(t, m.Syntax(), f.Syntax()) -} - -func TestField_Package(t *testing.T) { - t.Parallel() - - f := &field{} - m := dummyMsg() - m.addField(f) - - assert.NotNil(t, f.Package()) - assert.Equal(t, m.Package(), f.Package()) -} - -func TestField_File(t *testing.T) { - t.Parallel() - - f := &field{} - m := dummyMsg() - m.addField(f) - - assert.NotNil(t, f.File()) - assert.Equal(t, m.File(), f.File()) -} - -func TestField_BuildTarget(t *testing.T) { - t.Parallel() - - f := &field{} - m := dummyMsg() - m.addField(f) - - assert.False(t, f.BuildTarget()) - m.setParent(&file{buildTarget: true}) - assert.True(t, f.BuildTarget()) -} - -func TestField_Descriptor(t *testing.T) { - t.Parallel() - - f := &field{desc: &descriptor.FieldDescriptorProto{}} - assert.Equal(t, f.desc, f.Descriptor()) -} - -func TestField_Message(t *testing.T) { - t.Parallel() - - f := &field{} - m := dummyMsg() - m.addField(f) - - assert.Equal(t, m, f.Message()) -} - -func TestField_OneOf(t *testing.T) { - t.Parallel() - - f := &field{} - assert.Nil(t, f.OneOf()) - assert.False(t, f.InOneOf()) - - o := dummyOneof() - o.addField(f) - - assert.Equal(t, o, f.OneOf()) - assert.True(t, f.InOneOf()) -} - -func TestField_InRealOneOf(t *testing.T) { - t.Parallel() - - f := dummyField() - assert.False(t, f.InRealOneOf()) - - f = dummyOneOfField(false) - assert.True(t, f.InRealOneOf()) - - f = dummyOneOfField(true) - assert.False(t, f.InRealOneOf()) -} - -func TestField_HasPresence(t *testing.T) { - t.Parallel() - - f := dummyField() - f.addType(&repT{scalarT: &scalarT{}}) - assert.False(t, f.HasPresence()) - - f.addType(&mapT{repT: &repT{scalarT: &scalarT{}}}) - assert.False(t, f.HasPresence()) - - f.addType(&scalarT{}) - assert.False(t, f.HasPresence()) - - opt := true - f.desc = &descriptor.FieldDescriptorProto{Proto3Optional: &opt} - assert.True(t, f.HasPresence()) -} - -func TestField_HasOptionalKeyword(t *testing.T) { - t.Parallel() - - optLabel := descriptor.FieldDescriptorProto_LABEL_OPTIONAL - - f := &field{msg: &msg{parent: dummyFile()}} - assert.False(t, f.HasOptionalKeyword()) - - f.desc = &descriptor.FieldDescriptorProto{Label: &optLabel} - assert.False(t, f.HasOptionalKeyword()) - - f = dummyField() - assert.False(t, f.HasOptionalKeyword()) - - f = dummyOneOfField(false) - assert.False(t, f.HasOptionalKeyword()) - - f = dummyOneOfField(true) - assert.True(t, f.HasOptionalKeyword()) -} - -func TestField_Type(t *testing.T) { - t.Parallel() - - f := &field{} - f.addType(&scalarT{}) - - assert.Equal(t, f.typ, f.Type()) -} - -func TestField_Extension(t *testing.T) { - // cannot be parallel - - f := &field{desc: &descriptor.FieldDescriptorProto{}} - assert.NotPanics(t, func() { f.Extension(nil, nil) }) -} - -func TestField_Accept(t *testing.T) { - t.Parallel() - - f := &field{} - - assert.NoError(t, f.accept(nil)) - - v := &mockVisitor{err: errors.New("")} - assert.Error(t, f.accept(v)) - assert.Equal(t, 1, v.field) -} - -func TestField_Imports(t *testing.T) { - t.Parallel() - - f := &field{} - f.addType(&scalarT{}) - assert.Empty(t, f.Imports()) - - f.addType(&mockT{i: []File{&file{}, &file{}}}) - assert.Len(t, f.Imports(), 2) -} - -func TestField_Required(t *testing.T) { - t.Parallel() - - msg := dummyMsg() - - lbl := descriptor.FieldDescriptorProto_LABEL_REQUIRED - - f := &field{desc: &descriptor.FieldDescriptorProto{Label: &lbl}} - f.setMessage(msg) - - assert.False(t, f.Required(), "proto3 messages can never be marked required") - - f.File().(*file).desc.Syntax = proto.String(string(Proto2)) - assert.True(t, f.Required(), "proto2 + required") - - lbl = descriptor.FieldDescriptorProto_LABEL_OPTIONAL - f.desc.Label = &lbl - assert.False(t, f.Required(), "proto2 + optional") -} - -func TestField_ChildAtPath(t *testing.T) { - t.Parallel() - - f := &field{} - assert.Equal(t, f, f.childAtPath(nil)) - assert.Nil(t, f.childAtPath([]int32{1})) -} - -type mockField struct { - Field - i []File - m Message - err error -} - -func (f *mockField) Imports() []File { return f.i } - -func (f *mockField) setMessage(m Message) { f.m = m } - -func (f *mockField) accept(v Visitor) error { - _, err := v.VisitField(f) - if f.err != nil { - return f.err - } - return err -} - -func dummyField() *field { - m := dummyMsg() - str := descriptor.FieldDescriptorProto_TYPE_STRING - f := &field{desc: &descriptor.FieldDescriptorProto{Name: proto.String("field"), Type: &str}} - m.addField(f) - t := &scalarT{} - f.addType(t) - return f -} - -func dummyOneOfField(synthetic bool) *field { - m := dummyMsg() - o := dummyOneof() - str := descriptor.FieldDescriptorProto_TYPE_STRING - var oIndex int32 = 1 - f := &field{desc: &descriptor.FieldDescriptorProto{ - Name: proto.String("field"), - Type: &str, - OneofIndex: &oIndex, - Proto3Optional: &synthetic, - }} - o.addField(f) - m.addField(f) - m.addOneOf(o) - t := &scalarT{} - f.addType(t) - return f -} diff --git a/v2/field_type.go b/v2/field_type.go deleted file mode 100644 index 771341c..0000000 --- a/v2/field_type.go +++ /dev/null @@ -1,171 +0,0 @@ -package pgs - -// FieldType describes the type of a Field. -type FieldType interface { - // Field returns the parent Field of this type. While two FieldTypes might be - // equivalent, each instance of a FieldType is tied to its Field. - Field() Field - - // IsRepeated returns true if and only if the field is marked as "repeated". - // While map fields may be labeled as repeated, this method will not return - // true for them. - IsRepeated() bool - - // IsMap returns true if the field is a map type. - IsMap() bool - - // IsEnum returns true if the field is a singular enum value. Maps or - // repeated fields containing enums will still return false. - IsEnum() bool - - // IsEmbed returns true if the field is a singular message value. Maps or - // repeated fields containing embeds will still return false. - IsEmbed() bool - - // IsOptional returns true if the field is prefixed as optional. - IsOptional() bool - - // IsRequired returns true if and only if the field is prefixed as required. - IsRequired() bool - - // ProtoType returns the ProtoType value for this field. - ProtoType() ProtoType - - // ProtoLabel returns the ProtoLabel value for this field. - ProtoLabel() ProtoLabel - - // Imports includes all external proto files required by this field. - Imports() []File - - // Enum returns the Enum associated with this FieldType. If IsEnum returns - // false, this value will be nil. - Enum() Enum - - // Embed returns the embedded Message associated with this FieldType. If - // IsEmbed returns false, this value will be nil. - Embed() Message - - // Element returns the FieldTypeElem representing the element component of - // the type. - // - // For repeated fields, the returned type describes the type being repeated (i.e., - // the element type in the list implied by the repeated field). - // - // For maps, the returned type describes the type of values in the map. - // - // Nil will be returned if IsRepeated and IsMap both return false. - Element() FieldTypeElem - - // Key returns the FieldTypeElem representing the key component of the type (i.e, - // the type of keys in a map). - // - // Nil will be returned if IsMap returns false. - Key() FieldTypeElem - - setField(f Field) - toElem() FieldTypeElem -} - -type scalarT struct{ fld Field } - -func (s *scalarT) Field() Field { return s.fld } -func (s *scalarT) IsRepeated() bool { return false } -func (s *scalarT) IsMap() bool { return false } -func (s *scalarT) IsEnum() bool { return false } -func (s *scalarT) IsEmbed() bool { return false } -func (s *scalarT) ProtoType() ProtoType { return ProtoType(s.fld.Descriptor().GetType()) } -func (s *scalarT) ProtoLabel() ProtoLabel { return ProtoLabel(s.fld.Descriptor().GetLabel()) } -func (s *scalarT) Imports() []File { return nil } -func (s *scalarT) setField(f Field) { s.fld = f } -func (s *scalarT) Enum() Enum { return nil } -func (s *scalarT) Embed() Message { return nil } -func (s *scalarT) Element() FieldTypeElem { return nil } -func (s *scalarT) Key() FieldTypeElem { return nil } - -func (s *scalarT) IsOptional() bool { - return !s.fld.Syntax().SupportsRequiredPrefix() || s.ProtoLabel() == Optional -} - -func (s *scalarT) IsRequired() bool { - return s.fld.Syntax().SupportsRequiredPrefix() && s.ProtoLabel() == Required -} - -func (s *scalarT) toElem() FieldTypeElem { - return &scalarE{ - typ: s, - ptype: s.ProtoType(), - } -} - -type enumT struct { - *scalarT - enum Enum -} - -func (e *enumT) Enum() Enum { return e.enum } -func (e *enumT) IsEnum() bool { return true } - -func (e *enumT) Imports() []File { - if f := e.enum.File(); f.Name() != e.fld.File().Name() { - return []File{f} - } - return nil -} - -func (e *enumT) toElem() FieldTypeElem { - return &enumE{ - scalarE: e.scalarT.toElem().(*scalarE), - enum: e.enum, - } -} - -type embedT struct { - *scalarT - msg Message -} - -func (e *embedT) Embed() Message { return e.msg } -func (e *embedT) IsEmbed() bool { return true } - -func (e *embedT) Imports() []File { - if f := e.msg.File(); f.Name() != e.fld.File().Name() { - return []File{f} - } - return nil -} - -func (e *embedT) toElem() FieldTypeElem { - return &embedE{ - scalarE: e.scalarT.toElem().(*scalarE), - msg: e.msg, - } -} - -type repT struct { - *scalarT - el FieldTypeElem -} - -func (r *repT) IsRepeated() bool { return true } -func (r *repT) Element() FieldTypeElem { return r.el } - -func (r *repT) Imports() []File { return r.el.Imports() } - -func (r *repT) toElem() FieldTypeElem { panic("cannot convert repeated FieldType to FieldTypeElem") } - -type mapT struct { - *repT - key FieldTypeElem -} - -func (m *mapT) IsRepeated() bool { return false } -func (m *mapT) IsMap() bool { return true } -func (m *mapT) Key() FieldTypeElem { return m.key } - -var ( - _ FieldType = (*scalarT)(nil) - _ FieldType = (*enumT)(nil) - _ FieldType = (*embedT)(nil) - _ FieldType = (*repT)(nil) - _ FieldType = (*mapT)(nil) -) diff --git a/v2/field_type_elem.go b/v2/field_type_elem.go deleted file mode 100644 index 5b4e93b..0000000 --- a/v2/field_type_elem.go +++ /dev/null @@ -1,80 +0,0 @@ -package pgs - -// FieldTypeElem describes a component of a FieldType. This type only shows up -// in repeated and map FieldTypes. -type FieldTypeElem interface { - // ParentType returns the parent FieldType that holds this element. - ParentType() FieldType - - // ProtoType returns the ProtoType describing this component. - ProtoType() ProtoType - - // IsEmbed returns true if the component is an embedded message. - IsEmbed() bool - - // IsEnum returns true if the component is an enum value. - IsEnum() bool - - // Imports includes all external Files required by this field. - Imports() []File - - // Enum returns the Enum associated with this FieldTypeElem. If IsEnum - // returns false, this value will be nil. - Enum() Enum - - // Embed returns the embedded Message associated with this FieldTypeElem. If - // IsEmbed returns false, this value will be nil. - Embed() Message - - setType(t FieldType) -} - -type scalarE struct { - typ FieldType - ptype ProtoType -} - -func (s *scalarE) ParentType() FieldType { return s.typ } -func (s *scalarE) ProtoType() ProtoType { return s.ptype } -func (s *scalarE) IsEmbed() bool { return false } -func (s *scalarE) IsEnum() bool { return false } -func (s *scalarE) setType(t FieldType) { s.typ = t } -func (s *scalarE) Imports() []File { return nil } -func (s *scalarE) Enum() Enum { return nil } -func (s *scalarE) Embed() Message { return nil } - -type enumE struct { - *scalarE - enum Enum -} - -func (e *enumE) IsEnum() bool { return true } -func (e *enumE) Enum() Enum { return e.enum } - -func (e *enumE) Imports() []File { - if f := e.enum.File(); f.Name() != e.ParentType().Field().File().Name() { - return []File{f} - } - return nil -} - -type embedE struct { - *scalarE - msg Message -} - -func (e *embedE) IsEmbed() bool { return true } -func (e *embedE) Embed() Message { return e.msg } - -func (e *embedE) Imports() []File { - if f := e.msg.File(); f.Name() != e.ParentType().Field().File().Name() { - return []File{f} - } - return nil -} - -var ( - _ FieldTypeElem = (*scalarE)(nil) - _ FieldTypeElem = (*enumE)(nil) - _ FieldTypeElem = (*embedE)(nil) -) diff --git a/v2/field_type_elem_test.go b/v2/field_type_elem_test.go deleted file mode 100644 index d286cd2..0000000 --- a/v2/field_type_elem_test.go +++ /dev/null @@ -1,104 +0,0 @@ -package pgs - -import ( - "testing" - - "github.com/golang/protobuf/proto" - "github.com/golang/protobuf/protoc-gen-go/descriptor" - "github.com/stretchr/testify/assert" -) - -func TestScalarE_ParentType(t *testing.T) { - t.Parallel() - - s := &scalarE{} - s.setType(&scalarT{}) - assert.Equal(t, s.typ, s.ParentType()) -} - -func TestScalarE_ProtoType(t *testing.T) { - t.Parallel() - s := &scalarE{ptype: ProtoType(descriptor.FieldDescriptorProto_TYPE_BYTES)} - assert.Equal(t, s.ptype, s.ProtoType()) -} - -func TestScalarE_IsEmbed(t *testing.T) { - t.Parallel() - assert.False(t, (&scalarE{}).IsEmbed()) -} - -func TestScalarE_IsEnum(t *testing.T) { - t.Parallel() - assert.False(t, (&scalarE{}).IsEnum()) -} - -func TestScalarE_Imports(t *testing.T) { - t.Parallel() - assert.Nil(t, (&scalarE{}).Imports()) -} - -func TestScalarE_Embed(t *testing.T) { - t.Parallel() - assert.Nil(t, (&scalarE{}).Embed()) -} - -func TestScalarE_Enum(t *testing.T) { - t.Parallel() - assert.Nil(t, (&scalarE{}).Enum()) -} - -func TestEnumE_IsEnum(t *testing.T) { - t.Parallel() - assert.True(t, (&enumE{}).IsEnum()) -} - -func TestEnumE_Enum(t *testing.T) { - t.Parallel() - e := &enumE{enum: dummyEnum()} - assert.Equal(t, e.enum, e.Enum()) -} - -func TestEnumE_Imports(t *testing.T) { - t.Parallel() - - en := dummyEnum() - f := dummyFile() - en.parent = f - e := &enumE{scalarE: &scalarE{}, enum: en} - fld := dummyField() - e.typ = fld.typ - - assert.Empty(t, e.Imports()) - - f.desc.Name = proto.String("some/other/file.proto") - assert.Len(t, e.Imports(), 1) - assert.Equal(t, e.Enum().File(), e.Imports()[0]) -} - -func TestEmbedE_IsEmbed(t *testing.T) { - t.Parallel() - assert.True(t, (&embedE{}).IsEmbed()) -} - -func TestEmbedE_Embed(t *testing.T) { - t.Parallel() - e := &embedE{msg: dummyMsg()} - assert.Equal(t, e.msg, e.Embed()) -} - -func TestEmbedE_Imports(t *testing.T) { - t.Parallel() - - f := dummyFile() - msg := dummyMsg() - msg.parent = f - e := &embedE{scalarE: &scalarE{}, msg: msg} - fld := dummyField() - e.typ = fld.typ - - assert.Empty(t, e.Imports()) - f.desc.Name = proto.String("some/other/file.proto") - - assert.Len(t, e.Imports(), 1) - assert.Equal(t, e.Embed().File(), e.Imports()[0]) -} diff --git a/v2/field_type_test.go b/v2/field_type_test.go deleted file mode 100644 index dcb78a8..0000000 --- a/v2/field_type_test.go +++ /dev/null @@ -1,313 +0,0 @@ -package pgs - -import ( - "testing" - - "github.com/golang/protobuf/proto" - "github.com/golang/protobuf/protoc-gen-go/descriptor" - "github.com/stretchr/testify/assert" -) - -func TestScalarT_Field(t *testing.T) { - t.Parallel() - - f := dummyField() - s := &scalarT{} - f.addType(s) - - assert.Equal(t, f, s.Field()) -} - -func TestScalarT_IsRepeated(t *testing.T) { - t.Parallel() - s := &scalarT{} - assert.False(t, s.IsRepeated()) -} - -func TestScalarT_IsMap(t *testing.T) { - t.Parallel() - s := &scalarT{} - assert.False(t, s.IsMap()) -} - -func TestScalarT_IsEnum(t *testing.T) { - t.Parallel() - s := &scalarT{} - assert.False(t, s.IsEnum()) -} - -func TestScalarT_IsEmbed(t *testing.T) { - t.Parallel() - s := &scalarT{} - assert.False(t, s.IsEmbed()) -} - -func TestScalarT_ProtoType(t *testing.T) { - t.Parallel() - f := dummyField() - s := &scalarT{} - f.addType(s) - - assert.Equal(t, f.desc.GetType(), s.ProtoType().Proto()) -} - -func TestScalarT_ProtoLabel(t *testing.T) { - t.Parallel() - f := dummyField() - s := &scalarT{} - f.addType(s) - - assert.Equal(t, f.desc.GetLabel(), s.ProtoLabel().Proto()) -} - -func TestScalarT_Imports(t *testing.T) { - t.Parallel() - assert.Nil(t, (&scalarT{}).Imports()) -} - -func TestScalarT_Enum(t *testing.T) { - t.Parallel() - assert.Nil(t, (&scalarT{}).Enum()) -} - -func TestScalarT_Embed(t *testing.T) { - t.Parallel() - assert.Nil(t, (&scalarT{}).Embed()) -} - -func TestScalarT_Element(t *testing.T) { - t.Parallel() - assert.Nil(t, (&scalarT{}).Element()) -} - -func TestScalarT_Key(t *testing.T) { - t.Parallel() - assert.Nil(t, (&scalarT{}).Key()) -} - -func TestScalarT_IsOptional(t *testing.T) { - t.Parallel() - - s := &scalarT{} - f := dummyOneOfField(true) - f.addType(s) - - assert.True(t, s.IsOptional()) - - fl := dummyFile() - fl.desc.Syntax = nil - f.Message().setParent(fl) - - assert.True(t, s.IsOptional()) - - req := descriptor.FieldDescriptorProto_LABEL_REQUIRED - f.desc.Label = &req - - assert.False(t, s.IsOptional()) -} - -func TestScalarT_IsNotOptional(t *testing.T) { - t.Parallel() - - s := &scalarT{} - f := dummyField() - f.addType(s) - - assert.True(t, s.IsOptional()) - - fl := dummyFile() - fl.desc.Syntax = nil - f.Message().setParent(fl) - - assert.True(t, s.IsOptional()) - - req := descriptor.FieldDescriptorProto_LABEL_REQUIRED - f.desc.Label = &req - - assert.False(t, s.IsOptional()) -} - -func TestScalarT_IsRequired(t *testing.T) { - t.Parallel() - - s := &scalarT{} - f := dummyField() - f.addType(s) - - assert.False(t, s.IsRequired()) - - fl := dummyFile() - fl.desc.Syntax = nil - f.Message().setParent(fl) - - assert.False(t, s.IsRequired()) - - req := descriptor.FieldDescriptorProto_LABEL_REQUIRED - f.desc.Label = &req - - assert.True(t, s.IsRequired()) -} - -func TestScalarT_ToElem(t *testing.T) { - t.Parallel() - - s := &scalarT{} - f := dummyField() - f.addType(s) - - el := s.toElem() - assert.Equal(t, s, el.ParentType()) - assert.Equal(t, s.ProtoType(), el.ProtoType()) -} - -func TestEnumT_Enum(t *testing.T) { - t.Parallel() - e := &enumT{enum: &enum{}} - assert.Equal(t, e.enum, e.Enum()) -} - -func TestEnumT_IsEnum(t *testing.T) { - t.Parallel() - e := &enumT{} - assert.True(t, e.IsEnum()) -} - -func TestEnumT_Imports(t *testing.T) { - t.Parallel() - - f := dummyFile() - en := dummyEnum() - en.parent = f - e := &enumT{scalarT: &scalarT{}, enum: en} - fld := dummyField() - fld.addType(e) - - assert.Empty(t, e.Imports()) - - f.desc.Name = proto.String("some/other/file.proto") - assert.Len(t, e.Imports(), 1) - assert.Equal(t, e.enum.File(), e.Imports()[0]) -} - -func TestEnumT_ToElem(t *testing.T) { - t.Parallel() - - e := &enumT{ - scalarT: &scalarT{}, - enum: dummyEnum(), - } - f := dummyField() - f.addType(e) - - el := e.toElem() - assert.True(t, el.IsEnum()) - assert.Equal(t, e.enum, el.Enum()) - assert.Equal(t, e.ProtoType(), el.ProtoType()) -} - -func TestEmbedT_IsEmbed(t *testing.T) { - t.Parallel() - e := &embedT{} - assert.True(t, e.IsEmbed()) -} - -func TestEmbedT_Embed(t *testing.T) { - t.Parallel() - e := &embedT{msg: dummyMsg()} - assert.Equal(t, e.msg, e.Embed()) -} - -func TestEmbedT_Imports(t *testing.T) { - t.Parallel() - - msg := dummyMsg() - f := dummyFile() - msg.parent = f - e := &embedT{scalarT: &scalarT{}, msg: msg} - dummyField().addType(e) - - assert.Empty(t, e.Imports()) - - f.desc.Name = proto.String("some/other/file.proto") - assert.Len(t, e.Imports(), 1) - assert.Equal(t, e.msg.File(), e.Imports()[0]) -} - -func TestEmbedT_ToElem(t *testing.T) { - t.Parallel() - - e := &embedT{ - scalarT: &scalarT{}, - msg: dummyMsg(), - } - f := dummyField() - f.addType(e) - - el := e.toElem() - assert.True(t, el.IsEmbed()) - assert.Equal(t, e.msg, el.Embed()) - assert.Equal(t, e.ProtoType(), el.ProtoType()) -} - -func TestRepT_IsRepeated(t *testing.T) { - t.Parallel() - r := &repT{} - assert.True(t, r.IsRepeated()) -} - -func TestRepT_Element(t *testing.T) { - t.Parallel() - r := &repT{el: &scalarE{}} - assert.Equal(t, r.el, r.Element()) -} - -func TestRepT_Imports(t *testing.T) { - t.Parallel() - - msg := dummyMsg() - f := dummyFile() - msg.parent = f - e := &embedT{scalarT: &scalarT{}, msg: msg} - dummyField().addType(e) - - fld := dummyField() - r := &repT{scalarT: &scalarT{}, el: e.toElem()} - fld.addType(r) - - assert.Empty(t, r.Imports()) - - f.desc.Name = proto.String("some/other/file.proto") - assert.Len(t, r.Imports(), 1) - assert.Equal(t, r.el.Embed().File(), r.Imports()[0]) -} - -func TestRepT_ToElem(t *testing.T) { - t.Parallel() - assert.Panics(t, func() { (&repT{}).toElem() }) -} - -func TestMapT_IsRepeated(t *testing.T) { - t.Parallel() - assert.False(t, (&mapT{}).IsRepeated()) -} - -func TestMapT_IsMap(t *testing.T) { - t.Parallel() - assert.True(t, (&mapT{}).IsMap()) -} - -func TestMapT_Key(t *testing.T) { - t.Parallel() - m := &mapT{key: &scalarE{}} - assert.Equal(t, m.key, m.Key()) -} - -type mockT struct { - FieldType - i []File - f Field -} - -func (t *mockT) Imports() []File { return t.i } - -func (t *mockT) setField(f Field) { t.f = f } diff --git a/v2/file.go b/v2/file.go deleted file mode 100644 index 3e8c075..0000000 --- a/v2/file.go +++ /dev/null @@ -1,290 +0,0 @@ -package pgs - -import ( - "github.com/golang/protobuf/proto" - "github.com/golang/protobuf/protoc-gen-go/descriptor" -) - -// File describes the contents of a single proto file. -type File interface { - ParentEntity - - // InputPath returns the input FilePath. This is equivalent to the value - // returned by Name. - InputPath() FilePath - - // Descriptor returns the underlying descriptor for the proto file - Descriptor() *descriptor.FileDescriptorProto - - // TransitiveImports returns all direct and transitive dependencies of this - // File. Use Imports to obtain only direct dependencies. - TransitiveImports() []File - - // UnusedImports returns all imported files that aren't used by the current - // File. Public imports are not included in this list. - UnusedImports() []File - - // Dependents returns all files where the given file was directly or - // transitively imported. - Dependents() []File - - // Services returns the services from this proto file. - Services() []Service - - // SyntaxSourceCodeInfo returns the comment info attached to the `syntax` - // stanza of the file. This method is an alias of the SourceCodeInfo method. - SyntaxSourceCodeInfo() SourceCodeInfo - - // PackageSourceCodeInfo returns the comment info attached to the `package` - // stanza of the file. - PackageSourceCodeInfo() SourceCodeInfo - - setPackage(p Package) - - addFileDependency(fl File) - - addDependent(fl File) - - addService(s Service) - - addPackageSourceCodeInfo(info SourceCodeInfo) -} - -type file struct { - desc *descriptor.FileDescriptorProto - fqn string - pkg Package - enums []Enum - defExts []Extension - dependents []File - dependentsCache []File - fileDependencies []File - msgs []Message - srvs []Service - buildTarget bool - syntaxInfo, packageInfo SourceCodeInfo -} - -func (f *file) Name() Name { return Name(f.desc.GetName()) } -func (f *file) FullyQualifiedName() string { return f.fqn } -func (f *file) Syntax() Syntax { return Syntax(f.desc.GetSyntax()) } -func (f *file) Package() Package { return f.pkg } -func (f *file) File() File { return f } -func (f *file) BuildTarget() bool { return f.buildTarget } -func (f *file) Descriptor() *descriptor.FileDescriptorProto { return f.desc } -func (f *file) InputPath() FilePath { return FilePath(f.Name().String()) } -func (f *file) MapEntries() (me []Message) { return nil } -func (f *file) SourceCodeInfo() SourceCodeInfo { return f.SyntaxSourceCodeInfo() } -func (f *file) SyntaxSourceCodeInfo() SourceCodeInfo { return f.syntaxInfo } -func (f *file) PackageSourceCodeInfo() SourceCodeInfo { return f.packageInfo } - -func (f *file) Enums() []Enum { - return f.enums -} - -func (f *file) AllEnums() []Enum { - es := f.Enums() - for _, m := range f.msgs { - es = append(es, m.AllEnums()...) - } - return es -} - -func (f *file) Messages() []Message { - return f.msgs -} - -func (f *file) AllMessages() []Message { - msgs := f.Messages() - for _, m := range f.msgs { - msgs = append(msgs, m.AllMessages()...) - } - return msgs -} - -func (f *file) Services() []Service { - return f.srvs -} - -func (f *file) Imports() []File { - out := make([]File, len(f.fileDependencies)) - copy(out, f.fileDependencies) - return out -} - -func (f *file) TransitiveImports() []File { - importMap := make(map[string]File, len(f.fileDependencies)) - for _, fl := range f.fileDependencies { - importMap[fl.Name().String()] = fl - for _, imp := range fl.TransitiveImports() { - importMap[imp.File().Name().String()] = imp - } - } - - out := make([]File, 0, len(importMap)) - for _, imp := range importMap { - out = append(out, imp) - } - - return out -} - -func (f *file) UnusedImports() []File { - public := make(map[int]struct{}, len(f.desc.PublicDependency)) - for _, i := range f.desc.PublicDependency { - public[int(i)] = struct{}{} - } - - mp := make(map[string]File, len(f.fileDependencies)) - for i, fl := range f.fileDependencies { - if _, ok := public[i]; ok { - continue - } - mp[fl.Name().String()] = fl - } - - for _, msg := range f.AllMessages() { - for _, imp := range msg.Imports() { - delete(mp, imp.Name().String()) - } - } - - for _, svc := range f.Services() { - for _, imp := range svc.Imports() { - delete(mp, imp.Name().String()) - } - } - - out := make([]File, 0, len(mp)) - for _, fl := range mp { - out = append(out, fl) - } - - return out -} - -func (f *file) Dependents() []File { - if f.dependentsCache == nil { - set := make(map[string]File) - for _, fl := range f.dependents { - set[fl.Name().String()] = fl - for _, d := range fl.Dependents() { - set[d.Name().String()] = d - } - } - - f.dependentsCache = make([]File, 0, len(set)) - for _, d := range set { - f.dependentsCache = append(f.dependentsCache, d) - } - } - return f.dependentsCache -} - -func (f *file) Extension(desc *proto.ExtensionDesc, ext interface{}) (bool, error) { - return extension(f.desc.GetOptions(), desc, &ext) -} - -func (f *file) DefinedExtensions() []Extension { - return f.defExts -} - -func (f *file) accept(v Visitor) (err error) { - if v == nil { - return nil - } - - if v, err = v.VisitFile(f); err != nil || v == nil { - return - } - - for _, e := range f.enums { - if err = e.accept(v); err != nil { - return - } - } - - for _, m := range f.msgs { - if err = m.accept(v); err != nil { - return - } - } - - for _, s := range f.srvs { - if err = s.accept(v); err != nil { - return - } - } - - for _, ext := range f.defExts { - if err = ext.accept(v); err != nil { - return - } - } - - return -} - -func (f *file) addDefExtension(ext Extension) { - f.defExts = append(f.defExts, ext) -} - -func (f *file) setPackage(pkg Package) { f.pkg = pkg } - -func (f *file) addEnum(e Enum) { - e.setParent(f) - f.enums = append(f.enums, e) -} - -func (f *file) addFileDependency(fl File) { - f.fileDependencies = append(f.fileDependencies, fl) -} - -func (f *file) addDependent(fl File) { - f.dependents = append(f.dependents, fl) -} - -func (f *file) addMessage(m Message) { - m.setParent(f) - f.msgs = append(f.msgs, m) -} - -func (f *file) addService(s Service) { - s.setFile(f) - f.srvs = append(f.srvs, s) -} - -func (f *file) addMapEntry(m Message) { panic("cannot add map entry directly to file") } - -func (f *file) childAtPath(path []int32) Entity { - switch { - case len(path) == 0: - return f - case len(path)%2 == 1: // all declaration paths are multiples of two - return nil - } - - var child Entity - switch path[0] { - case messageTypePath: - child = f.msgs[path[1]] - case enumTypePath: - child = f.enums[path[1]] - case servicePath: - child = f.srvs[path[1]] - default: - return nil - } - - return child.childAtPath(path[2:]) -} - -func (f *file) addSourceCodeInfo(info SourceCodeInfo) { - f.syntaxInfo = info -} - -func (f *file) addPackageSourceCodeInfo(info SourceCodeInfo) { - f.packageInfo = info -} - -var _ File = (*file)(nil) diff --git a/v2/file_test.go b/v2/file_test.go deleted file mode 100644 index 8384925..0000000 --- a/v2/file_test.go +++ /dev/null @@ -1,366 +0,0 @@ -package pgs - -import ( - "errors" - "testing" - - "github.com/golang/protobuf/proto" - "github.com/golang/protobuf/protoc-gen-go/descriptor" - "github.com/stretchr/testify/assert" -) - -func TestFile_Name(t *testing.T) { - t.Parallel() - - f := &file{desc: &descriptor.FileDescriptorProto{ - Name: proto.String("foobar"), - }} - - assert.Equal(t, Name("foobar"), f.Name()) -} - -func TestFile_FullyQualifiedName(t *testing.T) { - t.Parallel() - - f := &file{fqn: "foo"} - assert.Equal(t, f.fqn, f.FullyQualifiedName()) -} - -func TestFile_Syntax(t *testing.T) { - t.Parallel() - - f := &file{desc: &descriptor.FileDescriptorProto{}} - - assert.Equal(t, Proto2, f.Syntax()) -} - -func TestFile_Package(t *testing.T) { - t.Parallel() - - f := &file{pkg: &pkg{comments: "fizz/buzz"}} - assert.Equal(t, f.pkg, f.Package()) -} - -func TestFile_File(t *testing.T) { - t.Parallel() - - f := &file{buildTarget: true} - assert.Equal(t, f, f.File()) -} - -func TestFile_BuildTarget(t *testing.T) { - t.Parallel() - - f := &file{buildTarget: true} - assert.True(t, f.BuildTarget()) - f.buildTarget = false - assert.False(t, f.BuildTarget()) -} - -func TestFile_Descriptor(t *testing.T) { - t.Parallel() - - f := &file{desc: &descriptor.FileDescriptorProto{}} - assert.Equal(t, f.desc, f.Descriptor()) -} - -func TestFile_InputPath(t *testing.T) { - t.Parallel() - - f := &file{desc: &descriptor.FileDescriptorProto{Name: proto.String("foo.bar")}} - assert.Equal(t, "foo.bar", f.InputPath().String()) -} - -func TestFile_Enums(t *testing.T) { - t.Parallel() - - f := &file{} - - assert.Empty(t, f.Enums()) - - e := &enum{} - f.addEnum(e) - assert.Len(t, f.Enums(), 1) - assert.Equal(t, e, f.Enums()[0]) -} - -func TestFile_AllEnums(t *testing.T) { - t.Parallel() - - f := &file{} - - assert.Empty(t, f.AllEnums()) - - f.addEnum(&enum{}) - m := &msg{} - m.addEnum(&enum{}) - f.addMessage(m) - - assert.Len(t, f.Enums(), 1) - assert.Len(t, f.AllEnums(), 2) -} - -func TestFile_Messages(t *testing.T) { - t.Parallel() - - f := &file{} - - assert.Empty(t, f.Messages()) - - m := &msg{} - f.addMessage(m) - assert.Len(t, f.Messages(), 1) - assert.Equal(t, m, f.Messages()[0]) -} - -func TestFile_MapEntries(t *testing.T) { - t.Parallel() - f := &file{} - - assert.Panics(t, func() { f.addMapEntry(&msg{}) }) - assert.Empty(t, f.MapEntries()) -} - -func TestFile_AllMessages(t *testing.T) { - t.Parallel() - - f := &file{} - - assert.Empty(t, f.AllMessages()) - - m := &msg{} - m.addMessage(&msg{}) - f.addMessage(m) - - assert.Len(t, f.Messages(), 1) - assert.Len(t, f.AllMessages(), 2) -} - -func TestFile_Services(t *testing.T) { - t.Parallel() - - f := &file{} - - assert.Empty(t, f.Services()) - - s := &service{} - f.addService(s) - - assert.Len(t, f.Services(), 1) - assert.Equal(t, s, f.Services()[0]) -} - -func TestFile_Imports(t *testing.T) { - t.Parallel() - - flDep := dummyFile() - nf := &file{desc: &descriptor.FileDescriptorProto{ - Name: proto.String("foobar"), - }} - flDep.addFileDependency(nf) - - f := &file{} - assert.Empty(t, f.Imports()) - - f.addFileDependency(flDep) - assert.Len(t, f.Imports(), 1) -} - -func TestFile_TransitiveImports(t *testing.T) { - t.Parallel() - - flDep := dummyFile() - nf := &file{desc: &descriptor.FileDescriptorProto{ - Name: proto.String("foobar"), - }} - flDep.addFileDependency(nf) - - f := &file{} - assert.Empty(t, f.TransitiveImports()) - - f.addFileDependency(flDep) - assert.Len(t, f.TransitiveImports(), 2) -} - -func TestFile_UnusedImports(t *testing.T) { - t.Parallel() - - target := &file{desc: &descriptor.FileDescriptorProto{ - Name: proto.String("foobar"), - }} - - unusedFile := &file{desc: &descriptor.FileDescriptorProto{ - Name: proto.String("i/am/unused.proto"), - }} - - target.addFileDependency(unusedFile) - - publicFile := &file{desc: &descriptor.FileDescriptorProto{ - Name: proto.String("i/am/public.proto"), - }} - - target.addFileDependency(publicFile) - target.desc.PublicDependency = append(target.desc.PublicDependency, 1) - - msgDep := dummyMsg() - usedFile := msgDep.File().(*file) - - ft := &embedT{scalarT: &scalarT{}, msg: msgDep} - fld := &field{} - fld.addType(ft) - m := &msg{} - m.addField(fld) - target.addMessage(m) - - mtd := &method{in: msgDep, out: m} - svc := &service{} - svc.addMethod(mtd) - target.addService(svc) - - target.addFileDependency(usedFile) - - unused := target.UnusedImports() - assert.Len(t, unused, 1) - assert.Equal(t, unusedFile, unused[0]) -} - -func TestFile_Dependents(t *testing.T) { - t.Parallel() - - f := &file{} - fl := dummyFile() - f.addDependent(fl) - deps := f.Dependents() - - assert.Len(t, deps, 1) - assert.Contains(t, deps, fl) -} - -func TestFile_Accept(t *testing.T) { - t.Parallel() - - f := &file{} - - assert.Nil(t, f.accept(nil)) - - v := &mockVisitor{} - assert.NoError(t, f.accept(v)) - assert.Equal(t, 1, v.file) - - v.Reset() - v.v = v - v.err = errors.New("foo") - assert.Equal(t, v.err, f.accept(v)) - assert.Equal(t, 1, v.file) - assert.Zero(t, v.enum) - assert.Zero(t, v.message) - assert.Zero(t, v.service) - assert.Zero(t, v.extension) - - v.Reset() - f.addEnum(&enum{}) - f.addMessage(&msg{}) - f.addService(&service{}) - f.addDefExtension(&ext{}) - assert.NoError(t, f.accept(v)) - assert.Equal(t, 1, v.file) - assert.Equal(t, 1, v.enum) - assert.Equal(t, 1, v.message) - assert.Equal(t, 1, v.service) - assert.Equal(t, 1, v.extension) - - v.Reset() - f.addDefExtension(&mockExtension{err: errors.New("fizz")}) - assert.EqualError(t, f.accept(v), "fizz") - assert.Equal(t, 1, v.file) - assert.Equal(t, 1, v.enum) - assert.Equal(t, 1, v.message) - assert.Equal(t, 1, v.service) - assert.Equal(t, 2, v.extension) - - v.Reset() - f.addService(&mockService{err: errors.New("fizz")}) - assert.EqualError(t, f.accept(v), "fizz") - assert.Equal(t, 1, v.file) - assert.Equal(t, 1, v.enum) - assert.Equal(t, 1, v.message) - assert.Equal(t, 2, v.service) - assert.Zero(t, v.extension) - - v.Reset() - f.addMessage(&mockMessage{err: errors.New("bar")}) - assert.EqualError(t, f.accept(v), "bar") - assert.Equal(t, 1, v.file) - assert.Equal(t, 1, v.enum) - assert.Equal(t, 2, v.message) - assert.Zero(t, v.service) - assert.Zero(t, v.extension) - - v.Reset() - f.addEnum(&mockEnum{err: errors.New("baz")}) - assert.EqualError(t, f.accept(v), "baz") - assert.Equal(t, 1, v.file) - assert.Equal(t, 2, v.enum) - assert.Zero(t, v.message) - assert.Zero(t, v.service) - assert.Zero(t, v.extension) -} - -func TestFile_Extension(t *testing.T) { - // cannot be parallel - - assert.NotPanics(t, func() { - (&file{ - desc: &descriptor.FileDescriptorProto{}, - }).Extension(nil, nil) - }) -} - -func TestFile_DefinedExtensions(t *testing.T) { - t.Parallel() - - f := &file{} - assert.Empty(t, f.DefinedExtensions()) - - ext := &ext{} - f.addDefExtension(ext) - assert.Len(t, f.DefinedExtensions(), 1) -} - -// needed to wrap since there is a File method -type mFile interface { - File -} - -type mockFile struct { - mFile - pkg Package - err error -} - -func (f *mockFile) setPackage(p Package) { - f.pkg = p -} - -func (f *mockFile) accept(v Visitor) error { - _, err := v.VisitFile(f) - if f.err != nil { - return f.err - } - return err -} - -func dummyFile() *file { - pkg := dummyPkg() - f := &file{ - pkg: pkg, - desc: &descriptor.FileDescriptorProto{ - Package: proto.String(pkg.ProtoName().String()), - Syntax: proto.String(string(Proto3)), - Name: proto.String("file.proto"), - }, - } - pkg.addFile(f) - - return f -} diff --git a/v2/generator.go b/v2/generator.go deleted file mode 100644 index 9442411..0000000 --- a/v2/generator.go +++ /dev/null @@ -1,91 +0,0 @@ -package pgs - -import ( - "io" - "log" - "os" -) - -// Generator configures and executes a protoc plugin's lifecycle. -type Generator struct { - Debugger - - persister persister // handles writing artifacts to their output - workflow workflow - - mods []Module // registered pg* modules - - in io.Reader // protoc input reader - out io.Writer // protoc output writer - - debug bool // whether or not to print debug messages - - params Parameters // CLI parameters passed in from protoc - paramMutators []ParamMutator // registered param mutators -} - -// Init configures a new Generator. InitOptions may be provided as well to -// modify the behavior of the generator. -func Init(opts ...InitOption) *Generator { - g := &Generator{ - in: os.Stdin, - out: os.Stdout, - persister: newPersister(), - workflow: &onceWorkflow{workflow: &standardWorkflow{}}, - } - - for _, opt := range opts { - opt(g) - } - - g.Debugger = initDebugger(g.debug, log.New(os.Stderr, "", 0)) - g.persister.SetDebugger(g.Debugger) - - return g -} - -// RegisterModule should be called before Render to attach a custom Module to -// the Generator. This method can be called multiple times. -func (g *Generator) RegisterModule(m ...Module) *Generator { - for _, mod := range m { - g.Assert(mod != nil, "nil module provided") - g.Debug("registering module: ", mod.Name()) - } - - g.mods = append(g.mods, m...) - return g -} - -// RegisterPostProcessor should be called before Render to attach -// PostProcessors to the Generator. This method can be called multiple times. -// PostProcessors are executed against their matches in the order in which they -// are registered. -func (g *Generator) RegisterPostProcessor(p ...PostProcessor) *Generator { - for _, pp := range p { - g.Assert(pp != nil, "nil post-processor provided") - } - g.persister.AddPostProcessor(p...) - return g -} - -// AST returns the constructed AST graph from the gatherer. This method is -// idempotent, can be called multiple times (before and after calls to Render, -// even), and is particularly useful in testing. -func (g *Generator) AST() AST { - return g.workflow.Init(g) -} - -// Render executes the protoc plugin flow, gathering the AST from the input -// io.Reader (typically stdin via protoc), running all the registered modules, -// and persisting the generated artifacts to the output io.Writer (typically -// stdout to protoc + direct file system writes for custom artifacts). This -// method is idempotent, in that subsequent calls to Render will have no -// effect. -func (g *Generator) Render() { - ast := g.workflow.Init(g) - arts := g.workflow.Run(ast) - g.workflow.Persist(arts) -} - -func (g *Generator) push(prefix string) { g.Debugger = g.Push(prefix) } -func (g *Generator) pop() { g.Debugger = g.Pop() } diff --git a/v2/generator_test.go b/v2/generator_test.go deleted file mode 100644 index 4c29011..0000000 --- a/v2/generator_test.go +++ /dev/null @@ -1,118 +0,0 @@ -package pgs - -import ( - "bytes" - "os" - "testing" - - "github.com/golang/protobuf/proto" - "github.com/golang/protobuf/protoc-gen-go/descriptor" - plugin_go "github.com/golang/protobuf/protoc-gen-go/plugin" - "github.com/stretchr/testify/assert" -) - -func TestInit(t *testing.T) { - t.Parallel() - - b := &bytes.Buffer{} - bb := &bytes.Buffer{} - g := Init(ProtocInput(b), ProtocOutput(bb), func(g *Generator) { /* noop */ }) - - assert.NotNil(t, g) - assert.Equal(t, g.in, b) - assert.Equal(t, g.out, bb) - - g = Init() - assert.Equal(t, os.Stdin, g.in) - assert.Equal(t, os.Stdout, g.out) - - _, ok := g.workflow.(*onceWorkflow) - assert.True(t, ok) -} - -func TestGenerator_RegisterModule(t *testing.T) { - t.Parallel() - - d := InitMockDebugger() - g := &Generator{Debugger: d} - - assert.Empty(t, g.mods) - g.RegisterModule(&mockModule{name: "foo"}) - - assert.False(t, d.Failed()) - assert.Len(t, g.mods, 1) - - assert.Panics(t, func() { g.RegisterModule(nil) }) - assert.True(t, d.Failed()) -} - -func TestGenerator_RegisterPostProcessor(t *testing.T) { - t.Parallel() - - d := InitMockDebugger() - p := newPersister() - g := &Generator{Debugger: d, persister: p} - - pp := &mockPP{} - - assert.Empty(t, p.procs) - g.RegisterPostProcessor(pp) - - assert.False(t, d.Failed()) - assert.Len(t, p.procs, 1) - - g.RegisterPostProcessor(nil) - assert.True(t, d.Failed()) -} - -func TestGenerator_AST(t *testing.T) { - t.Parallel() - - g := Init() - - wf := &dummyWorkflow{AST: new(graph)} - g.workflow = wf - - assert.Equal(t, wf.AST, g.AST()) - assert.True(t, wf.initted) -} - -func TestGenerator_Render(t *testing.T) { - // cannot be parallel - - req := &plugin_go.CodeGeneratorRequest{ - FileToGenerate: []string{"foo"}, - ProtoFile: []*descriptor.FileDescriptorProto{ - { - Name: proto.String("foo"), - Syntax: proto.String("proto2"), - Package: proto.String("bar"), - }, - }, - } - b, err := proto.Marshal(req) - assert.NoError(t, err) - - buf := &bytes.Buffer{} - g := Init(ProtocInput(bytes.NewReader(b)), ProtocOutput(buf)) - assert.NotPanics(t, g.Render) - - var res plugin_go.CodeGeneratorResponse - assert.NoError(t, proto.Unmarshal(buf.Bytes(), &res)) -} - -func TestGenerator_PushPop(t *testing.T) { - t.Parallel() - - g := Init() - g.push("foo") - - pd, ok := g.Debugger.(prefixedDebugger) - assert.True(t, ok) - assert.Equal(t, "[foo]", pd.prefix) - - g.pop() - - _, ok = g.Debugger.(rootDebugger) - assert.True(t, ok) -} diff --git a/v2/go.mod b/v2/go.mod deleted file mode 100644 index e81ad1a..0000000 --- a/v2/go.mod +++ /dev/null @@ -1,17 +0,0 @@ -module github.com/lyft/protoc-gen-star/v2 - -go 1.17 - -require ( - github.com/golang/protobuf v1.5.2 - github.com/spf13/afero v1.3.3 - github.com/stretchr/testify v1.6.1 - google.golang.org/protobuf v1.26.0 -) - -require ( - github.com/davecgh/go-spew v1.1.0 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - golang.org/x/text v0.3.0 // indirect - gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect -) diff --git a/v2/init_option.go b/v2/init_option.go deleted file mode 100644 index 5c92c2f..0000000 --- a/v2/init_option.go +++ /dev/null @@ -1,57 +0,0 @@ -package pgs - -import ( - "io" - "os" - - "github.com/spf13/afero" -) - -// An InitOption modifies the behavior of a Generator at initialization. -type InitOption func(g *Generator) - -// ParamMutator is a method that modifies Parameters p in-place. These are -// typically applied before code generation begins, and configurable via the -// MutateParams InitOption. -type ParamMutator func(p Parameters) - -// ProtocInput changes the input io.Reader source. This value is where the -// serialized CodeGeneratorRequest is received from protoc. By default, -// os.Stdin is used. -func ProtocInput(r io.Reader) InitOption { return func(g *Generator) { g.in = r } } - -// ProtocOutput changes the output io.Writer destination. This value is where -// the serialized CodeGeneratorResponse is sent to protoc. By default, -// os.Stdout is used. -func ProtocOutput(w io.Writer) InitOption { return func(g *Generator) { g.out = w } } - -// DebugMode enables verbose logging for module development and debugging. -func DebugMode() InitOption { return func(g *Generator) { g.debug = true } } - -// DebugEnv enables verbose logging only if the passed in environment variable -// is non-empty. -func DebugEnv(f string) InitOption { return func(g *Generator) { g.debug = os.Getenv(f) != "" } } - -// MutateParams applies pm to the parameters passed in from protoc. -func MutateParams(pm ...ParamMutator) InitOption { - return func(g *Generator) { g.paramMutators = append(g.paramMutators, pm...) } -} - -// FileSystem overrides the default file system used to write Artifacts to -// disk. By default, the OS's file system is used. This option currently only -// impacts CustomFile and CustomTemplateFile artifacts generated by modules. -func FileSystem(fs afero.Fs) InitOption { return func(g *Generator) { g.persister.SetFS(fs) } } - -// BiDirectional instructs the Generator to build the AST graph in both -// directions (ie, accessing dependents of an entity, not just dependencies). -func BiDirectional() InitOption { - return func(g *Generator) { g.workflow = &onceWorkflow{workflow: &standardWorkflow{BiDi: true}} } -} - -// SupportedFeatures allows defining protoc features to enable / disable. -// See: https://github.com/protocolbuffers/protobuf/blob/v3.17.0/docs/implementing_proto3_presence.md#signaling-that-your-code-generator-supports-proto3-optional -func SupportedFeatures(feat *uint64) InitOption { - return func(g *Generator) { - g.persister.SetSupportedFeatures(feat) - } -} diff --git a/v2/init_option_test.go b/v2/init_option_test.go deleted file mode 100644 index 8d636bb..0000000 --- a/v2/init_option_test.go +++ /dev/null @@ -1,91 +0,0 @@ -package pgs - -import ( - "bytes" - "math/rand" - "os" - "strconv" - "testing" - - "github.com/spf13/afero" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestDebugMode(t *testing.T) { - t.Parallel() - - g := &Generator{} - assert.False(t, g.debug) - - DebugMode()(g) - assert.True(t, g.debug) -} - -func TestDebugEnv(t *testing.T) { - t.Parallel() - - g := &Generator{} - assert.False(t, g.debug) - - e := strconv.Itoa(rand.Int()) - - DebugEnv(e)(g) - assert.False(t, g.debug) - - assert.NoError(t, os.Setenv(e, "1")) - DebugEnv(e)(g) - assert.True(t, g.debug) -} - -func TestFileSystem(t *testing.T) { - t.Parallel() - - p := dummyPersister(InitMockDebugger()) - g := &Generator{persister: p} - - fs := afero.NewMemMapFs() - FileSystem(fs)(g) - - assert.Equal(t, fs, p.fs) -} - -func TestProtocInput(t *testing.T) { - t.Parallel() - - g := &Generator{} - assert.Nil(t, g.in) - - b := &bytes.Buffer{} - ProtocInput(b)(g) - assert.Equal(t, b, g.in) -} - -func TestProtocOutput(t *testing.T) { - t.Parallel() - - g := &Generator{} - assert.Nil(t, g.out) - - b := &bytes.Buffer{} - ProtocOutput(b)(g) - assert.Equal(t, b, g.out) -} - -func TestBiDirectional(t *testing.T) { - t.Parallel() - - g := &Generator{} - assert.Nil(t, g.workflow) - - BiDirectional()(g) - wf := g.workflow - - require.IsType(t, &onceWorkflow{}, wf) - once := wf.(*onceWorkflow) - - require.IsType(t, &standardWorkflow{}, once.workflow) - std := once.workflow.(*standardWorkflow) - - assert.True(t, std.BiDi) -} diff --git a/v2/lang/go/Makefile b/v2/lang/go/Makefile deleted file mode 100644 index 565dc2c..0000000 --- a/v2/lang/go/Makefile +++ /dev/null @@ -1,55 +0,0 @@ -.PHONY: testdata-go-names -testdata-names: ../../bin/protoc-gen-debug # parse the proto file sets in testdata/names and renders binary CodeGeneratorRequest + official go codegen - cd testdata/names && \ - set -e; for subdir in `find . -mindepth 1 -maxdepth 1 -type d`; do \ - cd $$subdir; \ - params=`cat params`; \ - protoc -I . \ - --plugin=protoc-gen-debug=../../../../../bin/protoc-gen-debug \ - --debug_out=".:." \ - --go_out="plugins,paths=source_relative,$$params:." \ - `find . -name "*.proto"`; \ - cd -; \ - done - -testdata-packages: ../../bin/protoc-gen-debug - cd testdata/packages && \ - set -e; for subdir in `find . -mindepth 1 -maxdepth 1 -type d | grep -v targets`; do \ - cd $$subdir; \ - params=`cat params`; \ - protoc -I . -I .. \ - --plugin=protoc-gen-debug=../../../../../bin/protoc-gen-debug \ - --debug_out=".:." \ - --go_out="paths=source_relative,$$params:." \ - `find . -name "*.proto"`; \ - cd -; \ - done - -testdata-outputs: ../../bin/protoc-gen-debug - cd testdata/outputs && \ - set -e; for subdir in `find . -mindepth 1 -maxdepth 1 -type d`; do \ - cd $$subdir; \ - params=`cat params`; \ - protoc -I . -I .. \ - --plugin=protoc-gen-debug=../../../../../bin/protoc-gen-debug \ - --debug_out=".:." \ - --go_out="$$params:." \ - `find . -name "*.proto"`; \ - cd -; \ - done - -testdata-presence: ../../bin/protoc-gen-debug - cd testdata/presence && \ - set -e; for subdir in `find . -mindepth 1 -maxdepth 1 -type d`; do \ - cd $$subdir; \ - params=`cat params`; \ - protoc -I . -I .. \ - --plugin=protoc-gen-debug=../../../../../bin/protoc-gen-debug \ - --debug_out=".:." \ - --go_out="$$params:." \ - `find . -name "*.proto"`; \ - cd -; \ - done - -../../bin/protoc-gen-debug: - cd ../.. && $(MAKE) bin/protoc-gen-debug diff --git a/v2/lang/go/camel.go b/v2/lang/go/camel.go deleted file mode 100644 index d09b81b..0000000 --- a/v2/lang/go/camel.go +++ /dev/null @@ -1,78 +0,0 @@ -package pgsgo - -import ( - pgs "github.com/lyft/protoc-gen-star/v2" -) - -// PGGUpperCamelCase converts Name n to the protoc-gen-go defined upper -// camelcase. The rules are slightly different from pgs.UpperCamelCase in that -// leading underscores are converted to 'X', mid-string underscores followed by -// lowercase letters are removed and the letter is capitalized, all other -// punctuation is preserved. This method should be used when deriving names of -// protoc-gen-go generated code (ie, message/service struct names and field -// names). -// -// See: https://godoc.org/github.com/golang/protobuf/protoc-gen-go/generator#CamelCase -func PGGUpperCamelCase(n pgs.Name) pgs.Name { - return pgs.Name(camelCase(n.String())) -} - -// Below copied from https://github.com/golang/protobuf/blob/d04d7b157bb510b1e0c10132224b616ac0e26b17/protoc-gen-go/generator/generator.go#L2640-L2685, -// to fix deprecation warning: https://github.com/golang/protobuf/blob/b5de78c91d0d09482d65f0a96927631cd343d7bb/protoc-gen-go/generator/generator.go#L42-L47 - -// CamelCase returns the CamelCased name. -// If there is an interior underscore followed by a lower case letter, -// drop the underscore and convert the letter to upper case. -// There is a remote possibility of this rewrite causing a name collision, -// but it's so remote we're prepared to pretend it's nonexistent - since the -// C++ generator lowercases names, it's extremely unlikely to have two fields -// with different capitalizations. -// In short, _my_field_name_2 becomes XMyFieldName_2. -func camelCase(s string) string { - if s == "" { - return "" - } - t := make([]byte, 0, 32) - i := 0 - if s[0] == '_' { - // Need a capital letter; drop the '_'. - t = append(t, 'X') - i++ - } - // Invariant: if the next letter is lower case, it must be converted - // to upper case. - // That is, we process a word at a time, where words are marked by _ or - // upper case letter. Digits are treated as words. - for ; i < len(s); i++ { - c := s[i] - if c == '_' && i+1 < len(s) && isASCIILower(s[i+1]) { - continue // Skip the underscore in s. - } - if isASCIIDigit(c) { - t = append(t, c) - continue - } - // Assume we have a letter now - if not, it's a bogus identifier. - // The next word is a sequence of characters that must start upper case. - if isASCIILower(c) { - c ^= ' ' // Make it a capital letter. - } - t = append(t, c) // Guaranteed not lower case. - // Accept lower case sequence that follows. - for i+1 < len(s) && isASCIILower(s[i+1]) { - i++ - t = append(t, s[i]) - } - } - return string(t) -} - -// Is c an ASCII lower-case letter? -func isASCIILower(c byte) bool { - return 'a' <= c && c <= 'z' -} - -// Is c an ASCII digit? -func isASCIIDigit(c byte) bool { - return '0' <= c && c <= '9' -} diff --git a/v2/lang/go/context.go b/v2/lang/go/context.go deleted file mode 100644 index ac5ac43..0000000 --- a/v2/lang/go/context.go +++ /dev/null @@ -1,73 +0,0 @@ -package pgsgo - -import pgs "github.com/lyft/protoc-gen-star/v2" - -// Context resolves Go-specific language for Packages & Entities generated by -// protoc-gen-go. The rules that drive the naming behavior are complicated, and -// result from an interplay of the go_package file option, the proto package, -// and the proto filename itself. Therefore, it is recommended that all proto -// files that are targeting Go should include a fully qualified go_package -// option. These must be consistent for all proto files that are intended to be -// in the same Go package. -type Context interface { - // Params returns the Parameters associated with this context. - Params() pgs.Parameters - - // Name returns the name of a Node as it would appear in the generation output - // of protoc-gen-go. For each type, the following is returned: - // - // - Package: the Go package name - // - File: the Go package name - // - Message: the struct name - // - Field: the field name on the Message struct - // - OneOf: the field name on the Message struct - // - Enum: the type name - // - EnumValue: the constant name - // - Service: the server interface name - // - Method: the method name on the server and client interface - // - Name(node pgs.Node) pgs.Name - - // ServerName returns the name of the server interface for the Service. - ServerName(service pgs.Service) pgs.Name - - // ClientName returns the name of the client interface for the Service. - ClientName(service pgs.Service) pgs.Name - - // ServerStream returns the name of the grpc.ServerStream wrapper for this - // method. This name is only used if client or server streaming is - // implemented for this method. - ServerStream(method pgs.Method) pgs.Name - - // OneofOption returns the struct name that wraps a OneOf option's value. These - // messages contain one field, matching the value returned by Name for this - // Field. - OneofOption(field pgs.Field) pgs.Name - - // TypeName returns the type name of a Field as it would appear in the - // generated message struct from protoc-gen-go. Fields from imported - // packages will be prefixed with the package name. - Type(field pgs.Field) TypeName - - // PackageName returns the name of the Node's package as it would appear in - // Go source generated by the official protoc-gen-go plugin. - PackageName(node pgs.Node) pgs.Name - - // ImportPath returns the Go import path for an entity as it would be - // included in an import block in a Go file. This value is only appropriate - // for Entities imported into a target file/package. - ImportPath(entity pgs.Entity) pgs.FilePath - - // OutputPath returns the output path relative to the plugin's output destination - OutputPath(entity pgs.Entity) pgs.FilePath -} - -type context struct{ p pgs.Parameters } - -// InitContext configures a Context that should be used for deriving Go names -// for all Packages and Entities. -func InitContext(params pgs.Parameters) Context { - return context{params} -} - -func (c context) Params() pgs.Parameters { return c.p } diff --git a/v2/lang/go/context_test.go b/v2/lang/go/context_test.go deleted file mode 100644 index 7b6df09..0000000 --- a/v2/lang/go/context_test.go +++ /dev/null @@ -1,19 +0,0 @@ -package pgsgo - -import ( - "testing" - - pgs "github.com/lyft/protoc-gen-star/v2" - "github.com/stretchr/testify/assert" -) - -func TestContext_Params(t *testing.T) { - t.Parallel() - - p := pgs.Parameters{} - p.SetStr("foo", "bar") - ctx := InitContext(p) - - params := ctx.Params() - assert.Equal(t, "bar", params.Str("foo")) -} diff --git a/v2/lang/go/docs.go b/v2/lang/go/docs.go deleted file mode 100644 index 84f50ab..0000000 --- a/v2/lang/go/docs.go +++ /dev/null @@ -1,2 +0,0 @@ -// Package pgsgo contains Go-specific helpers for use with PG* based protoc-plugins -package pgsgo diff --git a/v2/lang/go/gofmt.go b/v2/lang/go/gofmt.go deleted file mode 100644 index 34bee58..0000000 --- a/v2/lang/go/gofmt.go +++ /dev/null @@ -1,36 +0,0 @@ -package pgsgo - -import ( - "go/format" - "strings" - - pgs "github.com/lyft/protoc-gen-star/v2" -) - -type goFmt struct{} - -// GoFmt returns a PostProcessor that runs gofmt on any files ending in ".go" -func GoFmt() pgs.PostProcessor { return goFmt{} } - -func (p goFmt) Match(a pgs.Artifact) bool { - var n string - - switch a := a.(type) { - case pgs.GeneratorFile: - n = a.Name - case pgs.GeneratorTemplateFile: - n = a.Name - case pgs.CustomFile: - n = a.Name - case pgs.CustomTemplateFile: - n = a.Name - default: - return false - } - - return strings.HasSuffix(n, ".go") -} - -func (p goFmt) Process(in []byte) ([]byte, error) { return format.Source(in) } - -var _ pgs.PostProcessor = goFmt{} diff --git a/v2/lang/go/gofmt_test.go b/v2/lang/go/gofmt_test.go deleted file mode 100644 index fc814cc..0000000 --- a/v2/lang/go/gofmt_test.go +++ /dev/null @@ -1,54 +0,0 @@ -package pgsgo - -import ( - "testing" - - pgs "github.com/lyft/protoc-gen-star/v2" - - "github.com/stretchr/testify/assert" -) - -func TestGoFmt_Match(t *testing.T) { - t.Parallel() - - pp := GoFmt() - - tests := []struct { - n string - a pgs.Artifact - m bool - }{ - {"GenFile", pgs.GeneratorFile{Name: "foo.go"}, true}, - {"GenFileNonGo", pgs.GeneratorFile{Name: "bar.txt"}, false}, - - {"GenTplFile", pgs.GeneratorTemplateFile{Name: "foo.go"}, true}, - {"GenTplFileNonGo", pgs.GeneratorTemplateFile{Name: "bar.txt"}, false}, - - {"CustomFile", pgs.CustomFile{Name: "foo.go"}, true}, - {"CustomFileNonGo", pgs.CustomFile{Name: "bar.txt"}, false}, - - {"CustomTplFile", pgs.CustomTemplateFile{Name: "foo.go"}, true}, - {"CustomTplFileNonGo", pgs.CustomTemplateFile{Name: "bar.txt"}, false}, - - {"NonMatch", pgs.GeneratorAppend{FileName: "foo.go"}, false}, - } - - for _, test := range tests { - tc := test - t.Run(tc.n, func(t *testing.T) { - t.Parallel() - assert.Equal(t, tc.m, pp.Match(tc.a)) - }) - } -} - -func TestGoFmt_Process(t *testing.T) { - t.Parallel() - - src := []byte("// test\n package foo\n\nvar bar int = 123\n") - exp := []byte("// test\npackage foo\n\nvar bar int = 123\n") - - out, err := GoFmt().Process(src) - assert.NoError(t, err) - assert.Equal(t, exp, out) -} diff --git a/v2/lang/go/helpers_test.go b/v2/lang/go/helpers_test.go deleted file mode 100644 index 70a5229..0000000 --- a/v2/lang/go/helpers_test.go +++ /dev/null @@ -1,45 +0,0 @@ -package pgsgo - -import ( - "io/ioutil" - "path/filepath" - "strings" - "testing" - - "github.com/golang/protobuf/proto" - plugin_go "github.com/golang/protobuf/protoc-gen-go/plugin" - pgs "github.com/lyft/protoc-gen-star/v2" - "github.com/stretchr/testify/require" -) - -func readCodeGenReq(t *testing.T, dir ...string) *plugin_go.CodeGeneratorRequest { - dirs := append(append([]string{"testdata"}, dir...), "code_generator_request.pb.bin") - filename := filepath.Join(dirs...) - - data, err := ioutil.ReadFile(filename) - require.NoError(t, err, "unable to read CDR at %q", filename) - - req := &plugin_go.CodeGeneratorRequest{} - err = proto.Unmarshal(data, req) - require.NoError(t, err, "unable to unmarshal CDR data at %q", filename) - - return req -} - -func buildGraph(t *testing.T, dir ...string) pgs.AST { - d := pgs.InitMockDebugger() - ast := pgs.ProcessCodeGeneratorRequest(d, readCodeGenReq(t, dir...)) - require.False(t, d.Failed(), "failed to build graph (see previous log statements)") - return ast -} - -func loadContext(t *testing.T, dir ...string) Context { - dirs := append(append([]string{"testdata"}, dir...), "params") - filename := filepath.Join(dirs...) - - data, err := ioutil.ReadFile(filename) - require.NoError(t, err, "unable to read params at %q", filename) - - params := pgs.ParseParameters(strings.TrimSpace(string(data))) - return InitContext(params) -} diff --git a/v2/lang/go/name.go b/v2/lang/go/name.go deleted file mode 100644 index 43fdfcd..0000000 --- a/v2/lang/go/name.go +++ /dev/null @@ -1,107 +0,0 @@ -package pgsgo - -import ( - "fmt" - "unicode" - "unicode/utf8" - - pgs "github.com/lyft/protoc-gen-star/v2" -) - -func (c context) Name(node pgs.Node) pgs.Name { - // Message or Enum - type ChildEntity interface { - Name() pgs.Name - Parent() pgs.ParentEntity - } - - switch en := node.(type) { - case pgs.Package: // the package name for the first file (should be consistent) - return c.PackageName(en) - case pgs.File: // the package name for this file - return c.PackageName(en) - case ChildEntity: // Message or Enum types, which may be nested - if p, ok := en.Parent().(pgs.Message); ok { - return joinChild(c.Name(p), en.Name()) - } - return PGGUpperCamelCase(en.Name()) - case pgs.Field: // field names cannot conflict with other generated methods - return replaceProtected(PGGUpperCamelCase(en.Name())) - case pgs.OneOf: // oneof field names cannot conflict with other generated methods - return replaceProtected(PGGUpperCamelCase(en.Name())) - case pgs.EnumValue: // EnumValue are prefixed with the enum name - if _, ok := en.Enum().Parent().(pgs.File); ok { - return joinNames(c.Name(en.Enum()), en.Name()) - } - return joinNames(c.Name(en.Enum().Parent()), en.Name()) - case pgs.Service: // always return the server name - return c.ServerName(en) - case pgs.Entity: // any other entity should be just upper-camel-cased - return PGGUpperCamelCase(en.Name()) - default: - panic("unreachable") - } -} - -func (c context) OneofOption(field pgs.Field) pgs.Name { - n := joinNames(c.Name(field.Message()), c.Name(field)) - - for _, msg := range field.Message().Messages() { - if c.Name(msg) == n { - return n + "_" - } - } - - for _, en := range field.Message().Enums() { - if c.Name(en) == n { - return n + "_" - } - } - - return n -} - -func (c context) ServerName(s pgs.Service) pgs.Name { - n := PGGUpperCamelCase(s.Name()) - return pgs.Name(fmt.Sprintf("%sServer", n)) -} - -func (c context) ClientName(s pgs.Service) pgs.Name { - n := PGGUpperCamelCase(s.Name()) - return pgs.Name(fmt.Sprintf("%sClient", n)) -} - -func (c context) ServerStream(m pgs.Method) pgs.Name { - s := PGGUpperCamelCase(m.Service().Name()) - n := PGGUpperCamelCase(m.Name()) - return joinNames(s, n) + "Server" -} - -var protectedNames = map[pgs.Name]pgs.Name{ - "Reset": "Reset_", - "String": "String_", - "ProtoMessage": "ProtoMessage_", - "Marshal": "Marshal_", - "Unmarshal": "Unmarshal_", - "ExtensionRangeArray": "ExtensionRangeArray_", - "ExtensionMap": "ExtensionMap_", - "Descriptor": "Descriptor_", -} - -func replaceProtected(n pgs.Name) pgs.Name { - if use, protected := protectedNames[n]; protected { - return use - } - return n -} - -func joinChild(a, b pgs.Name) pgs.Name { - if r, _ := utf8.DecodeRuneInString(b.String()); unicode.IsLetter(r) && unicode.IsLower(r) { - return pgs.Name(fmt.Sprintf("%s%s", a, PGGUpperCamelCase(b))) - } - return joinNames(a, PGGUpperCamelCase(b)) -} - -func joinNames(a, b pgs.Name) pgs.Name { - return pgs.Name(fmt.Sprintf("%s_%s", a, b)) -} diff --git a/v2/lang/go/name_test.go b/v2/lang/go/name_test.go deleted file mode 100644 index c7ad547..0000000 --- a/v2/lang/go/name_test.go +++ /dev/null @@ -1,236 +0,0 @@ -package pgsgo - -import ( - "testing" - - pgs "github.com/lyft/protoc-gen-star/v2" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestPGGUpperCamelCase(t *testing.T) { - t.Parallel() - - tests := []struct { - in string - ex string - }{ - {"foo_bar", "FooBar"}, - {"myJSON", "MyJSON"}, - {"PDFTemplate", "PDFTemplate"}, - {"_my_field_name_2", "XMyFieldName_2"}, - {"my.field", "My.field"}, - {"my_Field", "My_Field"}, - } - - for _, tc := range tests { - assert.Equal(t, tc.ex, PGGUpperCamelCase(pgs.Name(tc.in)).String()) - } -} - -func TestName(t *testing.T) { - t.Parallel() - - ast := buildGraph(t, "names", "entities") - ctx := loadContext(t, "names", "entities") - - f := ast.Targets()["entities.proto"] - assert.Equal(t, pgs.Name("entities"), ctx.Name(f)) - assert.Equal(t, pgs.Name("entities"), ctx.Name(f.Package())) - - assert.Panics(t, func() { - ctx.Name(nil) - }) - - tests := []struct { - entity string - expected pgs.Name - }{ - // Top-Level Messages - {"UpperCamelCaseMessage", "UpperCamelCaseMessage"}, - {"lowerCamelCaseMessage", "LowerCamelCaseMessage"}, - {"SCREAMING_SNAKE_CASE", "SCREAMING_SNAKE_CASE"}, - {"Upper_Snake_Case", "Upper_Snake_Case"}, - {"lower_snake_case", "LowerSnakeCase"}, - {"lowercase", "Lowercase"}, - {"UPPERCASE", "UPPERCASE"}, - {"String", "String"}, - {"MsgWith3dInside", "MsgWith3DInside"}, - {"MsgEndsWith3d", "MsgEndsWith3D"}, - - // Nested Messages - {"Nested.Message", "Nested_Message"}, - {"Nested.String", "Nested_String"}, - {"Nested.Message.Message", "Nested_Message_Message"}, - {"Nested.lowerMsg", "NestedLowerMsg"}, - - // Enums - {"UpperCamelCaseEnum", "UpperCamelCaseEnum"}, - {"lowerCamelCaseEnum", "LowerCamelCaseEnum"}, - {"SCREAMING_SNAKE_ENUM", "SCREAMING_SNAKE_ENUM"}, - {"lower_snake_enum", "LowerSnakeEnum"}, - {"Upper_Snake_Enum", "Upper_Snake_Enum"}, - - // EnumValues - {"UpperCamelCaseEnum.SCREAMING_SNAKE_CASE_ENUM_VALUE", "UpperCamelCaseEnum_SCREAMING_SNAKE_CASE_ENUM_VALUE"}, - {"UpperCamelCaseEnum.lower_snake_case_enum_value", "UpperCamelCaseEnum_lower_snake_case_enum_value"}, - {"UpperCamelCaseEnum.Upper_Snake_Case_Enum_Value", "UpperCamelCaseEnum_Upper_Snake_Case_Enum_Value"}, - {"UpperCamelCaseEnum.UpperCamelCaseEnumValue", "UpperCamelCaseEnum_UpperCamelCaseEnumValue"}, - {"UpperCamelCaseEnum.lowerCamelCaseEnumValue", "UpperCamelCaseEnum_lowerCamelCaseEnumValue"}, - {"lowerCamelCaseEnum.LCC_Value", "LowerCamelCaseEnum_LCC_Value"}, - {"SCREAMING_SNAKE_ENUM.SS_Value", "SCREAMING_SNAKE_ENUM_SS_Value"}, - {"lower_snake_enum.LS_Value", "LowerSnakeEnum_LS_Value"}, - {"Upper_Snake_Enum.US_Value", "Upper_Snake_Enum_US_Value"}, - - // Nested Enums - {"Nested.Enum", "Nested_Enum"}, - {"Nested.Enum.VALUE", "Nested_VALUE"}, - {"Nested.Message.Enum", "Nested_Message_Enum"}, - {"Nested.Message.Enum.NESTED", "Nested_Message_NESTED"}, - {"Nested.lowercase", "NestedLowercase"}, - {"Nested.lowercase.lower", "Nested_lower"}, - - // Field Names - {"Fields.lower_snake_case", "LowerSnakeCase"}, - {"Fields.Upper_Snake_Case", "Upper_Snake_Case"}, - {"Fields.SCREAMING_SNAKE_CASE", "SCREAMING_SNAKE_CASE"}, - {"Fields.lowerCamelCase", "LowerCamelCase"}, - {"Fields.UpperCamelCase", "UpperCamelCase"}, - {"Fields.string", "String_"}, - - // OneOfs - {"Oneofs.lower_snake_case", "LowerSnakeCase"}, - {"Oneofs.Upper_Snake_Case", "Upper_Snake_Case"}, - {"Oneofs.SCREAMING_SNAKE_CASE", "SCREAMING_SNAKE_CASE"}, - {"Oneofs.lowerCamelCase", "LowerCamelCase"}, - {"Oneofs.UpperCamelCase", "UpperCamelCase"}, - {"Oneofs.string", "String_"}, - {"Oneofs.oneof", "Oneof"}, - - // Services (always the Server name) - {"UpperCamelService", "UpperCamelServiceServer"}, - {"lowerCamelService", "LowerCamelServiceServer"}, - {"lower_snake_service", "LowerSnakeServiceServer"}, - {"Upper_Snake_Service", "Upper_Snake_ServiceServer"}, - {"SCREAMING_SNAKE_SERVICE", "SCREAMING_SNAKE_SERVICEServer"}, - {"reset", "ResetServer"}, - - // Methods - {"Service.UpperCamel", "UpperCamel"}, - {"Service.lowerCamel", "LowerCamel"}, - {"Service.lower_snake", "LowerSnake"}, - {"Service.Upper_Snake", "Upper_Snake"}, - {"Service.SCREAMING_SNAKE", "SCREAMING_SNAKE"}, - {"Service.Reset", "Reset"}, - } - - for _, test := range tests { - tc := test - t.Run(tc.entity, func(t *testing.T) { - t.Parallel() - - e, ok := ast.Lookup(".names.entities." + tc.entity) - require.True(t, ok, "could not locate entity") - assert.Equal(t, tc.expected, ctx.Name(e)) - }) - } -} - -func TestContext_OneofOption(t *testing.T) { - t.Parallel() - - ast := buildGraph(t, "names", "entities") - ctx := loadContext(t, "names", "entities") - - tests := []struct { - field string - expected pgs.Name - }{ - {"LS", "Oneofs_LS"}, - {"US", "Oneofs_US"}, - {"SS", "Oneofs_SS"}, - {"LC", "Oneofs_LC"}, - {"UC", "Oneofs_UC"}, - {"S", "Oneofs_S"}, - {"lower_snake_case_o", "Oneofs_LowerSnakeCaseO"}, - {"Upper_Snake_Case_O", "Oneofs_Upper_Snake_Case_O"}, - {"SCREAMING_SNAKE_CASE_O", "Oneofs_SCREAMING_SNAKE_CASE_O"}, - {"lowerCamelCaseO", "Oneofs_LowerCamelCaseO"}, - {"UpperCamelCaseO", "Oneofs_UpperCamelCaseO"}, - {"reset", "Oneofs_Reset_"}, - {"some_msg", "Oneofs_SomeMsg_"}, - {"some_enum", "Oneofs_SomeEnum_"}, - } - - for _, test := range tests { - tc := test - t.Run(tc.field, func(t *testing.T) { - t.Parallel() - - e, ok := ast.Lookup(".names.entities.Oneofs." + tc.field) - require.True(t, ok, "could not find field") - f := e.(pgs.Field) - assert.Equal(t, tc.expected, ctx.OneofOption(f)) - }) - } - -} - -func TestContext_ClientName(t *testing.T) { - t.Parallel() - - ast := buildGraph(t, "names", "entities") - ctx := loadContext(t, "names", "entities") - - tests := []struct { - service string - expected pgs.Name - }{ - {"UpperCamelService", "UpperCamelServiceClient"}, - {"lowerCamelService", "LowerCamelServiceClient"}, - {"lower_snake_service", "LowerSnakeServiceClient"}, - {"Upper_Snake_Service", "Upper_Snake_ServiceClient"}, - {"SCREAMING_SNAKE_SERVICE", "SCREAMING_SNAKE_SERVICEClient"}, - {"reset", "ResetClient"}, - } - - for _, test := range tests { - tc := test - t.Run(tc.service, func(t *testing.T) { - t.Parallel() - - e, ok := ast.Lookup(".names.entities." + tc.service) - require.True(t, ok, "could not find service") - s := e.(pgs.Service) - assert.Equal(t, tc.expected, ctx.ClientName(s)) - }) - } -} - -func TestContext_ServerStream(t *testing.T) { - t.Parallel() - - ast := buildGraph(t, "names", "entities") - ctx := loadContext(t, "names", "entities") - - tests := []struct { - method string - expected pgs.Name - }{ - {"UpperCamel", "Service_UpperCamelServer"}, - {"lowerCamel", "Service_LowerCamelServer"}, - {"lower_snake", "Service_LowerSnakeServer"}, - } - - for _, test := range tests { - tc := test - t.Run(tc.method, func(t *testing.T) { - t.Parallel() - - e, ok := ast.Lookup(".names.entities.Service." + tc.method) - require.True(t, ok, "could not find method") - m := e.(pgs.Method) - assert.Equal(t, tc.expected, ctx.ServerStream(m)) - }) - } -} diff --git a/v2/lang/go/package.go b/v2/lang/go/package.go deleted file mode 100644 index 6100271..0000000 --- a/v2/lang/go/package.go +++ /dev/null @@ -1,123 +0,0 @@ -package pgsgo - -import ( - "go/token" - "regexp" - "strings" - "unicode" - "unicode/utf8" - - pgs "github.com/lyft/protoc-gen-star/v2" -) - -var nonAlphaNumPattern = regexp.MustCompile("[^a-zA-Z0-9]") - -func (c context) PackageName(node pgs.Node) pgs.Name { - e, ok := node.(pgs.Entity) - if !ok { - e = node.(pgs.Package).Files()[0] - } - - _, pkg := c.optionPackage(e) - - // use import_path parameter ONLY if there is no go_package option in the file. - if ip := c.p.Str("import_path"); ip != "" && - e.File().Descriptor().GetOptions().GetGoPackage() == "" { - pkg = ip - } - - // if the package name is a Go keyword, prefix with '_' - if token.Lookup(pkg).IsKeyword() { - pkg = "_" + pkg - } - - // if package starts with digit, prefix with `_` - if r, _ := utf8.DecodeRuneInString(pkg); unicode.IsDigit(r) { - pkg = "_" + pkg - } - - // package name is kosher - return pgs.Name(pkg) -} - -func (c context) ImportPath(e pgs.Entity) pgs.FilePath { - path, _ := c.optionPackage(e) - path = c.p.Str("import_prefix") + path - return pgs.FilePath(path) -} - -func (c context) OutputPath(e pgs.Entity) pgs.FilePath { - out := e.File().InputPath().SetExt(".pb.go") - - // source relative doesn't try to be fancy - if Paths(c.p) == SourceRelative { - return out - } - - path, _ := c.optionPackage(e) - - // Import relative ignores the existing file structure - return pgs.FilePath(path).Push(out.Base()) -} - -func (c context) optionPackage(e pgs.Entity) (path, pkg string) { - // M mapping param overrides everything IFF the entity is not a build target - if override, ok := c.p["M"+e.File().InputPath().String()]; ok && !e.BuildTarget() { - path = override - pkg = override - if idx := strings.LastIndex(pkg, "/"); idx > -1 { - pkg = pkg[idx+1:] - } - return - } - - // check if there's a go_package option specified - pkg = c.resolveGoPackageOption(e) - path = e.File().InputPath().Dir().String() - - if pkg == "" { - // have a proto package name, so use that - if n := e.Package().ProtoName(); n != "" { - pkg = n.SnakeCase().String() - } else { // no other info, then replace all non-alphanumerics from the input file name - pkg = nonAlphaNumPattern.ReplaceAllString(e.File().InputPath().BaseName(), "_") - } - return - } - - // go_package="example.com/foo/bar;baz" should have a package name of `baz` - if idx := strings.LastIndex(pkg, ";"); idx > -1 { - path = pkg[:idx] - pkg = nonAlphaNumPattern.ReplaceAllString(pkg[idx+1:], "_") - return - } - - // go_package="example.com/foo/bar" should have a package name of `bar` - if idx := strings.LastIndex(pkg, "/"); idx > -1 { - path = pkg - pkg = nonAlphaNumPattern.ReplaceAllString(pkg[idx+1:], "_") - return - } - - pkg = nonAlphaNumPattern.ReplaceAllString(pkg, "_") - - return -} - -func (c context) resolveGoPackageOption(e pgs.Entity) string { - // attempt to get it from the current file - if pkg := e.File().Descriptor().GetOptions().GetGoPackage(); pkg != "" { - return pkg - } - - // protoc-gen-go will use the go_package option from _any_ file in the same - // execution since it's assumed that all the files are in the same Go - // package. PG* will only verify this against files in the same proto package - for _, f := range e.Package().Files() { - if pkg := f.Descriptor().GetOptions().GetGoPackage(); pkg != "" { - return pkg - } - } - - return "" -} diff --git a/v2/lang/go/package_test.go b/v2/lang/go/package_test.go deleted file mode 100644 index ca7bebb..0000000 --- a/v2/lang/go/package_test.go +++ /dev/null @@ -1,131 +0,0 @@ -package pgsgo - -import ( - "testing" - - pgs "github.com/lyft/protoc-gen-star/v2" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestPackageName(t *testing.T) { - t.Parallel() - - tests := []struct { - dir string - expected pgs.Name - }{ - {"keyword", "_package"}, // go keywords are prefixed with _ - {"package", "my_package"}, // use the go_package option - {"import", "bar"}, // uses the basename if go_package contains a / - {"override", "baz"}, // if go_package contains ;, use everything to the right - {"mapped", "unaffected"}, // M mapped params are ignored for build targets - {"import_path_mapped", "go_package"}, // mixed import_path and M parameters should lose to go_package - {"transitive_package", "foobar"}, // go_option gets picked up from other files if present - {"path_dash", "path_dash"}, // if basename of go_package contains invalid characters, replace with _ - } - - for _, test := range tests { - tc := test - t.Run(tc.dir, func(t *testing.T) { - t.Parallel() - - ast := buildGraph(t, "names", tc.dir) - ctx := loadContext(t, "names", tc.dir) - - for _, target := range ast.Targets() { - assert.Equal(t, tc.expected, ctx.PackageName(target)) - } - }) - } -} - -func TestImportPath(t *testing.T) { - t.Parallel() - - tests := []struct { - dir string - - fully pgs.FilePath - unqualified pgs.FilePath - none pgs.FilePath - }{ - { // no params changing the behavior of the import paths - "no_options", - "example.com/packages/targets/fully_qualified", - "targets/unqualified", - "targets/none", - }, - { // M params provided for each imported package - "mapped", - "example.com/foo/bar", - "example.com/fizz/buzz", - "example.com/quux", - }, - { // import_prefix param prefixes everything...pretty much doesn't work since it also prefixes the proto package - "import_prefix", - "foo.bar/example.com/packages/targets/fully_qualified", - "foo.bar/targets/unqualified", - "foo.bar/fizz/buzz", - }, - } - - for _, test := range tests { - tc := test - t.Run(tc.dir, func(t *testing.T) { - t.Parallel() - - ast := buildGraph(t, "packages", tc.dir) - ctx := loadContext(t, "packages", tc.dir) - - pkgs := map[string]pgs.FilePath{ - "packages.targets.fully_qualified": tc.fully, - "packages.targets.unqualified": tc.unqualified, - "packages.targets.none": tc.none, - } - - for pkg, expected := range pkgs { - t.Run(pkg, func(t *testing.T) { - p, ok := ast.Packages()[pkg] - require.True(t, ok, "package not found") - f := p.Files()[0] - assert.Equal(t, expected, ctx.ImportPath(f)) - }) - } - }) - } -} - -func TestOutputPath(t *testing.T) { - t.Parallel() - - tests := []struct { - dir, file string - expected pgs.FilePath - }{ - {"none", "none.proto", "none.pb.go"}, - {"none_srcrel", "none.proto", "none.pb.go"}, - {"unqualified", "unqualified.proto", "unqualified.pb.go"}, - {"unqualified_srcrel", "unqualified.proto", "unqualified.pb.go"}, - {"qualified", "qualified.proto", "example.com/qualified/qualified.pb.go"}, - {"qualified_srcrel", "qualified.proto", "qualified.pb.go"}, - {"import_prefix", "prefix.proto", "example.com/import_prefix/prefix.pb.go"}, - {"import_prefix_srcrel", "prefix.proto", "prefix.pb.go"}, - {"mapped", "mapped.proto", "mapped.pb.go"}, - {"mapped_srcrel", "mapped.proto", "mapped.pb.go"}, - } - - for _, test := range tests { - tc := test - t.Run(tc.dir, func(t *testing.T) { - t.Parallel() - - ast := buildGraph(t, "outputs", tc.dir) - ctx := loadContext(t, "outputs", tc.dir) - f, ok := ast.Lookup(tc.file) - require.True(t, ok, "file not found") - assert.Equal(t, tc.expected, ctx.OutputPath(f)) - }) - } - -} diff --git a/v2/lang/go/parameters.go b/v2/lang/go/parameters.go deleted file mode 100644 index c4358d5..0000000 --- a/v2/lang/go/parameters.go +++ /dev/null @@ -1,132 +0,0 @@ -package pgsgo - -import ( - "fmt" - "strings" - - pgs "github.com/lyft/protoc-gen-star/v2" -) - -const ( - importPrefixKey = "import_prefix" - importPathKey = "import_path" - importMapKeyPrefix = "M" - pathTypeKey = "paths" - pluginsKey = "plugins" - pluginsSep = "+" -) - -// PathType describes how the generated output file paths should be constructed. -type PathType string - -const ( - // ImportPathRelative is the default and outputs the file based off the go - // import path defined in the go_package option. - ImportPathRelative PathType = "" - - // SourceRelative indicates files should be output relative to the path of - // the source file. - SourceRelative PathType = "source_relative" -) - -// Plugins returns the sub-plugins enabled for this protoc plugin. If the all -// value is true, all registered plugins are considered enabled (ie, protoc was -// called with an empty "plugins" parameter). Otherwise, plugins contains the -// list of plugins enabled by name. -func Plugins(p pgs.Parameters) (plugins []string, all bool) { - s, ok := p[pluginsKey] - if !ok { - return - } - - if all = s == ""; all { - return - } - - plugins = strings.Split(s, pluginsSep) - return -} - -// HasPlugin returns true if the plugin name is enabled in the parameters. This -// method will always return true if all plugins are enabled. -func HasPlugin(p pgs.Parameters, name string) bool { - plugins, all := Plugins(p) - if all { - return true - } - - for _, pl := range plugins { - if pl == name { - return true - } - } - - return false -} - -// AddPlugin adds name to the list of plugins in the parameters. If all plugins -// are enabled, this method is a noop. -func AddPlugin(p pgs.Parameters, name ...string) { - if len(name) == 0 { - return - } - - plugins, all := Plugins(p) - if all { - return - } - - p.SetStr(pluginsKey, strings.Join(append(plugins, name...), pluginsSep)) -} - -// EnableAllPlugins changes the parameters to enable all registered sub-plugins. -func EnableAllPlugins(p pgs.Parameters) { p.SetStr(pluginsKey, "") } - -// ImportPrefix returns the protoc-gen-go parameter. This prefix is added onto -// the beginning of all Go import paths. This is useful for things like -// generating protos in a subdirectory, or regenerating vendored protobufs -// in-place. By default, this method returns an empty string. -// -// See: https://github.com/golang/protobuf#parameters -func ImportPrefix(p pgs.Parameters) string { return p.Str(importPrefixKey) } - -// SetImportPrefix sets the protoc-gen-go ImportPrefix parameter. This is -// useful for overriding the behavior of the ImportPrefix at runtime. -func SetImportPrefix(p pgs.Parameters, prefix string) { p.SetStr(importPrefixKey, prefix) } - -// ImportPath returns the protoc-gen-go parameter. This value is used as the -// package if the input proto files do not declare a go_package option. If it -// contains slashes, everything up to the rightmost slash is ignored. -// -// See: https://github.com/golang/protobuf#parameters -func ImportPath(p pgs.Parameters) string { return p.Str(importPathKey) } - -// SetImportPath sets the protoc-gen-go ImportPath parameter. This is useful -// for overriding the behavior of the ImportPath at runtime. -func SetImportPath(p pgs.Parameters, path string) { p.SetStr(importPathKey, path) } - -// Paths returns the protoc-gen-go parameter. This value is used to switch the -// mode used to determine the output paths of the generated code. By default, -// paths are derived from the import path specified by go_package. It can be -// overridden to be "source_relative", ignoring the import path using the -// source path exclusively. -func Paths(p pgs.Parameters) PathType { return PathType(p.Str(pathTypeKey)) } - -// SetPaths sets the protoc-gen-go Paths parameter. This is useful for -// overriding the behavior of Paths at runtime. -func SetPaths(p pgs.Parameters, pt PathType) { p.SetStr(pathTypeKey, string(pt)) } - -// MappedImport returns the protoc-gen-go import overrides for the specified proto -// file. Each entry in the map keys off a proto file (as loaded by protoc) with -// values of the Go package to use. These values will be prefixed with the -// value of ImportPrefix when generating the Go code. -func MappedImport(p pgs.Parameters, proto string) (string, bool) { - imp, ok := p[fmt.Sprintf("%s%s", importMapKeyPrefix, proto)] - return imp, ok -} - -// AddImportMapping adds a proto file to Go package import mapping to the -// parameters. -func AddImportMapping(p pgs.Parameters, proto, pkg string) { - p[fmt.Sprintf("%s%s", importMapKeyPrefix, proto)] = pkg -} diff --git a/v2/lang/go/parameters_test.go b/v2/lang/go/parameters_test.go deleted file mode 100644 index 0bc19d0..0000000 --- a/v2/lang/go/parameters_test.go +++ /dev/null @@ -1,134 +0,0 @@ -package pgsgo - -import ( - "testing" - - pgs "github.com/lyft/protoc-gen-star/v2" - "github.com/stretchr/testify/assert" -) - -func TestParameters_Plugins(t *testing.T) { - t.Parallel() - - p := pgs.Parameters{} - plugins, all := Plugins(p) - assert.Empty(t, plugins) - assert.False(t, all) - - p[pluginsKey] = "foo+bar" - plugins, all = Plugins(p) - assert.Equal(t, []string{"foo", "bar"}, plugins) - assert.False(t, all) - - p[pluginsKey] = "" - plugins, all = Plugins(p) - assert.Empty(t, plugins) - assert.True(t, all) -} - -func TestParameters_HasPlugin(t *testing.T) { - t.Parallel() - - p := pgs.Parameters{} - assert.False(t, HasPlugin(p, "foo")) - - p[pluginsKey] = "foo" - assert.True(t, HasPlugin(p, "foo")) - - p[pluginsKey] = "" - assert.True(t, HasPlugin(p, "foo")) - - p[pluginsKey] = "bar" - assert.False(t, HasPlugin(p, "foo")) -} - -func TestParameters_AddPlugin(t *testing.T) { - t.Parallel() - - p := pgs.Parameters{} - AddPlugin(p, "foo", "bar") - assert.Equal(t, "foo+bar", p[pluginsKey]) - - AddPlugin(p, "baz") - assert.Equal(t, "foo+bar+baz", p[pluginsKey]) - - AddPlugin(p) - assert.Equal(t, "foo+bar+baz", p[pluginsKey]) - - p[pluginsKey] = "" - AddPlugin(p, "fizz", "buzz") - assert.Equal(t, "", p[pluginsKey]) -} - -func TestParameters_EnableAllPlugins(t *testing.T) { - t.Parallel() - - p := pgs.Parameters{pluginsKey: "foo"} - _, all := Plugins(p) - assert.False(t, all) - - EnableAllPlugins(p) - _, all = Plugins(p) - assert.True(t, all) -} - -func TestParameters_ImportPrefix(t *testing.T) { - t.Parallel() - - p := pgs.Parameters{} - assert.Empty(t, ImportPrefix(p)) - SetImportPrefix(p, "foo") - assert.Equal(t, "foo", ImportPrefix(p)) -} - -func TestParameters_ImportPath(t *testing.T) { - t.Parallel() - - p := pgs.Parameters{} - assert.Empty(t, ImportPath(p)) - SetImportPath(p, "foo") - assert.Equal(t, "foo", ImportPath(p)) -} - -func TestParameters_ImportMap(t *testing.T) { - t.Parallel() - - p := pgs.Parameters{ - "Mfoo.proto": "bar", - "Mfizz/buzz.proto": "baz", - } - - AddImportMapping(p, "quux.proto", "shme") - - tests := []struct { - proto, path string - exists bool - }{ - {"quux.proto", "shme", true}, - {"foo.proto", "bar", true}, - {"fizz/buzz.proto", "baz", true}, - {"abcde.proto", "", false}, - } - - for _, test := range tests { - t.Run(test.proto, func(t *testing.T) { - path, ok := MappedImport(p, test.proto) - if test.exists { - assert.True(t, ok) - assert.Equal(t, test.path, path) - } else { - assert.False(t, ok) - } - }) - } -} - -func TestParameters_Paths(t *testing.T) { - t.Parallel() - - p := pgs.Parameters{} - - assert.Equal(t, ImportPathRelative, Paths(p)) - SetPaths(p, SourceRelative) - assert.Equal(t, SourceRelative, Paths(p)) -} diff --git a/v2/lang/go/testdata/names/entities/entities.proto b/v2/lang/go/testdata/names/entities/entities.proto deleted file mode 100644 index 8abee2c..0000000 --- a/v2/lang/go/testdata/names/entities/entities.proto +++ /dev/null @@ -1,118 +0,0 @@ -syntax = "proto3"; - -package names.entities; -option go_package = "example.com/names/entities"; - -message UpperCamelCaseMessage {} - -message lowerCamelCaseMessage {} - -message SCREAMING_SNAKE_CASE {} - -message Upper_Snake_Case {} - -message lower_snake_case {} - -message lowercase {} - -message UPPERCASE {} - -message String {} // protected name - -message Nested { - message Message { - message Message {} - - enum Enum { NESTED = 0; } - } - - message String {} // protected name - - enum Enum { VALUE = 0; } - - enum lowercase { lower = 0; } - - message lowerMsg {} -} - -enum UpperCamelCaseEnum { - SCREAMING_SNAKE_CASE_ENUM_VALUE = 0; - lower_snake_case_enum_value = 1; - Upper_Snake_Case_Enum_Value = 2; - UpperCamelCaseEnumValue = 3; - lowerCamelCaseEnumValue = 4; -} - -enum lowerCamelCaseEnum {LCC_Value = 0;} - -enum SCREAMING_SNAKE_ENUM {SS_Value = 0;} - -enum lower_snake_enum {LS_Value = 0;} - -enum Upper_Snake_Enum {US_Value = 0;} - -message Fields { - bool lower_snake_case = 1; - bool Upper_Snake_Case = 2; - bool SCREAMING_SNAKE_CASE = 3; - bool lowerCamelCase = 4; - bool UpperCamelCase = 5; - bool string = 6; // protected name -} - -message Oneofs { - oneof lower_snake_case {bool LS = 1;} - - oneof Upper_Snake_Case {bool US = 2;} - - oneof SCREAMING_SNAKE_CASE {bool SS = 3;} - - oneof lowerCamelCase {bool LC = 4;} - - oneof UpperCamelCase {bool UC = 5;} - - // protected - oneof string {bool S = 6;} - - oneof oneof { - bool lower_snake_case_o = 7; - bool Upper_Snake_Case_O = 8; - bool SCREAMING_SNAKE_CASE_O = 9; - bool lowerCamelCaseO = 10; - bool UpperCamelCaseO = 11; - bool reset = 12; // protected - } - - message SomeMsg {} - enum SomeEnum { VALUE = 0; } - oneof some_msg_oneof { - SomeMsg some_msg = 13; - SomeEnum some_enum = 14; - } -} - -service UpperCamelService {} - -service lowerCamelService {} - -service lower_snake_service {} - -service Upper_Snake_Service {} - -service SCREAMING_SNAKE_SERVICE {} - -service reset {} - -service Service { - rpc UpperCamel(stream String) returns (String); - rpc lowerCamel(String) returns (stream String); - rpc lower_snake(stream String) returns (stream String); - rpc Upper_Snake(String) returns (String); - rpc SCREAMING_SNAKE(String) returns (String); - rpc Reset(String) returns (String); -} - -message MsgWith3dInside {} - -message MsgEndsWith3d {} - diff --git a/v2/lang/go/testdata/names/entities/params b/v2/lang/go/testdata/names/entities/params deleted file mode 100644 index e69de29..0000000 diff --git a/v2/lang/go/testdata/names/import/import.proto b/v2/lang/go/testdata/names/import/import.proto deleted file mode 100644 index 00d77a7..0000000 --- a/v2/lang/go/testdata/names/import/import.proto +++ /dev/null @@ -1,4 +0,0 @@ -syntax="proto3"; - -package names.import; -option go_package = "example.com/foo/bar"; diff --git a/v2/lang/go/testdata/names/import/params b/v2/lang/go/testdata/names/import/params deleted file mode 100644 index e69de29..0000000 diff --git a/v2/lang/go/testdata/names/import_path_mapped/import_path_mapped.proto b/v2/lang/go/testdata/names/import_path_mapped/import_path_mapped.proto deleted file mode 100644 index 7632293..0000000 --- a/v2/lang/go/testdata/names/import_path_mapped/import_path_mapped.proto +++ /dev/null @@ -1,5 +0,0 @@ -syntax="proto3"; -package names.import_path_mapped; -option go_package="github.com/fizz/buzz;go_package"; - -message Mapped {} diff --git a/v2/lang/go/testdata/names/import_path_mapped/params b/v2/lang/go/testdata/names/import_path_mapped/params deleted file mode 100644 index bb1e6eb..0000000 --- a/v2/lang/go/testdata/names/import_path_mapped/params +++ /dev/null @@ -1 +0,0 @@ -Mimport_path_mapped.proto=github.com/foo/bar diff --git a/v2/lang/go/testdata/names/keyword/keyword.proto b/v2/lang/go/testdata/names/keyword/keyword.proto deleted file mode 100644 index 34c6923..0000000 --- a/v2/lang/go/testdata/names/keyword/keyword.proto +++ /dev/null @@ -1,6 +0,0 @@ -syntax="proto3"; - -package names.keyword; -option go_package = "example.com/package"; - -message Package {} diff --git a/v2/lang/go/testdata/names/keyword/params b/v2/lang/go/testdata/names/keyword/params deleted file mode 100644 index e69de29..0000000 diff --git a/v2/lang/go/testdata/names/mapped/mapped.proto b/v2/lang/go/testdata/names/mapped/mapped.proto deleted file mode 100644 index 1735985..0000000 --- a/v2/lang/go/testdata/names/mapped/mapped.proto +++ /dev/null @@ -1,5 +0,0 @@ -syntax="proto3"; -package names.mapped; -option go_package="./;unaffected"; - -message Mapped {} diff --git a/v2/lang/go/testdata/names/mapped/params b/v2/lang/go/testdata/names/mapped/params deleted file mode 100644 index f2c6140..0000000 --- a/v2/lang/go/testdata/names/mapped/params +++ /dev/null @@ -1 +0,0 @@ -Mmapped.proto=example.com/foobar diff --git a/v2/lang/go/testdata/names/override/override.proto b/v2/lang/go/testdata/names/override/override.proto deleted file mode 100644 index 86a0289..0000000 --- a/v2/lang/go/testdata/names/override/override.proto +++ /dev/null @@ -1,4 +0,0 @@ -syntax="proto3"; - -package names.override; -option go_package = "example.com/foo/bar;baz"; diff --git a/v2/lang/go/testdata/names/override/params b/v2/lang/go/testdata/names/override/params deleted file mode 100644 index e69de29..0000000 diff --git a/v2/lang/go/testdata/names/package/package.proto b/v2/lang/go/testdata/names/package/package.proto deleted file mode 100644 index 6d0c16e..0000000 --- a/v2/lang/go/testdata/names/package/package.proto +++ /dev/null @@ -1,6 +0,0 @@ -syntax="proto3"; - -package names.package; -option go_package = "example.com/names/package;my_package"; - -message Package {} diff --git a/v2/lang/go/testdata/names/package/params b/v2/lang/go/testdata/names/package/params deleted file mode 100644 index e69de29..0000000 diff --git a/v2/lang/go/testdata/names/path_dash/params b/v2/lang/go/testdata/names/path_dash/params deleted file mode 100644 index e69de29..0000000 diff --git a/v2/lang/go/testdata/names/path_dash/path_dash.proto b/v2/lang/go/testdata/names/path_dash/path_dash.proto deleted file mode 100644 index e7cd848..0000000 --- a/v2/lang/go/testdata/names/path_dash/path_dash.proto +++ /dev/null @@ -1,5 +0,0 @@ -syntax="proto3"; -package names.path_dash; -option go_package="example.com/path-dash"; - -message PathDash {} \ No newline at end of file diff --git a/v2/lang/go/testdata/names/transitive_package/other.proto b/v2/lang/go/testdata/names/transitive_package/other.proto deleted file mode 100644 index dab27be..0000000 --- a/v2/lang/go/testdata/names/transitive_package/other.proto +++ /dev/null @@ -1,8 +0,0 @@ -syntax="proto3"; -package names.transitive_package; - -option go_package="example.com/names/foobar"; - -message Other { - bool value = 1; -} diff --git a/v2/lang/go/testdata/names/transitive_package/params b/v2/lang/go/testdata/names/transitive_package/params deleted file mode 100644 index 2c1d3b7..0000000 --- a/v2/lang/go/testdata/names/transitive_package/params +++ /dev/null @@ -1 +0,0 @@ -Mmapped.proto=example.com/foo/bar,Mmapped_no_options/mapped.proto=example.com/fizz/buzz,Mnames/mapped_no_options/mapped.proto=example.com/quux/baz diff --git a/v2/lang/go/testdata/names/transitive_package/transitive.proto b/v2/lang/go/testdata/names/transitive_package/transitive.proto deleted file mode 100644 index d83f2bd..0000000 --- a/v2/lang/go/testdata/names/transitive_package/transitive.proto +++ /dev/null @@ -1,8 +0,0 @@ -syntax="proto3"; -package names.transitive_package; - -option go_package="example.com/names/foobar"; - -message Transitive { - bool value = 1; -} diff --git a/v2/lang/go/testdata/names/types/params b/v2/lang/go/testdata/names/types/params deleted file mode 100644 index e69de29..0000000 diff --git a/v2/lang/go/testdata/names/types/proto2.proto b/v2/lang/go/testdata/names/types/proto2.proto deleted file mode 100644 index cae3f31..0000000 --- a/v2/lang/go/testdata/names/types/proto2.proto +++ /dev/null @@ -1,65 +0,0 @@ -syntax="proto2"; -package names.types; -option go_package = "example.com/foo/bar"; - -import "google/protobuf/duration.proto"; -import "google/protobuf/type.proto"; - -message Proto2 { - optional double double = 1; - optional float float = 2; - optional int64 int64 = 3; - optional sfixed64 sfixed64 = 4; - optional sint64 sint64 = 5; - optional uint64 uint64 = 6; - optional fixed64 fixed64 = 7; - optional int32 int32 = 8; - optional sfixed32 sfixed32 = 9; - optional sint32 sint32 = 10; - optional uint32 uint32 = 11; - optional fixed32 fixed32 = 12; - optional bool bool = 13; - optional string string = 14; - optional bytes bytes = 15; - - optional Enum enum = 16; - optional google.protobuf.Syntax ext_enum = 17; - optional Required msg = 18; - optional google.protobuf.Duration ext_msg = 19; - - repeated double repeated_scalar = 20; - repeated Enum repeated_enum = 21; - repeated google.protobuf.Syntax repeated_ext_enum = 22; - repeated Required repeated_msg = 23; - repeated google.protobuf.Duration repeated_ext_msg = 24; - - map map_scalar = 25; - map map_enum = 26; - map map_ext_enum = 27; - map map_msg = 28; - map map_ext_msg = 29; - - enum Enum {VALUE = 0;} - - message Required { - required double double = 1; - required float float = 2; - required int64 int64 = 3; - required sfixed64 sfixed64 = 4; - required sint64 sint64 = 5; - required uint64 uint64 = 6; - required fixed64 fixed64 = 7; - required int32 int32 = 8; - required sfixed32 sfixed32 = 9; - required sint32 sint32 = 10; - required uint32 uint32 = 11; - required fixed32 fixed32 = 12; - required bool bool = 13; - required string string = 14; - required bytes bytes = 15; - required Enum enum = 16; - required google.protobuf.Syntax ext_enum = 17; - required Required msg = 18; - required google.protobuf.Duration ext_msg = 19; - } -} diff --git a/v2/lang/go/testdata/names/types/proto3.proto b/v2/lang/go/testdata/names/types/proto3.proto deleted file mode 100644 index a797dd9..0000000 --- a/v2/lang/go/testdata/names/types/proto3.proto +++ /dev/null @@ -1,45 +0,0 @@ -syntax="proto3"; -package names.types; -option go_package = "example.com/foo/bar"; - -import "google/protobuf/duration.proto"; -import "google/protobuf/type.proto"; - -message Proto3 { - double double = 1; - float float = 2; - int64 int64 = 3; - sfixed64 sfixed64 = 4; - sint64 sint64 = 5; - uint64 uint64 = 6; - fixed64 fixed64 = 7; - int32 int32 = 8; - sfixed32 sfixed32 = 9; - sint32 sint32 = 10; - uint32 uint32 = 11; - fixed32 fixed32 = 12; - bool bool = 13; - string string = 14; - bytes bytes = 15; - - Enum enum = 16; - google.protobuf.Syntax ext_enum = 17; - Message msg = 18; - google.protobuf.Duration ext_msg = 19; - - repeated double repeated_scalar = 20; - repeated Enum repeated_enum = 21; - repeated google.protobuf.Syntax repeated_ext_enum = 22; - repeated Message repeated_msg = 23; - repeated google.protobuf.Duration repeated_ext_msg = 24; - - map map_scalar = 25; - map map_enum = 26; - map map_ext_enum = 27; - map map_msg = 28; - map map_ext_msg = 29; - - enum Enum {VALUE = 0;} - - message Message {} -} diff --git a/v2/lang/go/testdata/outputs/import_prefix/params b/v2/lang/go/testdata/outputs/import_prefix/params deleted file mode 100644 index 187385e..0000000 --- a/v2/lang/go/testdata/outputs/import_prefix/params +++ /dev/null @@ -1 +0,0 @@ -import_prefix=foo/ diff --git a/v2/lang/go/testdata/outputs/import_prefix/prefix.proto b/v2/lang/go/testdata/outputs/import_prefix/prefix.proto deleted file mode 100644 index eeca6bd..0000000 --- a/v2/lang/go/testdata/outputs/import_prefix/prefix.proto +++ /dev/null @@ -1,4 +0,0 @@ -syntax="proto3"; -package outputs.import_prefix; -option go_package = "example.com/import_prefix"; -message ImportPrefix {} diff --git a/v2/lang/go/testdata/outputs/import_prefix_srcrel/params b/v2/lang/go/testdata/outputs/import_prefix_srcrel/params deleted file mode 100644 index b2b1469..0000000 --- a/v2/lang/go/testdata/outputs/import_prefix_srcrel/params +++ /dev/null @@ -1,2 +0,0 @@ -paths=source_relative,import_prefix=foo/ - diff --git a/v2/lang/go/testdata/outputs/import_prefix_srcrel/prefix.proto b/v2/lang/go/testdata/outputs/import_prefix_srcrel/prefix.proto deleted file mode 100644 index eeca6bd..0000000 --- a/v2/lang/go/testdata/outputs/import_prefix_srcrel/prefix.proto +++ /dev/null @@ -1,4 +0,0 @@ -syntax="proto3"; -package outputs.import_prefix; -option go_package = "example.com/import_prefix"; -message ImportPrefix {} diff --git a/v2/lang/go/testdata/outputs/mapped/mapped.proto b/v2/lang/go/testdata/outputs/mapped/mapped.proto deleted file mode 100644 index 5aadb78..0000000 --- a/v2/lang/go/testdata/outputs/mapped/mapped.proto +++ /dev/null @@ -1,5 +0,0 @@ -syntax="proto3"; -package outputs.mapped; -option go_package="./;mapped"; - -message Mapped {} diff --git a/v2/lang/go/testdata/outputs/mapped/params b/v2/lang/go/testdata/outputs/mapped/params deleted file mode 100644 index f2c6140..0000000 --- a/v2/lang/go/testdata/outputs/mapped/params +++ /dev/null @@ -1 +0,0 @@ -Mmapped.proto=example.com/foobar diff --git a/v2/lang/go/testdata/outputs/mapped_srcrel/mapped.proto b/v2/lang/go/testdata/outputs/mapped_srcrel/mapped.proto deleted file mode 100644 index 5aadb78..0000000 --- a/v2/lang/go/testdata/outputs/mapped_srcrel/mapped.proto +++ /dev/null @@ -1,5 +0,0 @@ -syntax="proto3"; -package outputs.mapped; -option go_package="./;mapped"; - -message Mapped {} diff --git a/v2/lang/go/testdata/outputs/mapped_srcrel/params b/v2/lang/go/testdata/outputs/mapped_srcrel/params deleted file mode 100644 index 5b9b814..0000000 --- a/v2/lang/go/testdata/outputs/mapped_srcrel/params +++ /dev/null @@ -1 +0,0 @@ -paths=source_relative,Mmapped.proto=example.com/foobar diff --git a/v2/lang/go/testdata/outputs/none/none.proto b/v2/lang/go/testdata/outputs/none/none.proto deleted file mode 100644 index e63c8aa..0000000 --- a/v2/lang/go/testdata/outputs/none/none.proto +++ /dev/null @@ -1,4 +0,0 @@ -syntax="proto3"; -package outputs.none; -option go_package="./;none"; -message None {} diff --git a/v2/lang/go/testdata/outputs/none/params b/v2/lang/go/testdata/outputs/none/params deleted file mode 100644 index e69de29..0000000 diff --git a/v2/lang/go/testdata/outputs/none_srcrel/none.proto b/v2/lang/go/testdata/outputs/none_srcrel/none.proto deleted file mode 100644 index 6e4f296..0000000 --- a/v2/lang/go/testdata/outputs/none_srcrel/none.proto +++ /dev/null @@ -1,5 +0,0 @@ -syntax="proto3"; - -option go_package="example.com/outputs/none"; - -message None {} diff --git a/v2/lang/go/testdata/outputs/none_srcrel/params b/v2/lang/go/testdata/outputs/none_srcrel/params deleted file mode 100644 index 3d387d0..0000000 --- a/v2/lang/go/testdata/outputs/none_srcrel/params +++ /dev/null @@ -1 +0,0 @@ -paths=source_relative diff --git a/v2/lang/go/testdata/outputs/qualified/params b/v2/lang/go/testdata/outputs/qualified/params deleted file mode 100644 index e69de29..0000000 diff --git a/v2/lang/go/testdata/outputs/qualified/qualified.proto b/v2/lang/go/testdata/outputs/qualified/qualified.proto deleted file mode 100644 index e821f85..0000000 --- a/v2/lang/go/testdata/outputs/qualified/qualified.proto +++ /dev/null @@ -1,5 +0,0 @@ -syntax="proto3"; -package outputs.qualified; -option go_package="example.com/qualified"; - -message FullyQualified{} diff --git a/v2/lang/go/testdata/outputs/qualified_srcrel/params b/v2/lang/go/testdata/outputs/qualified_srcrel/params deleted file mode 100644 index 3d387d0..0000000 --- a/v2/lang/go/testdata/outputs/qualified_srcrel/params +++ /dev/null @@ -1 +0,0 @@ -paths=source_relative diff --git a/v2/lang/go/testdata/outputs/qualified_srcrel/qualified.proto b/v2/lang/go/testdata/outputs/qualified_srcrel/qualified.proto deleted file mode 100644 index e821f85..0000000 --- a/v2/lang/go/testdata/outputs/qualified_srcrel/qualified.proto +++ /dev/null @@ -1,5 +0,0 @@ -syntax="proto3"; -package outputs.qualified; -option go_package="example.com/qualified"; - -message FullyQualified{} diff --git a/v2/lang/go/testdata/outputs/unqualified/params b/v2/lang/go/testdata/outputs/unqualified/params deleted file mode 100644 index e69de29..0000000 diff --git a/v2/lang/go/testdata/outputs/unqualified/unqualified.proto b/v2/lang/go/testdata/outputs/unqualified/unqualified.proto deleted file mode 100644 index 16b2cd4..0000000 --- a/v2/lang/go/testdata/outputs/unqualified/unqualified.proto +++ /dev/null @@ -1,5 +0,0 @@ -syntax="proto3"; -package outputs.unqualified; -option go_package="./;unqualified"; - -message Unqualified{} diff --git a/v2/lang/go/testdata/outputs/unqualified_srcrel/params b/v2/lang/go/testdata/outputs/unqualified_srcrel/params deleted file mode 100644 index 3d387d0..0000000 --- a/v2/lang/go/testdata/outputs/unqualified_srcrel/params +++ /dev/null @@ -1 +0,0 @@ -paths=source_relative diff --git a/v2/lang/go/testdata/outputs/unqualified_srcrel/unqualified.proto b/v2/lang/go/testdata/outputs/unqualified_srcrel/unqualified.proto deleted file mode 100644 index 2cbaa0a..0000000 --- a/v2/lang/go/testdata/outputs/unqualified_srcrel/unqualified.proto +++ /dev/null @@ -1,5 +0,0 @@ -syntax="proto3"; -package outputs.unqualified; -option go_package="example.com/outputs/unqualified"; - -message Unqualified{} diff --git a/v2/lang/go/testdata/packages/import_prefix/import_prefix.proto b/v2/lang/go/testdata/packages/import_prefix/import_prefix.proto deleted file mode 100644 index b6fc199..0000000 --- a/v2/lang/go/testdata/packages/import_prefix/import_prefix.proto +++ /dev/null @@ -1,13 +0,0 @@ -syntax="proto3"; -package packages.import_prefix; -option go_package="example.com/packages/import_prefix"; - -import "targets/fully_qualified/fully_qualified.proto"; -import "targets/unqualified/unqualified.proto"; -import "targets/none/none.proto"; - -message ImportPrefixed { - targets.fully_qualified.FullyQualified fully = 1; - targets.unqualified.Unqualified unqualified = 2; - targets.none.None none = 3; -} diff --git a/v2/lang/go/testdata/packages/import_prefix/params b/v2/lang/go/testdata/packages/import_prefix/params deleted file mode 100644 index e25d003..0000000 --- a/v2/lang/go/testdata/packages/import_prefix/params +++ /dev/null @@ -1 +0,0 @@ -Mtargets/none/none.proto=fizz/buzz,import_prefix=foo.bar/ diff --git a/v2/lang/go/testdata/packages/mapped/mapped.proto b/v2/lang/go/testdata/packages/mapped/mapped.proto deleted file mode 100644 index 4c3b77d..0000000 --- a/v2/lang/go/testdata/packages/mapped/mapped.proto +++ /dev/null @@ -1,13 +0,0 @@ -syntax="proto3"; -package packages.mapped; -option go_package="./;mapped"; - -import "targets/fully_qualified/fully_qualified.proto"; -import "targets/unqualified/unqualified.proto"; -import "targets/none/none.proto"; - -message Mapped { - targets.fully_qualified.FullyQualified fully = 1; - targets.unqualified.Unqualified unqualified = 2; - targets.none.None none = 3; -} diff --git a/v2/lang/go/testdata/packages/mapped/params b/v2/lang/go/testdata/packages/mapped/params deleted file mode 100644 index 63595e8..0000000 --- a/v2/lang/go/testdata/packages/mapped/params +++ /dev/null @@ -1 +0,0 @@ -Mtargets/fully_qualified/fully_qualified.proto=example.com/foo/bar,Mtargets/unqualified/unqualified.proto=example.com/fizz/buzz,Mtargets/none/none.proto=example.com/quux diff --git a/v2/lang/go/testdata/packages/no_options/no_options.proto b/v2/lang/go/testdata/packages/no_options/no_options.proto deleted file mode 100644 index 6df4ab5..0000000 --- a/v2/lang/go/testdata/packages/no_options/no_options.proto +++ /dev/null @@ -1,13 +0,0 @@ -syntax="proto3"; -package packages.no_options; -option go_package="example.com/packages/no_options"; - -import "targets/fully_qualified/fully_qualified.proto"; -import "targets/unqualified/unqualified.proto"; -import "targets/none/none.proto"; - -message NoOptions { - targets.fully_qualified.FullyQualified fully = 1; - targets.unqualified.Unqualified unqualified = 2; - targets.none.None none = 3; -} diff --git a/v2/lang/go/testdata/packages/no_options/params b/v2/lang/go/testdata/packages/no_options/params deleted file mode 100644 index e69de29..0000000 diff --git a/v2/lang/go/testdata/packages/targets/fully_qualified/fully_qualified.proto b/v2/lang/go/testdata/packages/targets/fully_qualified/fully_qualified.proto deleted file mode 100644 index 154694d..0000000 --- a/v2/lang/go/testdata/packages/targets/fully_qualified/fully_qualified.proto +++ /dev/null @@ -1,5 +0,0 @@ -syntax="proto3"; -package packages.targets.fully_qualified; -option go_package="example.com/packages/targets/fully_qualified"; - -message FullyQualified{} diff --git a/v2/lang/go/testdata/packages/targets/none/none.proto b/v2/lang/go/testdata/packages/targets/none/none.proto deleted file mode 100644 index e35851c..0000000 --- a/v2/lang/go/testdata/packages/targets/none/none.proto +++ /dev/null @@ -1,5 +0,0 @@ -syntax="proto3"; -package packages.targets.none; -option go_package="targets/none"; - -message None{} diff --git a/v2/lang/go/testdata/packages/targets/unqualified/unqualified.proto b/v2/lang/go/testdata/packages/targets/unqualified/unqualified.proto deleted file mode 100644 index 821447a..0000000 --- a/v2/lang/go/testdata/packages/targets/unqualified/unqualified.proto +++ /dev/null @@ -1,5 +0,0 @@ -syntax="proto3"; -package packages.targets.unqualified; -option go_package="targets/unqualified"; - -message Unqualified{} diff --git a/v2/lang/go/testdata/presence/types/params b/v2/lang/go/testdata/presence/types/params deleted file mode 100644 index e69de29..0000000 diff --git a/v2/lang/go/testdata/presence/types/proto3.proto b/v2/lang/go/testdata/presence/types/proto3.proto deleted file mode 100644 index 2a565f5..0000000 --- a/v2/lang/go/testdata/presence/types/proto3.proto +++ /dev/null @@ -1,67 +0,0 @@ -syntax="proto3"; -package names.types; -option go_package = "example.com/foo/bar"; - -import "google/protobuf/duration.proto"; -import "google/protobuf/type.proto"; - -message Proto3 { - double double = 1; - float float = 2; - int64 int64 = 3; - sfixed64 sfixed64 = 4; - sint64 sint64 = 5; - uint64 uint64 = 6; - fixed64 fixed64 = 7; - int32 int32 = 8; - sfixed32 sfixed32 = 9; - sint32 sint32 = 10; - uint32 uint32 = 11; - fixed32 fixed32 = 12; - bool bool = 13; - string string = 14; - bytes bytes = 15; - - Enum enum = 16; - google.protobuf.Syntax ext_enum = 17; - Message msg = 18; - google.protobuf.Duration ext_msg = 19; - - repeated double repeated_scalar = 20; - repeated Enum repeated_enum = 21; - repeated google.protobuf.Syntax repeated_ext_enum = 22; - repeated Message repeated_msg = 23; - repeated google.protobuf.Duration repeated_ext_msg = 24; - - map map_scalar = 25; - map map_enum = 26; - map map_ext_enum = 27; - map map_msg = 28; - map map_ext_msg = 29; - - enum Enum {VALUE = 0;} - - message Message {} - - message Optional { - optional double double = 1; - optional float float = 2; - optional int64 int64 = 3; - optional sfixed64 sfixed64 = 4; - optional sint64 sint64 = 5; - optional uint64 uint64 = 6; - optional fixed64 fixed64 = 7; - optional int32 int32 = 8; - optional sfixed32 sfixed32 = 9; - optional sint32 sint32 = 10; - optional uint32 uint32 = 11; - optional fixed32 fixed32 = 12; - optional bool bool = 13; - optional string string = 14; - optional bytes bytes = 15; - optional Enum enum = 16; - optional google.protobuf.Syntax ext_enum = 17; - optional Optional msg = 18; - optional google.protobuf.Duration ext_msg = 19; - } -} diff --git a/v2/lang/go/type_name.go b/v2/lang/go/type_name.go deleted file mode 100644 index 2e650f9..0000000 --- a/v2/lang/go/type_name.go +++ /dev/null @@ -1,134 +0,0 @@ -package pgsgo - -import ( - "fmt" - "strings" - - pgs "github.com/lyft/protoc-gen-star/v2" -) - -func (c context) Type(f pgs.Field) TypeName { - ft := f.Type() - - var t TypeName - switch { - case ft.IsMap(): - key := scalarType(ft.Key().ProtoType()) - return TypeName(fmt.Sprintf("map[%s]%s", key, c.elType(ft))) - case ft.IsRepeated(): - return TypeName(fmt.Sprintf("[]%s", c.elType(ft))) - case ft.IsEmbed(): - return c.importableTypeName(f, ft.Embed()).Pointer() - case ft.IsEnum(): - t = c.importableTypeName(f, ft.Enum()) - default: - t = scalarType(ft.ProtoType()) - } - - if f.HasPresence() { - return t.Pointer() - } - - return t -} - -func (c context) importableTypeName(f pgs.Field, e pgs.Entity) TypeName { - t := TypeName(c.Name(e)) - - if c.ImportPath(e) == c.ImportPath(f) { - return t - } - - return TypeName(fmt.Sprintf("%s.%s", c.PackageName(e), t)) -} - -func (c context) elType(ft pgs.FieldType) TypeName { - el := ft.Element() - switch { - case el.IsEnum(): - return c.importableTypeName(ft.Field(), el.Enum()) - case el.IsEmbed(): - return c.importableTypeName(ft.Field(), el.Embed()).Pointer() - default: - return scalarType(el.ProtoType()) - } -} - -func scalarType(t pgs.ProtoType) TypeName { - switch t { - case pgs.DoubleT: - return "float64" - case pgs.FloatT: - return "float32" - case pgs.Int64T, pgs.SFixed64, pgs.SInt64: - return "int64" - case pgs.UInt64T, pgs.Fixed64T: - return "uint64" - case pgs.Int32T, pgs.SFixed32, pgs.SInt32: - return "int32" - case pgs.UInt32T, pgs.Fixed32T: - return "uint32" - case pgs.BoolT: - return "bool" - case pgs.StringT: - return "string" - case pgs.BytesT: - return "[]byte" - default: - panic("unreachable: invalid scalar type") - } -} - -// A TypeName describes the name of a type (type on a field, or method signature) -type TypeName string - -// String satisfies the strings.Stringer interface. -func (n TypeName) String() string { return string(n) } - -// Element returns the TypeName of the element of n. For types other than -// slices and maps, this just returns n. -func (n TypeName) Element() TypeName { - parts := strings.SplitN(string(n), "]", 2) - return TypeName(parts[len(parts)-1]) -} - -// Key returns the TypeName of the key of n. For slices, the return TypeName is -// always "int", and for non slice/map types an empty TypeName is returned. -func (n TypeName) Key() TypeName { - parts := strings.SplitN(string(n), "]", 2) - if len(parts) == 1 { - return TypeName("") - } - - parts = strings.SplitN(parts[0], "[", 2) - if len(parts) != 2 { - return TypeName("") - } else if parts[1] == "" { - return TypeName("int") - } - - return TypeName(parts[1]) -} - -// IsPointer reports whether TypeName n is a pointer type, slice or a map. -func (n TypeName) IsPointer() bool { - ns := string(n) - return strings.HasPrefix(ns, "*") || - strings.HasPrefix(ns, "[") || - strings.HasPrefix(ns, "map[") -} - -// Pointer converts TypeName n to it's pointer type. If n is already a pointer, -// slice, or map, it is returned unmodified. -func (n TypeName) Pointer() TypeName { - if n.IsPointer() { - return n - } - return TypeName("*" + string(n)) -} - -// Value converts TypeName n to it's value type. If n is already a value type, -// slice, or map it is returned unmodified. -func (n TypeName) Value() TypeName { - return TypeName(strings.TrimPrefix(string(n), "*")) -} diff --git a/v2/lang/go/type_name_p2_presence_test.go b/v2/lang/go/type_name_p2_presence_test.go deleted file mode 100644 index 2fe93fd..0000000 --- a/v2/lang/go/type_name_p2_presence_test.go +++ /dev/null @@ -1,122 +0,0 @@ -//go:build !proto3_presence -// +build !proto3_presence - -package pgsgo - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - pgs "github.com/lyft/protoc-gen-star/v2" -) - -func TestType(t *testing.T) { - t.Parallel() - - ast := buildGraph(t, "names", "types") - ctx := loadContext(t, "names", "types") - - tests := []struct { - field string - expected TypeName - }{ - // proto2 syntax, optional - {"Proto2.double", "*float64"}, - {"Proto2.float", "*float32"}, - {"Proto2.int64", "*int64"}, - {"Proto2.sfixed64", "*int64"}, - {"Proto2.sint64", "*int64"}, - {"Proto2.uint64", "*uint64"}, - {"Proto2.fixed64", "*uint64"}, - {"Proto2.int32", "*int32"}, - {"Proto2.sfixed32", "*int32"}, - {"Proto2.sint32", "*int32"}, - {"Proto2.uint32", "*uint32"}, - {"Proto2.fixed32", "*uint32"}, - {"Proto2.bool", "*bool"}, - {"Proto2.string", "*string"}, - {"Proto2.bytes", "[]byte"}, - {"Proto2.enum", "*Proto2_Enum"}, - {"Proto2.ext_enum", "*ptype.Syntax"}, - {"Proto2.msg", "*Proto2_Required"}, - {"Proto2.ext_msg", "*duration.Duration"}, - {"Proto2.repeated_scalar", "[]float64"}, - {"Proto2.repeated_enum", "[]Proto2_Enum"}, - {"Proto2.repeated_ext_enum", "[]ptype.Syntax"}, - {"Proto2.repeated_msg", "[]*Proto2_Required"}, - {"Proto2.repeated_ext_msg", "[]*duration.Duration"}, - {"Proto2.map_scalar", "map[string]float32"}, - {"Proto2.map_enum", "map[int32]Proto2_Enum"}, - {"Proto2.map_ext_enum", "map[uint64]ptype.Syntax"}, - {"Proto2.map_msg", "map[uint32]*Proto2_Required"}, - {"Proto2.map_ext_msg", "map[int64]*duration.Duration"}, - - // proto2 syntax, required - {"Proto2.Required.double", "*float64"}, - {"Proto2.Required.float", "*float32"}, - {"Proto2.Required.int64", "*int64"}, - {"Proto2.Required.sfixed64", "*int64"}, - {"Proto2.Required.sint64", "*int64"}, - {"Proto2.Required.uint64", "*uint64"}, - {"Proto2.Required.fixed64", "*uint64"}, - {"Proto2.Required.int32", "*int32"}, - {"Proto2.Required.sfixed32", "*int32"}, - {"Proto2.Required.sint32", "*int32"}, - {"Proto2.Required.uint32", "*uint32"}, - {"Proto2.Required.fixed32", "*uint32"}, - {"Proto2.Required.bool", "*bool"}, - {"Proto2.Required.string", "*string"}, - {"Proto2.Required.bytes", "[]byte"}, - {"Proto2.Required.enum", "*Proto2_Enum"}, - {"Proto2.Required.ext_enum", "*ptype.Syntax"}, - {"Proto2.Required.msg", "*Proto2_Required"}, - {"Proto2.Required.ext_msg", "*duration.Duration"}, - - {"Proto3.double", "float64"}, - {"Proto3.float", "float32"}, - {"Proto3.int64", "int64"}, - {"Proto3.sfixed64", "int64"}, - {"Proto3.sint64", "int64"}, - {"Proto3.uint64", "uint64"}, - {"Proto3.fixed64", "uint64"}, - {"Proto3.int32", "int32"}, - {"Proto3.sfixed32", "int32"}, - {"Proto3.sint32", "int32"}, - {"Proto3.uint32", "uint32"}, - {"Proto3.fixed32", "uint32"}, - {"Proto3.bool", "bool"}, - {"Proto3.string", "string"}, - {"Proto3.bytes", "[]byte"}, - {"Proto3.enum", "Proto3_Enum"}, - {"Proto3.ext_enum", "ptype.Syntax"}, - {"Proto3.msg", "*Proto3_Message"}, - {"Proto3.ext_msg", "*duration.Duration"}, - {"Proto3.repeated_scalar", "[]float64"}, - {"Proto3.repeated_enum", "[]Proto3_Enum"}, - {"Proto3.repeated_ext_enum", "[]ptype.Syntax"}, - {"Proto3.repeated_msg", "[]*Proto3_Message"}, - {"Proto3.repeated_ext_msg", "[]*duration.Duration"}, - {"Proto3.map_scalar", "map[string]float32"}, - {"Proto3.map_enum", "map[int32]Proto3_Enum"}, - {"Proto3.map_ext_enum", "map[uint64]ptype.Syntax"}, - {"Proto3.map_msg", "map[uint32]*Proto3_Message"}, - {"Proto3.map_ext_msg", "map[int64]*duration.Duration"}, - } - - for _, test := range tests { - tc := test - t.Run(tc.field, func(t *testing.T) { - t.Parallel() - - e, ok := ast.Lookup(".names.types." + tc.field) - require.True(t, ok, "could not find field") - - fld, ok := e.(pgs.Field) - require.True(t, ok, "entity is not a field") - - assert.Equal(t, tc.expected, ctx.Type(fld)) - }) - } -} diff --git a/v2/lang/go/type_name_p3_presence_test.go b/v2/lang/go/type_name_p3_presence_test.go deleted file mode 100644 index cb0a2bc..0000000 --- a/v2/lang/go/type_name_p3_presence_test.go +++ /dev/null @@ -1,90 +0,0 @@ -// +build proto3_presence - -package pgsgo - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - pgs "github.com/lyft/protoc-gen-star/v2" -) - -func TestType(t *testing.T) { - t.Parallel() - - ast := buildGraph(t, "presence", "types") - ctx := loadContext(t, "presence", "types") - - tests := []struct { - field string - expected TypeName - }{ - {"Proto3.double", "float64"}, - {"Proto3.float", "float32"}, - {"Proto3.int64", "int64"}, - {"Proto3.sfixed64", "int64"}, - {"Proto3.sint64", "int64"}, - {"Proto3.uint64", "uint64"}, - {"Proto3.fixed64", "uint64"}, - {"Proto3.int32", "int32"}, - {"Proto3.sfixed32", "int32"}, - {"Proto3.sint32", "int32"}, - {"Proto3.uint32", "uint32"}, - {"Proto3.fixed32", "uint32"}, - {"Proto3.bool", "bool"}, - {"Proto3.string", "string"}, - {"Proto3.bytes", "[]byte"}, - {"Proto3.enum", "Proto3_Enum"}, - {"Proto3.ext_enum", "typepb.Syntax"}, - {"Proto3.msg", "*Proto3_Message"}, - {"Proto3.ext_msg", "*durationpb.Duration"}, - {"Proto3.repeated_scalar", "[]float64"}, - {"Proto3.repeated_enum", "[]Proto3_Enum"}, - {"Proto3.repeated_ext_enum", "[]typepb.Syntax"}, - {"Proto3.repeated_msg", "[]*Proto3_Message"}, - {"Proto3.repeated_ext_msg", "[]*durationpb.Duration"}, - {"Proto3.map_scalar", "map[string]float32"}, - {"Proto3.map_enum", "map[int32]Proto3_Enum"}, - {"Proto3.map_ext_enum", "map[uint64]typepb.Syntax"}, - {"Proto3.map_msg", "map[uint32]*Proto3_Message"}, - {"Proto3.map_ext_msg", "map[int64]*durationpb.Duration"}, - - // proto3 syntax optional - {"Proto3.Optional.double", "*float64"}, - {"Proto3.Optional.float", "*float32"}, - {"Proto3.Optional.int64", "*int64"}, - {"Proto3.Optional.sfixed64", "*int64"}, - {"Proto3.Optional.sint64", "*int64"}, - {"Proto3.Optional.uint64", "*uint64"}, - {"Proto3.Optional.fixed64", "*uint64"}, - {"Proto3.Optional.int32", "*int32"}, - {"Proto3.Optional.sfixed32", "*int32"}, - {"Proto3.Optional.sint32", "*int32"}, - {"Proto3.Optional.uint32", "*uint32"}, - {"Proto3.Optional.fixed32", "*uint32"}, - {"Proto3.Optional.bool", "*bool"}, - {"Proto3.Optional.string", "*string"}, - {"Proto3.Optional.bytes", "[]byte"}, - {"Proto3.Optional.enum", "*Proto3_Enum"}, - {"Proto3.Optional.ext_enum", "*typepb.Syntax"}, - {"Proto3.Optional.msg", "*Proto3_Optional"}, - {"Proto3.Optional.ext_msg", "*durationpb.Duration"}, - } - - for _, test := range tests { - tc := test - t.Run(tc.field, func(t *testing.T) { - t.Parallel() - - e, ok := ast.Lookup(".names.types." + tc.field) - require.True(t, ok, "could not find field") - - fld, ok := e.(pgs.Field) - require.True(t, ok, "entity is not a field") - - assert.Equal(t, tc.expected, ctx.Type(fld)) - }) - } -} diff --git a/v2/lang/go/type_name_test.go b/v2/lang/go/type_name_test.go deleted file mode 100644 index 9215d6b..0000000 --- a/v2/lang/go/type_name_test.go +++ /dev/null @@ -1,258 +0,0 @@ -package pgsgo - -import ( - "fmt" - "testing" - - "github.com/stretchr/testify/assert" - - pgs "github.com/lyft/protoc-gen-star/v2" -) - -func TestTypeName(t *testing.T) { - t.Parallel() - - tests := []struct { - in string - el string - key string - ptr string - val string - }{ - { - in: "int", - el: "int", - ptr: "*int", - val: "int", - }, - { - in: "*int", - el: "*int", - ptr: "*int", - val: "int", - }, - { - in: "foo.bar", - el: "foo.bar", - ptr: "*foo.bar", - val: "foo.bar", - }, - { - in: "*foo.bar", - el: "*foo.bar", - ptr: "*foo.bar", - val: "foo.bar", - }, - { - in: "[]string", - el: "string", - key: "int", - ptr: "[]string", - val: "[]string", - }, - { - in: "[]*string", - el: "*string", - key: "int", - ptr: "[]*string", - val: "[]*string", - }, - { - in: "[]foo.bar", - el: "foo.bar", - key: "int", - ptr: "[]foo.bar", - val: "[]foo.bar", - }, - { - in: "[]*foo.bar", - el: "*foo.bar", - key: "int", - ptr: "[]*foo.bar", - val: "[]*foo.bar", - }, - { - in: "map[string]float64", - el: "float64", - key: "string", - ptr: "map[string]float64", - val: "map[string]float64", - }, - { - in: "map[string]*float64", - el: "*float64", - key: "string", - ptr: "map[string]*float64", - val: "map[string]*float64", - }, - { - in: "map[string]foo.bar", - el: "foo.bar", - key: "string", - ptr: "map[string]foo.bar", - val: "map[string]foo.bar", - }, - { - in: "map[string]*foo.bar", - el: "*foo.bar", - key: "string", - ptr: "map[string]*foo.bar", - val: "map[string]*foo.bar", - }, - { - in: "[][]byte", - el: "[]byte", - key: "int", - ptr: "[][]byte", - val: "[][]byte", - }, - { - in: "map[int64][]byte", - el: "[]byte", - key: "int64", - ptr: "map[int64][]byte", - val: "map[int64][]byte", - }, - } - - for _, test := range tests { - tc := test - t.Run(tc.in, func(t *testing.T) { - tn := TypeName(tc.in) - t.Parallel() - - t.Run("Element", func(t *testing.T) { - t.Parallel() - assert.Equal(t, tc.el, tn.Element().String()) - }) - - t.Run("Key", func(t *testing.T) { - t.Parallel() - assert.Equal(t, tc.key, tn.Key().String()) - }) - - t.Run("IsPointer", func(t *testing.T) { - t.Parallel() - assert.Equal(t, tc.ptr == tc.in, tn.IsPointer()) - }) - - t.Run("Pointer", func(t *testing.T) { - t.Parallel() - assert.Equal(t, tc.ptr, tn.Pointer().String()) - }) - - t.Run("Value", func(t *testing.T) { - t.Parallel() - assert.Equal(t, tc.val, tn.Value().String()) - }) - }) - } -} - -func TestTypeName_Key_Malformed(t *testing.T) { - t.Parallel() - tn := TypeName("]malformed") - assert.Empty(t, tn.Key().String()) -} - -func TestScalarType_Invalid(t *testing.T) { - t.Parallel() - assert.Panics(t, func() { - scalarType(pgs.ProtoType(0)) - }) -} - -func ExampleTypeName_Element() { - types := []string{ - "int", - "*my.Type", - "[]string", - "map[string]*io.Reader", - } - - for _, t := range types { - fmt.Println(TypeName(t).Element()) - } - - // Output: - // int - // *my.Type - // string - // *io.Reader -} - -func ExampleTypeName_Key() { - types := []string{ - "int", - "*my.Type", - "[]string", - "map[string]*io.Reader", - } - - for _, t := range types { - fmt.Println(TypeName(t).Key()) - } - - // Output: - // - // - // int - // string -} - -func ExampleTypeName_IsPointer() { - types := []string{ - "int", - "*my.Type", - "[]string", - "map[string]*io.Reader", - } - - for _, t := range types { - fmt.Println(TypeName(t).IsPointer()) - } - - // Output: - // false - // true - // true - // true -} - -func ExampleTypeName_Pointer() { - types := []string{ - "int", - "*my.Type", - "[]string", - "map[string]*io.Reader", - } - - for _, t := range types { - fmt.Println(TypeName(t).Pointer()) - } - - // Output: - // *int - // *my.Type - // []string - // map[string]*io.Reader -} - -func ExampleTypeName_Value() { - types := []string{ - "int", - "*my.Type", - "[]string", - "map[string]*io.Reader", - } - - for _, t := range types { - fmt.Println(TypeName(t).Value()) - } - - // Output: - // int - // my.Type - // []string - // map[string]*io.Reader -} diff --git a/v2/message.go b/v2/message.go deleted file mode 100644 index 13a2f88..0000000 --- a/v2/message.go +++ /dev/null @@ -1,342 +0,0 @@ -package pgs - -import ( - "github.com/golang/protobuf/proto" - "github.com/golang/protobuf/protoc-gen-go/descriptor" -) - -// Message describes a proto message. Messages can be contained in either -// another Message or File, and may house further Messages and/or Enums. While -// all Fields technically live on the Message, some may be contained within -// OneOf blocks. -type Message interface { - ParentEntity - - // Descriptor returns the underlying proto descriptor for this message - Descriptor() *descriptor.DescriptorProto - - // Parent returns either the File or Message that directly contains this - // Message. - Parent() ParentEntity - - // Fields returns all fields on the message, including those contained within - // OneOf blocks. - Fields() []Field - - // NonOneOfFields returns all fields not contained within OneOf blocks. - NonOneOfFields() []Field - - // OneOfFields returns only the fields contained within OneOf blocks. - OneOfFields() []Field - - // SyntheticOneOfFields returns only the fields contained within synthetic OneOf blocks. - // See: https://github.com/protocolbuffers/protobuf/blob/v3.17.0/docs/field_presence.md - SyntheticOneOfFields() []Field - - // OneOfs returns the OneOfs contained within this Message. - OneOfs() []OneOf - - // RealOneOfs returns the OneOfs contained within this Message. - // This excludes synthetic OneOfs. - // See: https://github.com/protocolbuffers/protobuf/blob/v3.17.0/docs/field_presence.md - RealOneOfs() []OneOf - - // Extensions returns all of the Extensions applied to this Message. - Extensions() []Extension - - // Dependents returns all of the messages where message is directly or - // transitively used. - Dependents() []Message - - // IsMapEntry identifies this message as a MapEntry. If true, this message is - // not generated as code, and is used exclusively when marshaling a map field - // to the wire format. - IsMapEntry() bool - - // IsWellKnown identifies whether or not this Message is a WKT from the - // `google.protobuf` package. Most official plugins special case these types - // and they usually need to be handled differently. - IsWellKnown() bool - - // WellKnownType returns the WellKnownType associated with this field. If - // IsWellKnown returns false, UnknownWKT is returned. - WellKnownType() WellKnownType - - setParent(p ParentEntity) - addField(f Field) - addExtension(e Extension) - addOneOf(o OneOf) - addDependent(message Message) - getDependents(set map[string]Message) -} - -type msg struct { - desc *descriptor.DescriptorProto - parent ParentEntity - fqn string - - msgs, preservedMsgs []Message - enums []Enum - exts []Extension - defExts []Extension - fields []Field - oneofs []OneOf - maps []Message - dependents []Message - dependentsCache map[string]Message - - info SourceCodeInfo -} - -func (m *msg) Name() Name { return Name(m.desc.GetName()) } -func (m *msg) FullyQualifiedName() string { return m.fqn } -func (m *msg) Syntax() Syntax { return m.parent.Syntax() } -func (m *msg) Package() Package { return m.parent.Package() } -func (m *msg) File() File { return m.parent.File() } -func (m *msg) BuildTarget() bool { return m.parent.BuildTarget() } -func (m *msg) SourceCodeInfo() SourceCodeInfo { return m.info } -func (m *msg) Descriptor() *descriptor.DescriptorProto { return m.desc } -func (m *msg) Parent() ParentEntity { return m.parent } -func (m *msg) IsMapEntry() bool { return m.desc.GetOptions().GetMapEntry() } -func (m *msg) Enums() []Enum { return m.enums } -func (m *msg) Messages() []Message { return m.msgs } -func (m *msg) Fields() []Field { return m.fields } -func (m *msg) OneOfs() []OneOf { return m.oneofs } -func (m *msg) MapEntries() []Message { return m.maps } - -func (m *msg) WellKnownType() WellKnownType { - if m.Package().ProtoName() == WellKnownTypePackage { - return LookupWKT(m.Name()) - } - return UnknownWKT -} - -func (m *msg) IsWellKnown() bool { - return m.WellKnownType().Valid() -} - -func (m *msg) AllEnums() []Enum { - es := m.Enums() - for _, m := range m.msgs { - es = append(es, m.AllEnums()...) - } - return es -} - -func (m *msg) AllMessages() []Message { - msgs := m.Messages() - for _, sm := range m.msgs { - msgs = append(msgs, sm.AllMessages()...) - } - return msgs -} - -func (m *msg) NonOneOfFields() (f []Field) { - for _, fld := range m.fields { - if !fld.InOneOf() { - f = append(f, fld) - } - } - return f -} - -func (m *msg) OneOfFields() (f []Field) { - for _, o := range m.oneofs { - f = append(f, o.Fields()...) - } - - return f -} - -func (m *msg) SyntheticOneOfFields() (f []Field) { - for _, o := range m.oneofs { - if o.IsSynthetic() { - f = append(f, o.Fields()...) - } - } - - return f -} - -func (m *msg) RealOneOfs() (r []OneOf) { - for _, o := range m.oneofs { - if !o.IsSynthetic() { - r = append(r, o) - } - } - - return r -} - -func (m *msg) Imports() (i []File) { - // Mapping for avoiding duplicate entries - mp := make(map[string]File, len(m.fields)) - for _, f := range m.fields { - for _, imp := range f.Imports() { - mp[imp.File().Name().String()] = imp - } - } - for _, f := range mp { - i = append(i, f) - } - return -} - -func (m *msg) getDependents(set map[string]Message) { - m.populateDependentsCache() - - for fqn, d := range m.dependentsCache { - set[fqn] = d - } -} - -func (m *msg) populateDependentsCache() { - if m.dependentsCache != nil { - return - } - - m.dependentsCache = map[string]Message{} - for _, dep := range m.dependents { - m.dependentsCache[dep.FullyQualifiedName()] = dep - dep.getDependents(m.dependentsCache) - } -} - -func (m *msg) Dependents() []Message { - m.populateDependentsCache() - return messageSetToSlice(m.FullyQualifiedName(), m.dependentsCache) -} - -func (m *msg) Extension(desc *proto.ExtensionDesc, ext interface{}) (bool, error) { - return extension(m.desc.GetOptions(), desc, &ext) -} - -func (m *msg) Extensions() []Extension { - return m.exts -} - -func (m *msg) DefinedExtensions() []Extension { - return m.defExts -} - -func (m *msg) accept(v Visitor) (err error) { - if v == nil { - return nil - } - - if v, err = v.VisitMessage(m); err != nil || v == nil { - return - } - - for _, e := range m.enums { - if err = e.accept(v); err != nil { - return - } - } - - for _, sm := range m.msgs { - if err = sm.accept(v); err != nil { - return - } - } - - for _, f := range m.fields { - if err = f.accept(v); err != nil { - return - } - } - - for _, o := range m.oneofs { - if err = o.accept(v); err != nil { - return - } - } - - for _, ext := range m.defExts { - if err = ext.accept(v); err != nil { - return - } - } - - return -} - -func (m *msg) addExtension(ext Extension) { - m.exts = append(m.exts, ext) -} - -func (m *msg) addDefExtension(ext Extension) { - m.defExts = append(m.defExts, ext) -} - -func (m *msg) setParent(p ParentEntity) { m.parent = p } - -func (m *msg) addEnum(e Enum) { - e.setParent(m) - m.enums = append(m.enums, e) -} - -func (m *msg) addMessage(sm Message) { - sm.setParent(m) - m.msgs = append(m.msgs, sm) -} - -func (m *msg) addField(f Field) { - f.setMessage(m) - m.fields = append(m.fields, f) -} - -func (m *msg) addOneOf(o OneOf) { - o.setMessage(m) - m.oneofs = append(m.oneofs, o) -} - -func (m *msg) addMapEntry(me Message) { - me.setParent(m) - m.maps = append(m.maps, me) -} - -func (m *msg) addDependent(message Message) { - m.dependents = append(m.dependents, message) -} - -func (m *msg) childAtPath(path []int32) Entity { - switch { - case len(path) == 0: - return m - case len(path)%2 != 0: - return nil - } - - var child Entity - switch path[0] { - case messageTypeFieldPath: - child = m.fields[path[1]] - case messageTypeNestedTypePath: - child = m.preservedMsgs[path[1]] - case messageTypeEnumTypePath: - child = m.enums[path[1]] - case messageTypeOneofDeclPath: - child = m.oneofs[path[1]] - default: - return nil - } - - return child.childAtPath(path[2:]) -} - -func (m *msg) addSourceCodeInfo(info SourceCodeInfo) { m.info = info } - -func messageSetToSlice(name string, set map[string]Message) []Message { - dependents := make([]Message, 0, len(set)) - - for fqn, d := range set { - if fqn != name { - dependents = append(dependents, d) - } - } - - return dependents -} - -var _ Message = (*msg)(nil) diff --git a/v2/message_test.go b/v2/message_test.go deleted file mode 100644 index b6eec25..0000000 --- a/v2/message_test.go +++ /dev/null @@ -1,469 +0,0 @@ -package pgs - -import ( - "errors" - "testing" - - desc "github.com/golang/protobuf/descriptor" - "github.com/golang/protobuf/proto" - "github.com/golang/protobuf/protoc-gen-go/descriptor" - "github.com/golang/protobuf/ptypes/any" - "github.com/stretchr/testify/assert" -) - -func TestMsg_Name(t *testing.T) { - t.Parallel() - - m := &msg{desc: &descriptor.DescriptorProto{Name: proto.String("msg")}} - - assert.Equal(t, "msg", m.Name().String()) -} - -func TestMsg_FullyQualifiedName(t *testing.T) { - t.Parallel() - - m := &msg{fqn: "msg"} - assert.Equal(t, m.fqn, m.FullyQualifiedName()) -} - -func TestMsg_Syntax(t *testing.T) { - t.Parallel() - - m := &msg{} - f := dummyFile() - f.addMessage(m) - - assert.Equal(t, f.Syntax(), m.Syntax()) -} - -func TestMsg_Package(t *testing.T) { - t.Parallel() - - m := &msg{} - f := dummyFile() - f.addMessage(m) - - assert.NotNil(t, m.Package()) - assert.Equal(t, f.Package(), m.Package()) -} - -func TestMsg_File(t *testing.T) { - t.Parallel() - - m := &msg{} - pm := dummyMsg() - pm.addMessage(m) - - assert.NotNil(t, m.File()) - assert.Equal(t, pm.File(), m.File()) -} - -func TestMsg_BuildTarget(t *testing.T) { - t.Parallel() - - m := &msg{} - f := dummyFile() - f.addMessage(m) - - assert.False(t, m.BuildTarget()) - f.buildTarget = true - assert.True(t, m.BuildTarget()) -} - -func TestMsg_Descriptor(t *testing.T) { - t.Parallel() - - m := &msg{desc: &descriptor.DescriptorProto{}} - assert.Equal(t, m.desc, m.Descriptor()) -} - -func TestMsg_Parent(t *testing.T) { - t.Parallel() - - m := &msg{} - pm := dummyMsg() - pm.addMessage(m) - - assert.Equal(t, pm, m.Parent()) -} - -func TestMsg_IsMapEntry(t *testing.T) { - t.Parallel() - - m := &msg{desc: &descriptor.DescriptorProto{}} - assert.False(t, m.IsMapEntry()) - - m.desc.Options = &descriptor.MessageOptions{ - MapEntry: proto.Bool(true), - } - assert.True(t, m.IsMapEntry()) -} - -func TestMsg_Enums(t *testing.T) { - t.Parallel() - - m := &msg{} - assert.Empty(t, m.Enums()) - - sm := &msg{} - sm.addEnum(&enum{}) - m.addMessage(sm) - - m.addEnum(&enum{}) - assert.Len(t, m.Enums(), 1) -} - -func TestMsg_AllEnums(t *testing.T) { - t.Parallel() - - m := &msg{} - assert.Empty(t, m.AllEnums()) - - sm := &msg{} - sm.addEnum(&enum{}) - m.addMessage(sm) - - m.addEnum(&enum{}) - assert.Len(t, m.AllEnums(), 2) -} - -func TestMsg_Messages(t *testing.T) { - t.Parallel() - - m := &msg{} - assert.Empty(t, m.Messages()) - - sm := &msg{} - sm.addMessage(&msg{}) - m.addMessage(sm) - - assert.Len(t, m.Messages(), 1) -} - -func TestMsg_AllMessages(t *testing.T) { - t.Parallel() - - m := &msg{} - assert.Empty(t, m.AllMessages()) - - sm := &msg{} - sm.addMessage(&msg{}) - m.addMessage(sm) - - assert.Len(t, m.AllMessages(), 2) -} - -func TestMsg_MapEntries(t *testing.T) { - t.Parallel() - - m := &msg{} - assert.Empty(t, m.MapEntries()) - - m.addMapEntry(&msg{}) - assert.Len(t, m.MapEntries(), 1) -} - -func TestMsg_Fields(t *testing.T) { - t.Parallel() - - m := &msg{} - assert.Empty(t, m.Fields()) - - m.addField(&field{}) - m.addField(&field{oneof: &oneof{}}) - assert.Len(t, m.Fields(), 2) -} - -func TestMsg_NonOneOfFields(t *testing.T) { - t.Parallel() - - m := &msg{} - assert.Empty(t, m.NonOneOfFields()) - - m.addField(&field{}) - m.addField(&field{oneof: &oneof{}}) - m.addField(&field{}) - assert.Len(t, m.NonOneOfFields(), 2) -} - -func TestMsg_OneOfFields(t *testing.T) { - t.Parallel() - - o := &oneof{} - o.addField(&field{}) - - m := &msg{} - m.addField(&field{}) - m.addField(&field{}) - - assert.Empty(t, m.OneOfFields()) - m.addOneOf(o) - assert.Len(t, m.OneOfFields(), 1) -} - -func TestMsg_OneOfs(t *testing.T) { - t.Parallel() - - m := &msg{} - assert.Empty(t, m.OneOfs()) - - m.addOneOf(&oneof{}) - assert.Len(t, m.OneOfs(), 1) -} - -func TestMsg_SyntheticOneOfFields_And_RealOneOfs(t *testing.T) { - t.Parallel() - - oSyn := &oneof{} - oSyn.flds = []Field{dummyOneOfField(true)} - oSyn.flds[0].setOneOf(oSyn) - - oReal := &oneof{} - oReal.flds = []Field{dummyField(), dummyField()} - oReal.flds[0].setOneOf(oReal) - oReal.flds[1].setOneOf(oReal) - - // no one offs - m := dummyMsg() - assert.Len(t, m.OneOfFields(), 0, "oneof fields") - assert.Len(t, m.SyntheticOneOfFields(), 0, "synthetic oneof fields") - assert.Len(t, m.OneOfs(), 0, "oneofs") - assert.Len(t, m.RealOneOfs(), 0, "real oneofs") - - // one real oneof - m.addField(oReal.flds[0]) - m.addField(oReal.flds[1]) - m.addOneOf(oReal) - assert.Len(t, m.OneOfFields(), 2, "oneof fields") - assert.Len(t, m.SyntheticOneOfFields(), 0, "synthetic oneof fields") - assert.Len(t, m.OneOfs(), 1, "oneofs") - assert.Len(t, m.RealOneOfs(), 1, "real oneofs") - - // one real, one synthetic oneof - m.addField(oSyn.flds[0]) - m.addOneOf(oSyn) - assert.Len(t, m.OneOfFields(), 3, "oneof fields") - assert.Len(t, m.SyntheticOneOfFields(), 1, "synthetic oneof fields") - assert.Len(t, m.OneOfs(), 2, "oneofs") - assert.Len(t, m.RealOneOfs(), 1, "real oneofs") - - // one synthetic oneof - m = dummyMsg() - m.addField(oSyn.flds[0]) - m.addOneOf(oSyn) - assert.Len(t, m.OneOfFields(), 1, "oneof fields") - assert.Len(t, m.SyntheticOneOfFields(), 1, "synthetic oneof fields") - assert.Len(t, m.OneOfs(), 1, "oneofs") - assert.Len(t, m.RealOneOfs(), 0, "real oneofs") -} - -func TestMsg_Extension(t *testing.T) { - // cannot be parallel - m := &msg{desc: &descriptor.DescriptorProto{}} - assert.NotPanics(t, func() { m.Extension(nil, nil) }) -} - -func TestMsg_Extensions(t *testing.T) { - t.Parallel() - - m := &msg{} - assert.Empty(t, m.Extensions()) - - ext := &ext{} - m.addExtension(ext) - assert.Len(t, m.Extensions(), 1) -} - -func TestMsg_DefinedExtensions(t *testing.T) { - t.Parallel() - - m := &msg{} - assert.Empty(t, m.DefinedExtensions()) - - ext := &ext{} - m.addDefExtension(ext) - assert.Len(t, m.DefinedExtensions(), 1) -} - -func TestMsg_Accept(t *testing.T) { - t.Parallel() - - m := &msg{} - m.addMessage(&msg{}) - m.addEnum(&enum{}) - m.addField(&field{}) - m.addOneOf(&oneof{}) - m.addDefExtension(&ext{}) - - assert.NoError(t, m.accept(nil)) - - v := &mockVisitor{} - assert.NoError(t, m.accept(v)) - assert.Equal(t, 1, v.message) - assert.Zero(t, v.enum) - assert.Zero(t, v.field) - assert.Zero(t, v.oneof) - assert.Zero(t, v.extension) - - v.Reset() - v.v = v - v.err = errors.New("") - assert.Error(t, m.accept(v)) - assert.Equal(t, 1, v.message) - assert.Zero(t, v.enum) - assert.Zero(t, v.field) - assert.Zero(t, v.oneof) - assert.Zero(t, v.extension) - - v.Reset() - assert.NoError(t, m.accept(v)) - assert.Equal(t, 2, v.message) - assert.Equal(t, 1, v.enum) - assert.Equal(t, 1, v.field) - assert.Equal(t, 1, v.oneof) - assert.Equal(t, 1, v.extension) - - v.Reset() - m.addDefExtension(&mockExtension{err: errors.New("")}) - assert.Error(t, m.accept(v)) - assert.Equal(t, 2, v.message) - assert.Equal(t, 1, v.enum) - assert.Equal(t, 1, v.field) - assert.Equal(t, 1, v.oneof) - assert.Equal(t, 2, v.extension) - - v.Reset() - m.addOneOf(&mockOneOf{err: errors.New("")}) - assert.Error(t, m.accept(v)) - assert.Equal(t, 2, v.message) - assert.Equal(t, 1, v.enum) - assert.Equal(t, 1, v.field) - assert.Equal(t, 2, v.oneof) - assert.Zero(t, v.extension) - - v.Reset() - m.addField(&mockField{err: errors.New("")}) - assert.Error(t, m.accept(v)) - assert.Equal(t, 2, v.message) - assert.Equal(t, 1, v.enum) - assert.Equal(t, 2, v.field) - assert.Zero(t, v.oneof) - assert.Zero(t, v.extension) - - v.Reset() - m.addMessage(&mockMessage{err: errors.New("")}) - assert.Error(t, m.accept(v)) - assert.Equal(t, 3, v.message) - assert.Equal(t, 1, v.enum) - assert.Zero(t, v.field) - assert.Zero(t, v.oneof) - assert.Zero(t, v.extension) - - v.Reset() - m.addEnum(&mockEnum{err: errors.New("")}) - assert.Error(t, m.accept(v)) - assert.Equal(t, 2, v.enum) - assert.Equal(t, 1, v.message) - assert.Zero(t, v.field) - assert.Zero(t, v.oneof) - assert.Zero(t, v.extension) -} - -func TestMsg_Imports(t *testing.T) { - t.Parallel() - - m := &msg{} - assert.Empty(t, m.Imports()) - - m.addField(&mockField{i: []File{&file{}, &file{}}}) - assert.Len(t, m.Imports(), 1) - - nf := &file{desc: &descriptor.FileDescriptorProto{ - Name: proto.String("foobar"), - }} - m.addField(&mockField{i: []File{nf, nf}}) - assert.Len(t, m.Imports(), 2) -} - -func TestMsg_Dependents(t *testing.T) { - t.Parallel() - - pkg := dummyPkg() - f := &file{ - pkg: pkg, - desc: &descriptor.FileDescriptorProto{ - Package: proto.String(pkg.ProtoName().String()), - Syntax: proto.String(string(Proto3)), - Name: proto.String("test_file.proto"), - }, - } - - m := &msg{parent: f} - m.fqn = fullyQualifiedName(f, m) - m2 := dummyMsg() - m.addDependent(m2) - deps := m.Dependents() - - assert.Len(t, deps, 1) - assert.Contains(t, deps, m2) -} - -func TestMsg_ChildAtPath(t *testing.T) { - t.Parallel() - - m := &msg{} - assert.Equal(t, m, m.childAtPath(nil)) - assert.Nil(t, m.childAtPath([]int32{1})) - assert.Nil(t, m.childAtPath([]int32{999, 456})) -} - -func TestMsg_WellKnownType(t *testing.T) { - fd, md := desc.ForMessage(&any.Any{}) - p := &pkg{fd: fd} - f := &file{desc: fd} - m := &msg{desc: md} - f.addMessage(m) - p.addFile(f) - - assert.True(t, m.IsWellKnown()) - assert.Equal(t, AnyWKT, m.WellKnownType()) - - m.desc.Name = proto.String("Foobar") - assert.False(t, m.IsWellKnown()) - assert.Equal(t, UnknownWKT, m.WellKnownType()) - - m.desc.Name = proto.String("Any") - f.desc.Package = proto.String("fizz.buzz") - assert.False(t, m.IsWellKnown()) - assert.Equal(t, UnknownWKT, m.WellKnownType()) -} - -type mockMessage struct { - Message - i []File - p ParentEntity - err error -} - -func (m *mockMessage) Imports() []File { return m.i } - -func (m *mockMessage) setParent(p ParentEntity) { m.p = p } - -func (m *mockMessage) accept(v Visitor) error { - _, err := v.VisitMessage(m) - if m.err != nil { - return m.err - } - return err -} - -func dummyMsg() *msg { - f := dummyFile() - - m := &msg{ - desc: &descriptor.DescriptorProto{Name: proto.String("message")}, - } - - f.addMessage(m) - return m -} diff --git a/v2/method.go b/v2/method.go deleted file mode 100644 index ff45731..0000000 --- a/v2/method.go +++ /dev/null @@ -1,96 +0,0 @@ -package pgs - -import ( - "github.com/golang/protobuf/proto" - "github.com/golang/protobuf/protoc-gen-go/descriptor" -) - -// Method describes a method on a proto service -type Method interface { - Entity - - // Descriptor returns the underlying proto descriptor for this. - Descriptor() *descriptor.MethodDescriptorProto - - // Service returns the parent service for this. - Service() Service - - // Input returns the Message representing the input type for this. - Input() Message - - // Output returns the Message representing the output type for this. - Output() Message - - // ClientStreaming indicates if this method allows clients to stream inputs. - ClientStreaming() bool - - // ServerStreaming indicates if this method allows servers to stream outputs. - ServerStreaming() bool - - setService(Service) -} - -type method struct { - desc *descriptor.MethodDescriptorProto - fqn string - service Service - - in, out Message - - info SourceCodeInfo -} - -func (m *method) Name() Name { return Name(m.desc.GetName()) } -func (m *method) FullyQualifiedName() string { return m.fqn } -func (m *method) Syntax() Syntax { return m.service.Syntax() } -func (m *method) Package() Package { return m.service.Package() } -func (m *method) File() File { return m.service.File() } -func (m *method) BuildTarget() bool { return m.service.BuildTarget() } -func (m *method) SourceCodeInfo() SourceCodeInfo { return m.info } -func (m *method) Descriptor() *descriptor.MethodDescriptorProto { return m.desc } -func (m *method) Service() Service { return m.service } -func (m *method) Input() Message { return m.in } -func (m *method) Output() Message { return m.out } -func (m *method) ClientStreaming() bool { return m.desc.GetClientStreaming() } -func (m *method) ServerStreaming() bool { return m.desc.GetServerStreaming() } -func (m *method) BiDirStreaming() bool { return m.ClientStreaming() && m.ServerStreaming() } - -func (m *method) Imports() (i []File) { - mine := m.File().Name() - input := m.Input().File() - output := m.Output().File() - - if mine != input.Name() { - i = append(i, input) - } - if mine != output.Name() && input.Name() != output.Name() { - i = append(i, output) - } - return -} - -func (m *method) Extension(desc *proto.ExtensionDesc, ext interface{}) (ok bool, err error) { - return extension(m.desc.GetOptions(), desc, &ext) -} - -func (m *method) accept(v Visitor) (err error) { - if v == nil { - return - } - - _, err = v.VisitMethod(m) - return -} - -func (m *method) setService(s Service) { m.service = s } - -func (m *method) childAtPath(path []int32) Entity { - if len(path) == 0 { - return m - } - return nil -} - -func (m *method) addSourceCodeInfo(info SourceCodeInfo) { m.info = info } - -var _ Method = (*method)(nil) diff --git a/v2/method_test.go b/v2/method_test.go deleted file mode 100644 index 30dfa81..0000000 --- a/v2/method_test.go +++ /dev/null @@ -1,193 +0,0 @@ -package pgs - -import ( - "testing" - - "errors" - - "github.com/golang/protobuf/proto" - "github.com/golang/protobuf/protoc-gen-go/descriptor" - "github.com/stretchr/testify/assert" -) - -func TestMethod_Name(t *testing.T) { - t.Parallel() - m := &method{desc: &descriptor.MethodDescriptorProto{Name: proto.String("foo")}} - assert.Equal(t, "foo", m.Name().String()) -} - -func TestMethod_FullyQualifiedName(t *testing.T) { - t.Parallel() - - m := &method{fqn: "fizz"} - assert.Equal(t, m.fqn, m.FullyQualifiedName()) -} - -func TestMethod_Syntax(t *testing.T) { - t.Parallel() - m := &method{} - s := dummyService() - s.addMethod(m) - assert.Equal(t, s.Syntax(), m.Syntax()) -} - -func TestMethod_Package(t *testing.T) { - t.Parallel() - m := &method{} - s := dummyService() - s.addMethod(m) - - assert.NotNil(t, m.Package()) - assert.Equal(t, s.Package(), m.Package()) -} - -func TestMethod_File(t *testing.T) { - t.Parallel() - m := &method{} - s := dummyService() - s.addMethod(m) - - assert.NotNil(t, m.File()) - assert.Equal(t, s.File(), m.File()) -} - -func TestMethod_BuildTarget(t *testing.T) { - t.Parallel() - m := &method{} - s := dummyService() - s.addMethod(m) - - assert.False(t, m.BuildTarget()) - s.setFile(&file{buildTarget: true}) - assert.True(t, m.BuildTarget()) -} - -func TestMethod_Descriptor(t *testing.T) { - t.Parallel() - m := &method{desc: &descriptor.MethodDescriptorProto{}} - assert.Equal(t, m.desc, m.Descriptor()) -} - -func TestMethod_Service(t *testing.T) { - t.Parallel() - m := &method{} - s := dummyService() - s.addMethod(m) - - assert.Equal(t, s, m.Service()) -} - -func TestMethod_Input(t *testing.T) { - t.Parallel() - m := &method{in: &msg{}} - assert.Equal(t, m.in, m.Input()) -} - -func TestMethod_Output(t *testing.T) { - t.Parallel() - m := &method{out: &msg{}} - assert.Equal(t, m.out, m.Output()) -} - -func TestMethod_ClientStreaming(t *testing.T) { - t.Parallel() - - m := &method{desc: &descriptor.MethodDescriptorProto{}} - assert.False(t, m.ClientStreaming()) - m.desc.ClientStreaming = proto.Bool(true) - assert.True(t, m.ClientStreaming()) -} - -func TestMethod_ServerStreaming(t *testing.T) { - t.Parallel() - - m := &method{desc: &descriptor.MethodDescriptorProto{}} - assert.False(t, m.ServerStreaming()) - m.desc.ServerStreaming = proto.Bool(true) - assert.True(t, m.ServerStreaming()) -} - -func TestMethod_BiDirStreaming(t *testing.T) { - t.Parallel() - - m := &method{desc: &descriptor.MethodDescriptorProto{}} - assert.False(t, m.BiDirStreaming()) - m.desc.ServerStreaming = proto.Bool(true) - assert.False(t, m.BiDirStreaming()) - m.desc.ServerStreaming = proto.Bool(false) - m.desc.ClientStreaming = proto.Bool(true) - assert.False(t, m.BiDirStreaming()) - m.desc.ServerStreaming = proto.Bool(true) - assert.True(t, m.BiDirStreaming()) -} - -func TestMethod_Imports(t *testing.T) { - t.Parallel() - - s := dummyService() - m := &method{ - in: dummyMsg(), - out: dummyMsg(), - } - s.addMethod(m) - - f := &file{desc: &descriptor.FileDescriptorProto{ - Name: proto.String("foobar"), - }} - assert.Empty(t, m.Imports()) - m.in = &msg{parent: f} - assert.Len(t, m.Imports(), 1) - - m.out = &msg{parent: &file{}} - assert.Len(t, m.Imports(), 2) - - m.out = &msg{parent: f} - assert.Len(t, m.Imports(), 1) -} - -func TestMethod_Extension(t *testing.T) { - // cannot be parallel - - m := &method{desc: &descriptor.MethodDescriptorProto{}} - assert.NotPanics(t, func() { m.Extension(nil, nil) }) -} - -func TestMethod_Accept(t *testing.T) { - t.Parallel() - - m := &method{} - - assert.Nil(t, m.accept(nil)) - - v := &mockVisitor{err: errors.New("foo")} - assert.Error(t, m.accept(v)) - assert.Equal(t, 1, v.method) -} - -func TestMethod_ChildAtPath(t *testing.T) { - t.Parallel() - - m := &method{} - - assert.Equal(t, m, m.childAtPath(nil)) - assert.Nil(t, m.childAtPath([]int32{1})) -} - -type mockMethod struct { - Method - i []File - s Service - err error -} - -func (m *mockMethod) Imports() []File { return m.i } - -func (m *mockMethod) setService(s Service) { m.s = s } - -func (m *mockMethod) accept(v Visitor) error { - _, err := v.VisitMethod(m) - if m.err != nil { - return m.err - } - return err -} diff --git a/v2/module.go b/v2/module.go deleted file mode 100644 index 528a831..0000000 --- a/v2/module.go +++ /dev/null @@ -1,266 +0,0 @@ -package pgs - -import "os" - -// Module describes the interface for a domain-specific code generation module -// that can be registered with the PG* generator. -type Module interface { - // The Name of the Module, used when establishing the build context and used - // as the base prefix for all debugger output. - Name() string - - // InitContext is called on a Module with a pre-configured BuildContext that - // should be stored and used by the Module. - InitContext(c BuildContext) - - // Execute is called on the module with the target Files as well as all - // loaded Packages from the gatherer. The module should return a slice of - // Artifacts that it would like to be generated. - Execute(targets map[string]File, packages map[string]Package) []Artifact -} - -// ModuleBase provides utility methods and a base implementation for a -// protoc-gen-star Module. ModuleBase should be used as an anonymously embedded -// field of an actual Module implementation. The only methods that need to be -// overridden are Name and Execute. -// -// type MyModule { -// *pgs.ModuleBase -// } -// -// func InitMyModule() *MyModule { return &MyModule{ &pgs.ModuleBase{} } } -// -// func (m *MyModule) Name() string { return "MyModule" } -// -// func (m *MyModule) Execute(...) []pgs.Artifact { ... } -// -type ModuleBase struct { - BuildContext - artifacts []Artifact -} - -// InitContext populates this Module with the BuildContext from the parent -// Generator, allowing for easy debug logging, error checking, and output path -// management. This method is called prior to Execute for modules registered -// with the generator. -func (m *ModuleBase) InitContext(c BuildContext) { - m.BuildContext = c - m.Debug("initializing") -} - -// Name satisfies the Module interface, however this method will panic and must -// be overridden by a parent struct. -func (m *ModuleBase) Name() string { - panic("Name method is not implemented for this module") -} - -// Execute satisfies the Module interface, however this method will fail and -// must be overridden by a parent struct. -func (m *ModuleBase) Execute(targets map[string]File, packages map[string]Package) []Artifact { - m.Fail("Execute method is not implemented for this module") - return m.Artifacts() -} - -// Push adds a prefix to the Module's BuildContext. Pop should be called when -// the context is complete. -func (m *ModuleBase) Push(prefix string) BuildContext { - m.BuildContext = m.BuildContext.Push(prefix) - return m -} - -// PushDir changes the OutputPath of the Module's BuildContext. Pop (or PopDir) -// should be called when that context is complete. -func (m *ModuleBase) PushDir(dir string) BuildContext { - m.BuildContext = m.BuildContext.PushDir(dir) - return m -} - -// Pop removes the last push from the Module's BuildContext. This method should -// only be called after a paired Push or PushDir. -func (m *ModuleBase) Pop() BuildContext { - m.BuildContext = m.BuildContext.Pop() - return m -} - -// PopDir removes the last PushDir from the Module's BuildContext. This method -// should only be called after a paired PushDir. -func (m *ModuleBase) PopDir() BuildContext { - m.BuildContext = m.BuildContext.PopDir() - return m -} - -// Artifacts returns the slice of generation artifacts that have been captured -// by the Module. This method should/can be the return value of its Execute -// method. Subsequent calls will return a nil slice until more artifacts are -// added. -func (m *ModuleBase) Artifacts() []Artifact { - out := m.artifacts - m.artifacts = nil - return out -} - -// AddArtifact adds an Artifact to this Module's collection of generation -// artifacts. This method is available as a convenience but the other Add & -// Overwrite methods should be used preferentially. -func (m *ModuleBase) AddArtifact(a ...Artifact) { m.artifacts = append(m.artifacts, a...) } - -// AddGeneratorFile adds a file with the provided name and contents to the code -// generation response payload to protoc. Name must be a path relative to and -// within the protoc-plugin's output destination, which may differ from the -// BuildContext's OutputPath value. If another Module or Plugin has added a -// file with the same name, protoc will produce an error. -func (m *ModuleBase) AddGeneratorFile(name, content string) { - m.AddArtifact(GeneratorFile{ - Name: name, - Contents: content, - }) -} - -// OverwriteGeneratorFile behaves the same as AddGeneratorFile, however if a -// previously executed Module has created a file with the same name, it will be -// overwritten with this one. -func (m *ModuleBase) OverwriteGeneratorFile(name, content string) { - m.AddArtifact(GeneratorFile{ - Name: name, - Contents: content, - Overwrite: true, - }) -} - -// AddGeneratorTemplateFile behaves the same as AddGeneratorFile, however the -// contents are rendered from the provided tpl and data. -func (m *ModuleBase) AddGeneratorTemplateFile(name string, tpl Template, data interface{}) { - m.AddArtifact(GeneratorTemplateFile{ - Name: name, - TemplateArtifact: TemplateArtifact{ - Template: tpl, - Data: data, - }, - }) -} - -// OverwriteGeneratorTemplateFile behaves the same as OverwriteGeneratorFile, -// however the contents are rendered from the provided tpl and data. -func (m *ModuleBase) OverwriteGeneratorTemplateFile(name string, tpl Template, data interface{}) { - m.AddArtifact(GeneratorTemplateFile{ - Name: name, - Overwrite: true, - TemplateArtifact: TemplateArtifact{ - Template: tpl, - Data: data, - }, - }) -} - -// AddGeneratorAppend attempts to append content to the specified file name. -// Name must be a path relative to and within the protoc-plugin's output -// destination, which may differ from the BuildContext's OutputPath value. If -// the file is not generated by this protoc-plugin, execution will fail. -func (m *ModuleBase) AddGeneratorAppend(name, content string) { - m.AddArtifact(GeneratorAppend{ - FileName: name, - Contents: content, - }) -} - -// AddGeneratorTemplateAppend behaves the same as AddGeneratorAppend, however -// the contents are rendered from the provided tpl and data. -func (m *ModuleBase) AddGeneratorTemplateAppend(name string, tpl Template, data interface{}) { - m.AddArtifact(GeneratorTemplateAppend{ - FileName: name, - TemplateArtifact: TemplateArtifact{ - Template: tpl, - Data: data, - }, - }) -} - -// AddGeneratorInjection attempts to inject content into the file with name at -// the specified insertion point. Name must be a path relative to and within -// the protoc-plugin's output destination, which may differ from the -// BuildContext's OutputPath value. The file does not need to be generated by -// this protoc-plugin but the generating plugin must be called first in the -// protoc execution. -// -// See: https://godoc.org/github.com/golang/protobuf/protoc-gen-go/plugin#CodeGeneratorResponse_File -func (m *ModuleBase) AddGeneratorInjection(name, point, content string) { - m.AddArtifact(GeneratorInjection{ - FileName: name, - InsertionPoint: point, - Contents: content, - }) -} - -// AddGeneratorTemplateInjection behaves the same as AddGeneratorInjection, -// however the contents are rendered from the provided tpl and data. -func (m *ModuleBase) AddGeneratorTemplateInjection(name, point string, tpl Template, data interface{}) { - m.AddArtifact(GeneratorTemplateInjection{ - FileName: name, - InsertionPoint: point, - TemplateArtifact: TemplateArtifact{ - Template: tpl, - Data: data, - }, - }) -} - -// AddCustomFile creates a file directly on the file system with the provided -// content and perms. Unlike AddGeneratorFile, this method does not use protoc -// to generate the file. If name is a relative path, it is related to the -// directory in which protoc was executed; name can also be an absolute path. -// If a file already exists with the specified name, the file will not be -// created and there will be no generation error. -func (m *ModuleBase) AddCustomFile(name, content string, perms os.FileMode) { - m.AddArtifact(CustomFile{ - Name: name, - Contents: content, - Perms: perms, - }) -} - -// OverwriteCustomFile behaves the same as AddCustomFile, however if the file -// already exists, it will be overwritten with this one. -func (m *ModuleBase) OverwriteCustomFile(name, content string, perms os.FileMode) { - m.AddArtifact(CustomFile{ - Name: name, - Contents: content, - Perms: perms, - Overwrite: true, - }) -} - -// AddCustomTemplateFile behaves the same as AddCustomFile, however the -// contents are rendered from the provided tpl and data. -func (m *ModuleBase) AddCustomTemplateFile(name string, tpl Template, data interface{}, perms os.FileMode) { - m.AddArtifact(CustomTemplateFile{ - Name: name, - Perms: perms, - TemplateArtifact: TemplateArtifact{ - Template: tpl, - Data: data, - }, - }) -} - -// OverwriteCustomTemplateFile behaves the same as OverwriteCustomFile, however -// the contents are rendered from the provided tpl and data. -func (m *ModuleBase) OverwriteCustomTemplateFile(name string, tpl Template, data interface{}, perms os.FileMode) { - m.AddArtifact(CustomTemplateFile{ - Name: name, - Perms: perms, - Overwrite: true, - TemplateArtifact: TemplateArtifact{ - Template: tpl, - Data: data, - }, - }) -} - -// AddError adds a string to the `errors` field of the created -// CodeGeneratorResponse. Multiple calls to AddError will cause the errors to -// be concatenated (separated by "; "). -func (m *ModuleBase) AddError(message string) { - m.AddArtifact(GeneratorError{Message: message}) -} - -var _ Module = (*ModuleBase)(nil) diff --git a/v2/module_test.go b/v2/module_test.go deleted file mode 100644 index 94eae59..0000000 --- a/v2/module_test.go +++ /dev/null @@ -1,284 +0,0 @@ -package pgs - -import ( - "testing" - - "text/template" - - "github.com/stretchr/testify/assert" -) - -type mockModule struct { - *ModuleBase - name string - executed bool -} - -func newMockModule() *mockModule { return &mockModule{ModuleBase: &ModuleBase{}} } - -func (m *mockModule) Name() string { return m.name } - -func (m *mockModule) Execute(targets map[string]File, packages map[string]Package) []Artifact { - m.executed = true - return nil -} - -func TestModuleBase_InitContext(t *testing.T) { - t.Parallel() - - m := new(ModuleBase) - assert.Nil(t, m.BuildContext) - bc := Context(InitMockDebugger(), Parameters{}, ".") - m.InitContext(bc) - assert.NotNil(t, m.BuildContext) -} - -func TestModuleBase_Name(t *testing.T) { - t.Parallel() - - m := new(ModuleBase) - assert.Panics(t, func() { m.Name() }) -} - -func TestModuleBase_Execute(t *testing.T) { - t.Parallel() - - m := new(ModuleBase) - d := InitMockDebugger() - m.InitContext(Context(d, Parameters{}, ".")) - - assert.NotPanics(t, func() { m.Execute(nil, nil) }) - assert.True(t, d.Failed()) -} - -func TestModuleBase_PushPop(t *testing.T) { - t.Parallel() - - m := new(ModuleBase) - m.InitContext(Context(InitMockDebugger(), Parameters{}, ".")) - m.Push("foo") - m.Pop() -} - -func TestModuleBase_PushPopDir(t *testing.T) { - t.Parallel() - - m := new(ModuleBase) - m.InitContext(Context(InitMockDebugger(), Parameters{}, "foo")) - m.PushDir("bar") - assert.Equal(t, "foo/bar", m.OutputPath()) - m.PopDir() - assert.Equal(t, "foo", m.OutputPath()) -} - -func TestModuleBase_Artifacts(t *testing.T) { - t.Parallel() - - arts := []Artifact{nil, nil, nil} - m := &ModuleBase{artifacts: arts} - assert.Equal(t, arts, m.Artifacts()) - assert.Empty(t, m.Artifacts()) -} - -func TestModuleBase_AddArtifact(t *testing.T) { - t.Parallel() - - m := new(ModuleBase) - assert.Empty(t, m.Artifacts()) - m.AddArtifact(nil, nil) - assert.Len(t, m.Artifacts(), 2) -} - -func TestModuleBase_AddGeneratorFile(t *testing.T) { - t.Parallel() - - m := new(ModuleBase) - m.AddGeneratorFile("foo", "bar") - arts := m.Artifacts() - assert.Len(t, arts, 1) - assert.Equal(t, GeneratorFile{ - Name: "foo", - Contents: "bar", - }, arts[0]) -} - -func TestModuleBase_OverwriteGeneratorFile(t *testing.T) { - t.Parallel() - - m := new(ModuleBase) - m.OverwriteGeneratorFile("foo", "bar") - arts := m.Artifacts() - assert.Len(t, arts, 1) - assert.Equal(t, GeneratorFile{ - Name: "foo", - Contents: "bar", - Overwrite: true, - }, arts[0]) -} - -func TestModuleBase_AddGeneratorTemplateFile(t *testing.T) { - t.Parallel() - - m := new(ModuleBase) - m.AddGeneratorTemplateFile("foo", template.New("bar"), "baz") - arts := m.Artifacts() - assert.Len(t, arts, 1) - assert.Equal(t, GeneratorTemplateFile{ - Name: "foo", - TemplateArtifact: TemplateArtifact{ - Template: template.New("bar"), - Data: "baz", - }, - }, arts[0]) -} - -func TestModuleBase_OverwriteGeneratorTemplateFile(t *testing.T) { - t.Parallel() - - m := new(ModuleBase) - m.OverwriteGeneratorTemplateFile("foo", template.New("bar"), "baz") - arts := m.Artifacts() - assert.Len(t, arts, 1) - assert.Equal(t, GeneratorTemplateFile{ - Name: "foo", - Overwrite: true, - TemplateArtifact: TemplateArtifact{ - Template: template.New("bar"), - Data: "baz", - }, - }, arts[0]) -} - -func TestModuleBase_AddGeneratorAppend(t *testing.T) { - t.Parallel() - - m := new(ModuleBase) - m.AddGeneratorAppend("foo", "bar") - arts := m.Artifacts() - assert.Len(t, arts, 1) - assert.Equal(t, GeneratorAppend{ - FileName: "foo", - Contents: "bar", - }, arts[0]) -} - -func TestModuleBase_AddGeneratorTemplateAppend(t *testing.T) { - t.Parallel() - - m := new(ModuleBase) - m.AddGeneratorTemplateAppend("foo", template.New("bar"), "baz") - arts := m.Artifacts() - assert.Len(t, arts, 1) - assert.Equal(t, GeneratorTemplateAppend{ - FileName: "foo", - TemplateArtifact: TemplateArtifact{ - Template: template.New("bar"), - Data: "baz", - }, - }, arts[0]) -} - -func TestModuleBase_AddGeneratorInjection(t *testing.T) { - t.Parallel() - - m := new(ModuleBase) - m.AddGeneratorInjection("foo", "bar", "baz") - arts := m.Artifacts() - assert.Len(t, arts, 1) - assert.Equal(t, GeneratorInjection{ - FileName: "foo", - InsertionPoint: "bar", - Contents: "baz", - }, arts[0]) -} - -func TestModuleBase_AddGeneratorTemplateInjection(t *testing.T) { - t.Parallel() - - m := new(ModuleBase) - m.AddGeneratorTemplateInjection("foo", "bar", template.New("fizz"), "buzz") - arts := m.Artifacts() - assert.Len(t, arts, 1) - assert.Equal(t, GeneratorTemplateInjection{ - FileName: "foo", - InsertionPoint: "bar", - TemplateArtifact: TemplateArtifact{ - Template: template.New("fizz"), - Data: "buzz", - }, - }, arts[0]) -} - -func TestModuleBase_AddCustomFile(t *testing.T) { - t.Parallel() - - m := new(ModuleBase) - m.AddCustomFile("foo", "bar", 0765) - arts := m.Artifacts() - assert.Len(t, arts, 1) - assert.Equal(t, CustomFile{ - Name: "foo", - Contents: "bar", - Perms: 0765, - }, arts[0]) -} - -func TestModuleBase_OverwriteCustomFile(t *testing.T) { - t.Parallel() - - m := new(ModuleBase) - m.OverwriteCustomFile("foo", "bar", 0765) - arts := m.Artifacts() - assert.Len(t, arts, 1) - assert.Equal(t, CustomFile{ - Name: "foo", - Contents: "bar", - Overwrite: true, - Perms: 0765, - }, arts[0]) -} - -func TestModuleBase_AddCustomTemplateFile(t *testing.T) { - t.Parallel() - - m := new(ModuleBase) - m.AddCustomTemplateFile("foo", template.New("bar"), "baz", 0765) - arts := m.Artifacts() - assert.Len(t, arts, 1) - assert.Equal(t, CustomTemplateFile{ - Name: "foo", - Perms: 0765, - TemplateArtifact: TemplateArtifact{ - Template: template.New("bar"), - Data: "baz", - }, - }, arts[0]) -} - -func TestModuleBase_OverwriteCustomTemplateFile(t *testing.T) { - t.Parallel() - - m := new(ModuleBase) - m.OverwriteCustomTemplateFile("foo", template.New("bar"), "baz", 0765) - arts := m.Artifacts() - assert.Len(t, arts, 1) - assert.Equal(t, CustomTemplateFile{ - Name: "foo", - Overwrite: true, - Perms: 0765, - TemplateArtifact: TemplateArtifact{ - Template: template.New("bar"), - Data: "baz", - }, - }, arts[0]) -} - -func TestModuleBase_AddError(t *testing.T) { - t.Parallel() - - m := new(ModuleBase) - m.AddError("bohoo") - arts := m.Artifacts() - assert.Len(t, arts, 1) - assert.Equal(t, GeneratorError{Message: "bohoo"}, arts[0]) -} diff --git a/v2/name.go b/v2/name.go deleted file mode 100644 index 54ad386..0000000 --- a/v2/name.go +++ /dev/null @@ -1,185 +0,0 @@ -package pgs - -import ( - "bytes" - "fmt" - "strings" - "unicode" - "unicode/utf8" - - "path/filepath" -) - -// A Name describes an identifier of an Entity (Message, Field, Enum, Service, -// Field). It can be converted to multiple forms using the provided helper -// methods, or a custom transform can be used to modify its behavior. -type Name string - -// String satisfies the strings.Stringer interface. -func (n Name) String() string { return string(n) } - -// UpperCamelCase converts Name n to upper camelcase, where each part is -// title-cased and concatenated with no separator. -func (n Name) UpperCamelCase() Name { return n.Transform(strings.Title, strings.Title, "") } - -// LowerCamelCase converts Name n to lower camelcase, where each part is -// title-cased and concatenated with no separator except the first which is -// lower-cased. -func (n Name) LowerCamelCase() Name { return n.Transform(strings.Title, strings.ToLower, "") } - -// ScreamingSnakeCase converts Name n to screaming-snake-case, where each part -// is all-caps and concatenated with underscores. -func (n Name) ScreamingSnakeCase() Name { return n.Transform(strings.ToUpper, strings.ToUpper, "_") } - -// LowerSnakeCase converts Name n to lower-snake-case, where each part is -// lower-cased and concatenated with underscores. -func (n Name) LowerSnakeCase() Name { return n.Transform(strings.ToLower, strings.ToLower, "_") } - -// UpperSnakeCase converts Name n to upper-snake-case, where each part is -// title-cased and concatenated with underscores. -func (n Name) UpperSnakeCase() Name { return n.Transform(strings.Title, strings.Title, "_") } - -// SnakeCase converts Name n to snake-case, where each part preserves its -// capitalization and concatenated with underscores. -func (n Name) SnakeCase() Name { return n.Transform(ID, ID, "_") } - -// LowerDotNotation converts Name n to lower dot notation, where each part is -// lower-cased and concatenated with periods. -func (n Name) LowerDotNotation() Name { return n.Transform(strings.ToLower, strings.ToLower, ".") } - -// UpperDotNotation converts Name n to upper dot notation, where each part is -// title-cased and concatenated with periods. -func (n Name) UpperDotNotation() Name { return n.Transform(strings.Title, strings.Title, ".") } - -// Split breaks apart Name n into its constituent components. Precedence -// follows dot notation, then underscores (excluding underscore prefixes), then -// camelcase. Numbers are treated as standalone components. -func (n Name) Split() (parts []string) { - ns := string(n) - - switch { - case ns == "": - return []string{""} - case strings.LastIndex(ns, ".") >= 0: - return strings.Split(ns, ".") - case strings.LastIndex(ns, "_") > 0: // leading underscore does not count - parts = strings.Split(ns, "_") - if parts[0] == "" { - parts[1] = "_" + parts[1] - return parts[1:] - } - return - default: // camelCase - buf := &bytes.Buffer{} - var capt, lodash, num bool - for _, r := range ns { - uc := unicode.IsUpper(r) || unicode.IsTitle(r) - dg := unicode.IsDigit(r) - - if r == '_' && buf.Len() == 0 && len(parts) == 0 { - lodash = true - } - - if uc && !capt && buf.Len() > 0 && !lodash { // new upper letter - parts = append(parts, buf.String()) - buf.Reset() - } else if dg && !num && buf.Len() > 0 && !lodash { // new digit - parts = append(parts, buf.String()) - buf.Reset() - } else if !uc && capt && buf.Len() > 1 { // upper to lower - if ss := buf.String(); len(ss) > 1 && - (len(ss) != 2 || ss[0] != '_') { - pr, _ := utf8.DecodeLastRuneInString(ss) - parts = append(parts, strings.TrimSuffix(ss, string(pr))) - buf.Reset() - buf.WriteRune(pr) - } - } else if !dg && num && buf.Len() >= 1 { - parts = append(parts, buf.String()) - buf.Reset() - } - - num = dg - capt = uc - buf.WriteRune(r) - } - parts = append(parts, buf.String()) - return - } -} - -// NameTransformer is a function that mutates a string. Many of the methods in -// the standard strings package satisfy this signature. -type NameTransformer func(string) string - -// ID is a NameTransformer that does not mutate the string. -func ID(s string) string { return s } - -// Chain combines the behavior of two Transformers into one. If multiple -// transformations need to be performed on a Name, this method should be used -// to reduce it to a single transformation before applying. -func (n NameTransformer) Chain(t NameTransformer) NameTransformer { - return func(s string) string { return t(n(s)) } -} - -// Transform applies a transformation to the parts of Name n, returning a new -// Name. Transformer first is applied to the first part, with mod applied to -// all subsequent ones. The parts are then concatenated with the separator sep. -// For optimal efficiency, multiple NameTransformers should be Chained together -// before calling Transform. -func (n Name) Transform(mod, first NameTransformer, sep string) Name { - parts := n.Split() - - for i, p := range parts { - if i == 0 { - parts[i] = first(p) - } else { - parts[i] = mod(p) - } - } - - return Name(strings.Join(parts, sep)) -} - -// A FilePath describes the name of a file or directory. This type simplifies -// path related operations. -type FilePath string - -// JoinPaths is an convenient alias around filepath.Join, to easily create -// FilePath types. -func JoinPaths(elem ...string) FilePath { return FilePath(filepath.Join(elem...)) } - -// String satisfies the strings.Stringer interface. -func (n FilePath) String() string { return string(n) } - -// Dir returns the parent directory of the current FilePath. This method is an -// alias around filepath.Dir. -func (n FilePath) Dir() FilePath { return FilePath(filepath.Dir(n.String())) } - -// Base returns the base of the current FilePath (the last element in the -// path). This method is an alias around filepath.Base. -func (n FilePath) Base() string { return filepath.Base(n.String()) } - -// Ext returns the extension of the current FilePath (starting at and including -// the last '.' in the FilePath). This method is an alias around filepath.Ext. -func (n FilePath) Ext() string { return filepath.Ext(n.String()) } - -// BaseName returns the Base of the current FilePath without Ext. -func (n FilePath) BaseName() string { return strings.TrimSuffix(n.Base(), n.Ext()) } - -// SetExt returns a new FilePath with the extension replaced with ext. -func (n FilePath) SetExt(ext string) FilePath { return n.SetBase(n.BaseName() + ext) } - -// SetBase returns a new FilePath with the base element replaced with base. -func (n FilePath) SetBase(base string) FilePath { return n.Dir().Push(base) } - -// Pop returns a new FilePath with the last element removed. Pop is an alias -// for the Dir method. -func (n FilePath) Pop() FilePath { return n.Dir() } - -// Push returns a new FilePath with elem added to the end -func (n FilePath) Push(elem string) FilePath { return JoinPaths(n.String(), elem) } - -func fullyQualifiedName(p, e Entity) string { - return fmt.Sprintf("%s.%s", p.FullyQualifiedName(), e.Name()) -} diff --git a/v2/name_test.go b/v2/name_test.go deleted file mode 100644 index 0ed1f02..0000000 --- a/v2/name_test.go +++ /dev/null @@ -1,314 +0,0 @@ -package pgs - -import ( - "fmt" - "testing" - - "strings" - - "github.com/stretchr/testify/assert" -) - -func TestName_Split(t *testing.T) { - t.Parallel() - - tests := []struct { - in string - parts []string - }{ - // camel case - {"fooBar", []string{"foo", "Bar"}}, - {"FooBar", []string{"Foo", "Bar"}}, - {"myJSON", []string{"my", "JSON"}}, - {"JSONStringFooBar", []string{"JSON", "String", "Foo", "Bar"}}, - {"myJSONString", []string{"my", "JSON", "String"}}, - - // snake case - {"FOO_BAR", []string{"FOO", "BAR"}}, - {"foo_bar_baz", []string{"foo", "bar", "baz"}}, - {"Foo_Bar", []string{"Foo", "Bar"}}, - {"JSONString_Foo_Bar", []string{"JSONString", "Foo", "Bar"}}, - - // dot notation - {"foo.bar", []string{"foo", "bar"}}, - {".foo.bar", []string{"", "foo", "bar"}}, - {".JSONString.Foo.Bar", []string{"", "JSONString", "Foo", "Bar"}}, - - // leading underscore - {"_Privatish", []string{"_Privatish"}}, - {"_privatish", []string{"_privatish"}}, - {"_foo_bar", []string{"_foo", "bar"}}, - {"_Foo_Bar", []string{"_Foo", "Bar"}}, - {"_JSON_String", []string{"_JSON", "String"}}, - {"_JString", []string{"_J", "String"}}, - {"__Double", []string{"_", "Double"}}, - - // numbers - {"abc123", []string{"abc", "123"}}, - {"123def", []string{"123", "def"}}, - {"abc1def", []string{"abc", "1", "def"}}, - {"ABC1DEF", []string{"ABC", "1", "DEF"}}, - - // empty - {"", []string{""}}, - } - - for _, test := range tests { - tc := test - t.Run(tc.in, func(t *testing.T) { - t.Parallel() - assert.Equal(t, tc.parts, Name(tc.in).Split()) - }) - } -} - -func TestName(t *testing.T) { - t.Parallel() - - tests := []struct { - in []string - ucc string - lcc string - ssc string - lsc string - usc string - ldn string - udn string - }{ - { - in: []string{"fooBar", "FooBar", "foo_bar", "Foo_Bar", "foo_Bar", "foo.Bar", "Foo.Bar"}, - ucc: "FooBar", - lcc: "fooBar", - ssc: "FOO_BAR", - lsc: "foo_bar", - usc: "Foo_Bar", - ldn: "foo.bar", - udn: "Foo.Bar", - }, - { - in: []string{"JSONString", "JSON_String", "JSON_string", "JSON.string"}, - ucc: "JSONString", - lcc: "jsonString", - ssc: "JSON_STRING", - lsc: "json_string", - usc: "JSON_String", - ldn: "json.string", - udn: "JSON.String", - }, - { - in: []string{"myJSON", "my_JSON", "My_JSON", "my.JSON"}, - ucc: "MyJSON", - lcc: "myJSON", - ssc: "MY_JSON", - lsc: "my_json", - usc: "My_JSON", - ldn: "my.json", - udn: "My.JSON", - }, - } - - for _, test := range tests { - tc := test - for _, in := range tc.in { - n := Name(in) - t.Run(string(n), func(t *testing.T) { - t.Parallel() - - t.Run("UpperCamelCase", func(t *testing.T) { - t.Parallel() - assert.Equal(t, tc.ucc, n.UpperCamelCase().String()) - }) - - t.Run("lowerCamelCase", func(t *testing.T) { - t.Parallel() - assert.Equal(t, tc.lcc, n.LowerCamelCase().String()) - }) - - t.Run("SCREAMING_SNAKE_CASE", func(t *testing.T) { - t.Parallel() - assert.Equal(t, tc.ssc, n.ScreamingSnakeCase().String()) - }) - - t.Run("lower_snake_case", func(t *testing.T) { - t.Parallel() - assert.Equal(t, tc.lsc, n.LowerSnakeCase().String()) - }) - - t.Run("Upper_Snake_Case", func(t *testing.T) { - t.Parallel() - assert.Equal(t, tc.usc, n.UpperSnakeCase().String()) - }) - - t.Run("lower.dot.notation", func(t *testing.T) { - t.Parallel() - assert.Equal(t, tc.ldn, n.LowerDotNotation().String()) - }) - - t.Run("Upper.Dot.Notation", func(t *testing.T) { - t.Parallel() - assert.Equal(t, tc.udn, n.UpperDotNotation().String()) - }) - }) - } - } -} - -func TestNameTransformer_Chain(t *testing.T) { - t.Parallel() - - nt := NameTransformer(strings.ToUpper) - nt = nt.Chain(func(s string) string { return "_" + s }) - - assert.Equal(t, "_FOO", nt("foo")) -} - -func TestFilePath(t *testing.T) { - t.Parallel() - - fp := FilePath("alpha/beta/gamma.proto") - assert.Equal(t, "alpha/beta/gamma.proto", fp.String()) - assert.Equal(t, "alpha/beta", fp.Dir().String()) - assert.Equal(t, "gamma.proto", fp.Base()) - assert.Equal(t, ".proto", fp.Ext()) - assert.Equal(t, "gamma", fp.BaseName()) - assert.Equal(t, "alpha/beta/gamma.foo", fp.SetExt(".foo").String()) - assert.Equal(t, "alpha/beta/delta.bar", fp.SetBase("delta.bar").String()) - assert.Equal(t, "alpha/beta", fp.Pop().String()) - assert.Equal(t, "alpha/beta/delta", fp.Dir().Push("delta").String()) -} - -func ExampleName_UpperCamelCase() { - names := []string{ - "foo_bar", - "myJSON", - "PDFTemplate", - } - - for _, n := range names { - fmt.Println(Name(n).UpperCamelCase()) - } - - // Output: - // FooBar - // MyJSON - // PDFTemplate -} - -func ExampleName_LowerCamelCase() { - names := []string{ - "foo_bar", - "myJSON", - "PDFTemplate", - } - - for _, n := range names { - fmt.Println(Name(n).LowerCamelCase()) - } - - // Output: - // fooBar - // myJSON - // pdfTemplate -} - -func ExampleName_ScreamingSnakeCase() { - names := []string{ - "foo_bar", - "myJSON", - "PDFTemplate", - } - - for _, n := range names { - fmt.Println(Name(n).ScreamingSnakeCase()) - } - - // Output: - // FOO_BAR - // MY_JSON - // PDF_TEMPLATE -} - -func ExampleName_LowerSnakeCase() { - names := []string{ - "foo_bar", - "myJSON", - "PDFTemplate", - } - - for _, n := range names { - fmt.Println(Name(n).LowerSnakeCase()) - } - - // Output: - // foo_bar - // my_json - // pdf_template -} - -func ExampleName_UpperSnakeCase() { - names := []string{ - "foo_bar", - "myJSON", - "PDFTemplate", - } - - for _, n := range names { - fmt.Println(Name(n).UpperSnakeCase()) - } - - // Output: - // Foo_Bar - // My_JSON - // PDF_Template -} - -func ExampleName_SnakeCase() { - names := []string{ - "foo_bar", - "myJSON", - "PDFTemplate", - } - - for _, n := range names { - fmt.Println(Name(n).SnakeCase()) - } - - // Output: - // foo_bar - // my_JSON - // PDF_Template -} - -func ExampleName_LowerDotNotation() { - names := []string{ - "foo_bar", - "myJSON", - "PDFTemplate", - } - - for _, n := range names { - fmt.Println(Name(n).LowerDotNotation()) - } - - // Output: - // foo.bar - // my.json - // pdf.template -} - -func ExampleName_UpperDotNotation() { - names := []string{ - "foo_bar", - "myJSON", - "PDFTemplate", - } - - for _, n := range names { - fmt.Println(Name(n).UpperDotNotation()) - } - - // Output: - // Foo.Bar - // My.JSON - // PDF.Template -} diff --git a/v2/node.go b/v2/node.go deleted file mode 100644 index cff6f58..0000000 --- a/v2/node.go +++ /dev/null @@ -1,76 +0,0 @@ -package pgs - -// Node represents any member of the proto descriptor AST. Typically, the -// highest level Node is the Package. -type Node interface { - accept(Visitor) error -} - -// A Visitor exposes methods to walk an AST Node and its children in a depth- -// first manner. If the returned Visitor v is non-nil, it will be used to -// descend into the children of the current node. If nil, those children will -// be skipped. Any error returned will immediately halt execution. -type Visitor interface { - VisitPackage(Package) (v Visitor, err error) - VisitFile(File) (v Visitor, err error) - VisitMessage(Message) (v Visitor, err error) - VisitEnum(Enum) (v Visitor, err error) - VisitEnumValue(EnumValue) (v Visitor, err error) - VisitField(Field) (v Visitor, err error) - VisitExtension(Extension) (v Visitor, err error) - VisitOneOf(OneOf) (v Visitor, err error) - VisitService(Service) (v Visitor, err error) - VisitMethod(Method) (v Visitor, err error) -} - -// Walk applies a depth-first visitor pattern with v against Node n. -func Walk(v Visitor, n Node) error { return n.accept(v) } - -type nilVisitor struct{} - -// NilVisitor returns a Visitor that always responds with (nil, nil) for all -// methods. This is useful as an anonymous embedded struct to satisfy the -// Visitor interface for implementations that don't require visiting every Node -// type. NilVisitor should be used over PassThroughVisitor if short-circuiting -// behavior is desired. -func NilVisitor() Visitor { return nilVisitor{} } - -func (nv nilVisitor) VisitPackage(p Package) (v Visitor, err error) { return nil, nil } -func (nv nilVisitor) VisitFile(f File) (v Visitor, err error) { return nil, nil } -func (nv nilVisitor) VisitMessage(m Message) (v Visitor, err error) { return nil, nil } -func (nv nilVisitor) VisitEnum(e Enum) (v Visitor, err error) { return nil, nil } -func (nv nilVisitor) VisitEnumValue(e EnumValue) (v Visitor, err error) { return nil, nil } -func (nv nilVisitor) VisitField(f Field) (v Visitor, err error) { return nil, nil } -func (nv nilVisitor) VisitExtension(e Extension) (v Visitor, err error) { return nil, nil } -func (nv nilVisitor) VisitOneOf(o OneOf) (v Visitor, err error) { return nil, nil } -func (nv nilVisitor) VisitService(s Service) (v Visitor, err error) { return nil, nil } -func (nv nilVisitor) VisitMethod(m Method) (v Visitor, err error) { return nil, nil } - -var _ Visitor = nilVisitor{} - -type passVisitor struct { - v Visitor -} - -// PassThroughVisitor returns a Visitor that always responds with (v, nil) for -// all methods. This is useful as an anonymous embedded struct to satisfy the -// Visitor interface for implementations that need access to deep child nodes -// (eg, EnumValue, Field, Method) without implementing each method of the -// interface explicitly. -func PassThroughVisitor(v Visitor) Visitor { return passVisitor{v: v} } - -func (pv passVisitor) VisitPackage(Package) (v Visitor, err error) { return pv.v, nil } -func (pv passVisitor) VisitFile(File) (v Visitor, err error) { return pv.v, nil } -func (pv passVisitor) VisitMessage(Message) (v Visitor, err error) { return pv.v, nil } -func (pv passVisitor) VisitEnum(Enum) (v Visitor, err error) { return pv.v, nil } -func (pv passVisitor) VisitEnumValue(EnumValue) (v Visitor, err error) { return pv.v, nil } -func (pv passVisitor) VisitField(Field) (v Visitor, err error) { return pv.v, nil } -func (pv passVisitor) VisitExtension(Extension) (v Visitor, err error) { return pv.v, nil } -func (pv passVisitor) VisitOneOf(OneOf) (v Visitor, err error) { return pv.v, nil } -func (pv passVisitor) VisitService(Service) (v Visitor, err error) { return pv.v, nil } -func (pv passVisitor) VisitMethod(Method) (v Visitor, err error) { return pv.v, nil } - -var ( - _ Visitor = nilVisitor{} - _ Visitor = passVisitor{} -) diff --git a/v2/node_nilvisitor_test.go b/v2/node_nilvisitor_test.go deleted file mode 100644 index dfbe871..0000000 --- a/v2/node_nilvisitor_test.go +++ /dev/null @@ -1,64 +0,0 @@ -package pgs - -import ( - "fmt" - - "github.com/golang/protobuf/proto" - "github.com/golang/protobuf/protoc-gen-go/descriptor" -) - -type enumPrinter struct { - Visitor -} - -func EnumPrinter() Visitor { return enumPrinter{NilVisitor()} } - -func (p enumPrinter) VisitMessage(m Message) (Visitor, error) { return p, nil } - -func (p enumPrinter) VisitEnum(e Enum) (Visitor, error) { - fmt.Println(e.Name()) - return nil, nil -} - -func ExampleNilVisitor() { - n := enumNode() - p := EnumPrinter() - - if err := Walk(p, n); err != nil { - panic(err) - } - - // Output: - // Bar - // Foo -} - -func enumNode() Node { - // simulating the following proto file: - // - // syntax="proto3"; - // - // package fizz; - // - // message Gadget { - // - // enum Bar { - // // ... - // } - // - // message Gizmo { - // enum Foo { - // // ... - // } - // } - // } - - sm := &msg{} - sm.addEnum(&enum{desc: &descriptor.EnumDescriptorProto{Name: proto.String("Foo")}}) - - m := &msg{} - m.addMessage(sm) - m.addEnum(&enum{desc: &descriptor.EnumDescriptorProto{Name: proto.String("Bar")}}) - - return m -} diff --git a/v2/node_passvisitor_test.go b/v2/node_passvisitor_test.go deleted file mode 100644 index 8c4a313..0000000 --- a/v2/node_passvisitor_test.go +++ /dev/null @@ -1,67 +0,0 @@ -package pgs - -import ( - "fmt" - - "github.com/golang/protobuf/proto" - "github.com/golang/protobuf/protoc-gen-go/descriptor" -) - -type fieldPrinter struct { - Visitor -} - -func FieldPrinter() Visitor { - p := &fieldPrinter{} - p.Visitor = PassThroughVisitor(p) - return p -} - -func (p fieldPrinter) VisitField(f Field) (Visitor, error) { - fmt.Println(f.Name()) - return nil, nil -} - -func ExamplePassThroughVisitor() { - n := fieldNode() - p := FieldPrinter() - - if err := Walk(p, n); err != nil { - panic(err) - } - - // Output: - // Foo - // Bar -} - -func fieldNode() Node { - // simulating the following proto file: - // - // syntax="proto3"; - // - // package fizz; - // - // message Gadget { - // string Bar = 1; - // - // message Gizmo { - // int Foo = 1; - // } - // } - - sm := &msg{} - sm.addField(&field{desc: &descriptor.FieldDescriptorProto{Name: proto.String("Foo")}}) - - m := &msg{} - m.addMessage(sm) - m.addField(&field{desc: &descriptor.FieldDescriptorProto{Name: proto.String("Bar")}}) - - f := &file{} - f.addMessage(m) - - p := &pkg{} - p.addFile(f) - - return p -} diff --git a/v2/node_test.go b/v2/node_test.go deleted file mode 100644 index 017bd63..0000000 --- a/v2/node_test.go +++ /dev/null @@ -1,186 +0,0 @@ -package pgs - -import ( - "errors" - "testing" - - "github.com/stretchr/testify/assert" -) - -type mockNode struct { - Node - - a func(Visitor) error -} - -func (n mockNode) accept(v Visitor) error { return n.a(v) } - -func TestWalk(t *testing.T) { - t.Parallel() - - e := errors.New("TestWalk") - type mockVisitor struct{ Visitor } - mv := mockVisitor{} - n := mockNode{} - - n.a = func(v Visitor) error { - assert.Equal(t, mv, v) - return e - } - - assert.Equal(t, e, Walk(mv, n)) -} - -func TestNilVisitor(t *testing.T) { - t.Parallel() - - nv := NilVisitor() - - v, err := nv.VisitPackage(&pkg{}) - assert.Nil(t, v) - assert.NoError(t, err) - - v, err = nv.VisitFile(&file{}) - assert.Nil(t, v) - assert.NoError(t, err) - - v, err = nv.VisitService(&service{}) - assert.Nil(t, v) - assert.NoError(t, err) - - v, err = nv.VisitMethod(&method{}) - assert.Nil(t, v) - assert.NoError(t, err) - - v, err = nv.VisitEnum(&enum{}) - assert.Nil(t, v) - assert.NoError(t, err) - - v, err = nv.VisitEnumValue(&enumVal{}) - assert.Nil(t, v) - assert.NoError(t, err) - - v, err = nv.VisitMessage(&msg{}) - assert.Nil(t, v) - assert.NoError(t, err) - - v, err = nv.VisitField(&field{}) - assert.Nil(t, v) - assert.NoError(t, err) - - v, err = nv.VisitExtension(&ext{}) - assert.Nil(t, v) - assert.NoError(t, err) - - v, err = nv.VisitOneOf(&oneof{}) - assert.Nil(t, v) - assert.NoError(t, err) -} - -func TestPassThroughVisitor(t *testing.T) { - t.Parallel() - - nv := NilVisitor() - pv := PassThroughVisitor(nv) - - v, err := pv.VisitPackage(&pkg{}) - assert.Equal(t, nv, v) - assert.NoError(t, err) - - v, err = pv.VisitFile(&file{}) - assert.Equal(t, nv, v) - assert.NoError(t, err) - - v, err = pv.VisitService(&service{}) - assert.Equal(t, nv, v) - assert.NoError(t, err) - - v, err = pv.VisitMethod(&method{}) - assert.Equal(t, nv, v) - assert.NoError(t, err) - - v, err = pv.VisitEnum(&enum{}) - assert.Equal(t, nv, v) - assert.NoError(t, err) - - v, err = pv.VisitEnumValue(&enumVal{}) - assert.Equal(t, nv, v) - assert.NoError(t, err) - - v, err = pv.VisitMessage(&msg{}) - assert.Equal(t, nv, v) - assert.NoError(t, err) - - v, err = pv.VisitField(&field{}) - assert.Equal(t, nv, v) - assert.NoError(t, err) - - v, err = pv.VisitExtension(&ext{}) - assert.Equal(t, nv, v) - assert.NoError(t, err) - - v, err = pv.VisitOneOf(&oneof{}) - assert.Equal(t, nv, v) - assert.NoError(t, err) -} - -type mockVisitor struct { - v Visitor - err error - - pkg, file, message, enum, enumvalue, extension, field, oneof, service, method int -} - -func (v *mockVisitor) VisitPackage(p Package) (w Visitor, err error) { - v.pkg++ - return v.v, v.err -} - -func (v *mockVisitor) VisitFile(f File) (w Visitor, err error) { - v.file++ - return v.v, v.err -} - -func (v *mockVisitor) VisitMessage(m Message) (w Visitor, err error) { - v.message++ - return v.v, v.err -} - -func (v *mockVisitor) VisitEnum(e Enum) (w Visitor, err error) { - v.enum++ - return v.v, v.err -} - -func (v *mockVisitor) VisitEnumValue(ev EnumValue) (w Visitor, err error) { - v.enumvalue++ - return v.v, v.err -} - -func (v *mockVisitor) VisitField(f Field) (w Visitor, err error) { - v.field++ - return v.v, v.err -} - -func (v *mockVisitor) VisitExtension(e Extension) (w Visitor, err error) { - v.extension++ - return v.v, v.err -} - -func (v *mockVisitor) VisitOneOf(o OneOf) (w Visitor, err error) { - v.oneof++ - return v.v, v.err -} - -func (v *mockVisitor) VisitService(s Service) (w Visitor, err error) { - v.service++ - return v.v, v.err -} - -func (v *mockVisitor) VisitMethod(m Method) (w Visitor, err error) { - v.method++ - return v.v, v.err -} - -func (v *mockVisitor) Reset() { - *v = mockVisitor{v: v.v} -} diff --git a/v2/oneof.go b/v2/oneof.go deleted file mode 100644 index 34970af..0000000 --- a/v2/oneof.go +++ /dev/null @@ -1,103 +0,0 @@ -package pgs - -import ( - "github.com/golang/protobuf/proto" - "github.com/golang/protobuf/protoc-gen-go/descriptor" -) - -// OneOf describes a OneOf block within a Message. OneOfs behave like C++ -// unions, where only one of the contained fields will exist on the Message. -type OneOf interface { - Entity - - // Descriptor returns the underlying proto descriptor for this OneOf - Descriptor() *descriptor.OneofDescriptorProto - - // Message returns the parent message for this OneOf. - Message() Message - - // Fields returns all fields contained within this OneOf. - Fields() []Field - - // IsSynthetic returns true if this is a proto3 synthetic oneof. - // See: https://github.com/protocolbuffers/protobuf/blob/v3.17.0/docs/field_presence.md - IsSynthetic() bool - - setMessage(m Message) - addField(f Field) -} - -type oneof struct { - desc *descriptor.OneofDescriptorProto - msg Message - flds []Field - fqn string - - info SourceCodeInfo -} - -func (o *oneof) accept(v Visitor) (err error) { - if v == nil { - return - } - - _, err = v.VisitOneOf(o) - return -} - -func (o *oneof) Name() Name { return Name(o.desc.GetName()) } -func (o *oneof) FullyQualifiedName() string { return o.fqn } -func (o *oneof) Syntax() Syntax { return o.msg.Syntax() } -func (o *oneof) Package() Package { return o.msg.Package() } -func (o *oneof) File() File { return o.msg.File() } -func (o *oneof) BuildTarget() bool { return o.msg.BuildTarget() } -func (o *oneof) SourceCodeInfo() SourceCodeInfo { return o.info } -func (o *oneof) Descriptor() *descriptor.OneofDescriptorProto { return o.desc } -func (o *oneof) Message() Message { return o.msg } -func (o *oneof) setMessage(m Message) { o.msg = m } - -func (o *oneof) IsSynthetic() bool { - return o.Syntax() == Proto3 && - len(o.flds) == 1 && - !o.flds[0].InRealOneOf() -} - -func (o *oneof) Imports() (i []File) { - // Mapping for avoiding duplicate entries - mp := make(map[string]File, len(o.flds)) - for _, f := range o.flds { - for _, imp := range f.Imports() { - mp[imp.File().Name().String()] = imp - } - } - for _, f := range mp { - i = append(i, f) - } - return -} - -func (o *oneof) Extension(desc *proto.ExtensionDesc, ext interface{}) (ok bool, err error) { - return extension(o.desc.GetOptions(), desc, &ext) -} - -func (o *oneof) Fields() []Field { - f := make([]Field, len(o.flds)) - copy(f, o.flds) - return f -} - -func (o *oneof) addField(f Field) { - f.setOneOf(o) - o.flds = append(o.flds, f) -} - -func (o *oneof) childAtPath(path []int32) Entity { - if len(path) == 0 { - return o - } - return nil -} - -func (o *oneof) addSourceCodeInfo(info SourceCodeInfo) { o.info = info } - -var _ OneOf = (*oneof)(nil) diff --git a/v2/oneof_test.go b/v2/oneof_test.go deleted file mode 100644 index d0eef9c..0000000 --- a/v2/oneof_test.go +++ /dev/null @@ -1,179 +0,0 @@ -package pgs - -import ( - "testing" - - "errors" - - "github.com/golang/protobuf/proto" - "github.com/golang/protobuf/protoc-gen-go/descriptor" - "github.com/stretchr/testify/assert" -) - -func TestOneof_Name(t *testing.T) { - t.Parallel() - - o := &oneof{desc: &descriptor.OneofDescriptorProto{Name: proto.String("foo")}} - assert.Equal(t, "foo", o.Name().String()) -} - -func TestOneOf_FullyQualifiedName(t *testing.T) { - t.Parallel() - - o := &oneof{fqn: "one_of"} - assert.Equal(t, o.fqn, o.FullyQualifiedName()) -} - -func TestOneof_Syntax(t *testing.T) { - t.Parallel() - - m := dummyMsg() - o := &oneof{} - m.addOneOf(o) - - assert.Equal(t, m.Syntax(), o.Syntax()) -} - -func TestOneof_Package(t *testing.T) { - t.Parallel() - - m := dummyMsg() - o := &oneof{} - m.addOneOf(o) - - assert.NotNil(t, o.Package()) - assert.Equal(t, m.Package(), o.Package()) -} - -func TestOneof_File(t *testing.T) { - t.Parallel() - - m := dummyMsg() - o := &oneof{} - m.addOneOf(o) - - assert.NotNil(t, o.File()) - assert.Equal(t, m.File(), o.File()) -} - -func TestOneof_BuildTarget(t *testing.T) { - t.Parallel() - - m := dummyMsg() - o := &oneof{} - m.addOneOf(o) - - assert.False(t, o.BuildTarget()) - m.setParent(&file{buildTarget: true}) - assert.True(t, o.BuildTarget()) -} - -func TestOneof_Descriptor(t *testing.T) { - t.Parallel() - - o := &oneof{desc: &descriptor.OneofDescriptorProto{}} - - assert.Equal(t, o.desc, o.Descriptor()) -} - -func TestOneof_Message(t *testing.T) { - t.Parallel() - - m := dummyMsg() - o := &oneof{} - m.addOneOf(o) - - assert.Equal(t, m, o.Message()) -} - -func TestOneof_Imports(t *testing.T) { - t.Parallel() - - o := &oneof{} - assert.Empty(t, o.Imports()) - - o.addField(&mockField{i: []File{&file{}, &file{}}, Field: &field{}}) - assert.Len(t, o.Imports(), 1) - - f := &file{desc: &descriptor.FileDescriptorProto{ - Name: proto.String("foobar"), - }} - o.addField(&mockField{i: []File{f}, Field: &field{}}) - assert.Len(t, o.Imports(), 2) -} - -func TestOneof_Extension(t *testing.T) { - // cannot be parallel - - o := &oneof{desc: &descriptor.OneofDescriptorProto{}} - assert.NotPanics(t, func() { o.Extension(nil, nil) }) -} - -func TestOneof_Fields(t *testing.T) { - t.Parallel() - - o := &oneof{} - assert.Empty(t, o.Fields()) - - o.addField(&field{}) - assert.Len(t, o.Fields(), 1) -} - -func TestOneof_IsSynthetic(t *testing.T) { - t.Parallel() - - o := &oneof{msg: &msg{parent: dummyFile()}} - assert.False(t, o.IsSynthetic()) - - o.flds = []Field{dummyField()} - o.flds[0].setOneOf(o) - assert.False(t, o.IsSynthetic()) - - o.flds = []Field{dummyOneOfField(true)} - assert.True(t, o.IsSynthetic()) -} - -func TestOneof_Accept(t *testing.T) { - t.Parallel() - - o := &oneof{} - assert.NoError(t, o.accept(nil)) - - v := &mockVisitor{err: errors.New("")} - assert.Error(t, o.accept(v)) - assert.Equal(t, 1, v.oneof) -} - -func TestOneof_ChildAtPath(t *testing.T) { - t.Parallel() - - o := &oneof{} - assert.Equal(t, o, o.childAtPath(nil)) - assert.Nil(t, o.childAtPath([]int32{1})) -} - -type mockOneOf struct { - OneOf - i []File - m Message - err error -} - -func (o *mockOneOf) Imports() []File { return o.i } - -func (o *mockOneOf) setMessage(m Message) { o.m = m } - -func (o *mockOneOf) accept(v Visitor) error { - _, err := v.VisitOneOf(o) - if o.err != nil { - return o.err - } - return err -} - -func dummyOneof() *oneof { - m := dummyMsg() - o := &oneof{desc: &descriptor.OneofDescriptorProto{Name: proto.String("oneof")}} - m.addOneOf(o) - return o -} diff --git a/v2/package.go b/v2/package.go deleted file mode 100644 index e6f6b28..0000000 --- a/v2/package.go +++ /dev/null @@ -1,58 +0,0 @@ -package pgs - -import "github.com/golang/protobuf/protoc-gen-go/descriptor" - -// Package is a container that encapsulates all the files under a single -// package namespace. -type Package interface { - Node - - // The name of the proto package. - ProtoName() Name - - // All the files loaded for this Package - Files() []File - - addFile(f File) - - setComments(c string) -} - -type pkg struct { - fd *descriptor.FileDescriptorProto - files []File - - comments string -} - -func (p *pkg) ProtoName() Name { return Name(p.fd.GetPackage()) } -func (p *pkg) Comments() string { return p.comments } - -func (p *pkg) Files() []File { return p.files } - -func (p *pkg) accept(v Visitor) (err error) { - if v == nil { - return nil - } - - if v, err = v.VisitPackage(p); err != nil || v == nil { - return - } - - for _, f := range p.Files() { - if err = f.accept(v); err != nil { - return - } - } - - return -} - -func (p *pkg) addFile(f File) { - f.setPackage(p) - p.files = append(p.files, f) -} - -func (p *pkg) setComments(comments string) { - p.comments = comments -} diff --git a/v2/package_test.go b/v2/package_test.go deleted file mode 100644 index b3b60fd..0000000 --- a/v2/package_test.go +++ /dev/null @@ -1,88 +0,0 @@ -package pgs - -import ( - "testing" - - "github.com/golang/protobuf/proto" - "github.com/golang/protobuf/protoc-gen-go/descriptor" - - "errors" - - "github.com/stretchr/testify/assert" -) - -func TestPkg_ProtoName(t *testing.T) { - t.Parallel() - - p := dummyPkg() - assert.Equal(t, p.fd.GetPackage(), p.ProtoName().String()) -} - -func TestPkg_Files(t *testing.T) { - t.Parallel() - - p := &pkg{} - assert.Empty(t, p.Files()) - - p.addFile(&file{}) - p.addFile(&file{}) - p.addFile(&file{}) - - assert.Len(t, p.Files(), 3) -} - -func TestPkg_AddFile(t *testing.T) { - t.Parallel() - - p := &pkg{} - f := &file{} - p.addFile(f) - assert.Len(t, p.files, 1) - assert.EqualValues(t, f, p.files[0]) -} - -func TestPkg_Accept(t *testing.T) { - t.Parallel() - - p := &pkg{ - files: []File{&mockFile{}}, - } - assert.Nil(t, p.accept(nil)) - - v := &mockVisitor{} - assert.NoError(t, p.accept(v)) - assert.Equal(t, 1, v.pkg) - assert.Zero(t, v.file) - - v.Reset() - v.err = errors.New("foobar") - assert.EqualError(t, p.accept(v), "foobar") - assert.Equal(t, 1, v.pkg) - assert.Zero(t, v.file) - - v.Reset() - v.v = v - assert.NoError(t, p.accept(v)) - assert.Equal(t, 1, v.pkg) - assert.Equal(t, 1, v.file) - - v.Reset() - p.addFile(&mockFile{err: errors.New("fizzbuzz")}) - assert.EqualError(t, p.accept(v), "fizzbuzz") - assert.Equal(t, 1, v.pkg) - assert.Equal(t, 2, v.file) -} - -func TestPackage_Comments(t *testing.T) { - t.Parallel() - - pkg := dummyPkg() - pkg.setComments("foobar") - assert.Equal(t, "foobar", pkg.Comments()) -} - -func dummyPkg() *pkg { - return &pkg{ - fd: &descriptor.FileDescriptorProto{Package: proto.String("pkg_name")}, - } -} diff --git a/v2/parameters.go b/v2/parameters.go deleted file mode 100644 index 8fd3dbf..0000000 --- a/v2/parameters.go +++ /dev/null @@ -1,179 +0,0 @@ -package pgs - -import ( - "fmt" - "sort" - "strconv" - "strings" - "time" -) - -const outputPathKey = "output_path" - -// Parameters provides a convenience for accessing and modifying the parameters -// passed into the protoc-gen-star plugin. -type Parameters map[string]string - -// ParseParameters converts the raw params string provided by protoc into a -// representative mapping. -func ParseParameters(p string) (params Parameters) { - parts := strings.Split(p, ",") - params = make(map[string]string, len(parts)) - - for _, p = range parts { - if i := strings.Index(p, "="); i < 0 { - params[p] = "" - } else { - params[p[:i]] = p[i+1:] - } - } - - return -} - -// Clone creates an independent copy of Parameters p. -func (p Parameters) Clone() Parameters { - out := make(Parameters, len(p)) - for k, v := range p { - out[k] = v - } - return out -} - -// OutputPath returns the protoc-gen-star special parameter. If not set in the -// execution of protoc, "." is returned, indicating that output is relative to -// the (unknown) output location for sub-plugins or the directory where protoc -// is executed for a Module. Setting "output_path" during the protoc execution -// ensures that Modules can know absolutely where to generate code. -func (p Parameters) OutputPath() string { return p.StrDefault(outputPathKey, ".") } - -// SetOutputPath sets the protoc-gen-star OutputPath parameter. This is useful -// for overriding the behavior of the ImportPath at runtime. -func (p Parameters) SetOutputPath(path string) { p.SetStr(outputPathKey, path) } - -// String satisfies the string.Stringer interface. This method returns p in the -// format it is provided to the protoc execution. Output of this function is -// always stable; parameters are sorted before the string is emitted. -func (p Parameters) String() string { - parts := make([]string, 0, len(p)) - - for k, v := range p { - if v == "" { - parts = append(parts, k) - } else { - parts = append(parts, fmt.Sprintf("%s=%s", k, v)) - } - } - - sort.Strings(parts) - - return strings.Join(parts, ",") -} - -// Str returns the parameter with name, returning an empty string if it is not -// set. -func (p Parameters) Str(name string) string { return p.StrDefault(name, "") } - -// StrDefault returns the parameter with name, or if it is unset, returns the -// def default value. -func (p Parameters) StrDefault(name string, def string) string { - if s, ok := p[name]; ok { - return s - } - - return def -} - -// SetStr sets the parameter name to s. -func (p Parameters) SetStr(name string, s string) { p[name] = s } - -// Int returns the parameter with name, returning zero if it is not set. An -// error is returned if the value cannot be parsed as an int. -func (p Parameters) Int(name string) (int, error) { return p.IntDefault(name, 0) } - -// IntDefault returns the parameter with name, or if it is unset, returns the -// def default value. An error is returned if the value cannot be parsed as an -// int. -func (p Parameters) IntDefault(name string, def int) (int, error) { - if s, ok := p[name]; ok { - return strconv.Atoi(s) - } - return def, nil -} - -// SetInt sets the parameter name to i. -func (p Parameters) SetInt(name string, i int) { p[name] = strconv.Itoa(i) } - -// Uint returns the parameter with name, returning zero if it is not set. An -// error is returned if the value cannot be parsed as a base-10 uint. -func (p Parameters) Uint(name string) (uint, error) { return p.UintDefault(name, 0) } - -// UintDefault returns the parameter with name, or if it is unset, returns the -// def default value. An error is returned if the value cannot be parsed as a -// base-10 uint. -func (p Parameters) UintDefault(name string, def uint) (uint, error) { - if s, ok := p[name]; ok { - ui, err := strconv.ParseUint(s, 10, strconv.IntSize) - return uint(ui), err - } - return def, nil -} - -// SetUint sets the parameter name to ui. -func (p Parameters) SetUint(name string, ui uint) { p[name] = strconv.FormatUint(uint64(ui), 10) } - -// Float returns the parameter with name, returning zero if it is -// not set. An error is returned if the value cannot be parsed as a float64 -func (p Parameters) Float(name string) (float64, error) { return p.FloatDefault(name, 0) } - -// FloatDefault returns the parameter with name, or if it is unset, returns the -// def default value. An error is returned if the value cannot be parsed as a -// float64. -func (p Parameters) FloatDefault(name string, def float64) (float64, error) { - if s, ok := p[name]; ok { - return strconv.ParseFloat(s, 64) - } - return def, nil -} - -// SetFloat sets the parameter name to f. -func (p Parameters) SetFloat(name string, f float64) { p[name] = strconv.FormatFloat(f, 'g', -1, 64) } - -// Bool returns the parameter with name, returning false if it is not set. An -// error is returned if the value cannot be parsed as a boolean. Empty values -// are considered true. -func (p Parameters) Bool(name string) (bool, error) { return p.BoolDefault(name, false) } - -// BoolDefault returns the parameter with name, or if it is unset, returns the -// def default value. An error is returned if the value cannot be parsed as a -// boolean. Empty values are considered true. -func (p Parameters) BoolDefault(name string, def bool) (bool, error) { - if s, ok := p[name]; ok { - if strings.TrimSpace(s) == "" { - return true, nil - } - return strconv.ParseBool(s) - } - - return def, nil -} - -// SetBool sets the parameter name to b. -func (p Parameters) SetBool(name string, b bool) { p[name] = strconv.FormatBool(b) } - -// Duration returns the parameter with name, returning zero if it is not set. -// An error is returned if the value cannot be parsed as a time.Duration. -func (p Parameters) Duration(name string) (time.Duration, error) { return p.DurationDefault(name, 0) } - -// DurationDefault returns the parameter with name, or if it is unset, returns -// the def default value. An error is returned if the value cannot be parsed as -// a time.Duration. -func (p Parameters) DurationDefault(name string, def time.Duration) (time.Duration, error) { - if s, ok := p[name]; ok { - return time.ParseDuration(s) - } - return def, nil -} - -// SetDuration sets the parameter name to d. -func (p Parameters) SetDuration(name string, d time.Duration) { p[name] = d.String() } diff --git a/v2/parameters_test.go b/v2/parameters_test.go deleted file mode 100644 index 68c7cdd..0000000 --- a/v2/parameters_test.go +++ /dev/null @@ -1,238 +0,0 @@ -package pgs - -import ( - "testing" - "time" - - "github.com/stretchr/testify/assert" -) - -func TestParameters_OutputPath(t *testing.T) { - t.Parallel() - - p := Parameters{} - assert.Equal(t, ".", p.OutputPath()) - p.SetOutputPath("foo") - assert.Equal(t, "foo", p.OutputPath()) -} - -func TestParseParameters(t *testing.T) { - t.Parallel() - - tests := []struct { - in string - out Parameters - }{ - { - "foo=bar", - Parameters{"foo": "bar"}, - }, - { - "fizz", - Parameters{"fizz": ""}, - }, - { - "foo=bar,fizz=buzz", - Parameters{"foo": "bar", "fizz": "buzz"}, - }, - { - "foo=bar,foo", - Parameters{"foo": ""}, - }, - } - - for _, test := range tests { - tc := test - t.Run(tc.in, func(t *testing.T) { - assert.Equal(t, tc.out, ParseParameters(tc.in)) - }) - } -} - -func TestParameters_String(t *testing.T) { - t.Parallel() - - tests := []struct { - in Parameters - out string - }{ - { - Parameters{"foo": "bar"}, - "foo=bar", - }, - { - Parameters{"fizz": ""}, - "fizz", - }, - { - Parameters{"foo": "bar", "fizz": ""}, - "fizz,foo=bar", - }, - } - - for _, test := range tests { - tc := test - t.Run(tc.out, func(t *testing.T) { - assert.Equal(t, tc.out, tc.in.String()) - }) - } -} - -func TestParameters_Str(t *testing.T) { - t.Parallel() - - p := Parameters{"foo": "bar"} - - assert.Equal(t, "bar", p.Str("foo")) - assert.Empty(t, p.Str("baz")) - assert.Equal(t, "fizz", p.StrDefault("baz", "fizz")) - - p.SetStr("baz", "buzz") - assert.Equal(t, "buzz", p.Str("baz")) -} - -func TestParameters_Int(t *testing.T) { - t.Parallel() - - p := Parameters{"foo": "456", "fizz": "buzz"} - - out, err := p.Int("foo") - assert.NoError(t, err) - assert.Equal(t, 456, out) - - _, err = p.Int("fizz") - assert.Error(t, err) - - out, err = p.Int("baz") - assert.NoError(t, err) - assert.Zero(t, out) - - out, err = p.IntDefault("baz", 123) - assert.NoError(t, err) - assert.Equal(t, 123, out) - - p.SetInt("baz", 789) - out, err = p.Int("baz") - assert.NoError(t, err) - assert.Equal(t, 789, out) -} - -func TestParameters_Uint(t *testing.T) { - t.Parallel() - - p := Parameters{"foo": "456", "fizz": "-789"} - - out, err := p.Uint("foo") - assert.NoError(t, err) - assert.Equal(t, uint(456), out) - - _, err = p.Uint("fizz") - assert.Error(t, err) - - out, err = p.Uint("buzz") - assert.NoError(t, err) - assert.Zero(t, out) - - out, err = p.UintDefault("baz", 123) - assert.NoError(t, err) - assert.Equal(t, uint(123), out) - - p.SetUint("baz", 999) - out, err = p.Uint("baz") - assert.NoError(t, err) - assert.Equal(t, uint(999), out) -} - -func TestParameters_Float(t *testing.T) { - t.Parallel() - - p := Parameters{"foo": "1.23", "fizz": "buzz"} - - out, err := p.Float("foo") - assert.NoError(t, err) - assert.Equal(t, 1.23, out) - - _, err = p.Float("fizz") - assert.Error(t, err) - - out, err = p.Float("baz") - assert.NoError(t, err) - assert.Zero(t, out) - - out, err = p.FloatDefault("baz", 4.56) - assert.NoError(t, err) - assert.Equal(t, 4.56, out) - - p.SetFloat("baz", -7.89) - out, err = p.Float("baz") - assert.NoError(t, err) - assert.Equal(t, -7.89, out) -} - -func TestParameters_Bool(t *testing.T) { - t.Parallel() - - p := Parameters{"foo": "true", "bar": "", "fizz": "buzz"} - - out, err := p.Bool("foo") - assert.NoError(t, err) - assert.True(t, out) - - out, err = p.Bool("bar") - assert.NoError(t, err) - assert.True(t, out) - - _, err = p.Bool("fizz") - assert.Error(t, err) - - out, err = p.Bool("baz") - assert.NoError(t, err) - assert.False(t, out) - - out, err = p.BoolDefault("baz", true) - assert.NoError(t, err) - assert.True(t, out) - - p.SetBool("baz", true) - out, err = p.Bool("baz") - assert.NoError(t, err) - assert.True(t, out) -} - -func TestParameters_Duration(t *testing.T) { - t.Parallel() - - p := Parameters{"foo": "123s", "fizz": "buzz"} - - out, err := p.Duration("foo") - assert.NoError(t, err) - assert.Equal(t, 123*time.Second, out) - - _, err = p.Duration("fizz") - assert.Error(t, err) - - out, err = p.Duration("baz") - assert.NoError(t, err) - assert.Zero(t, out) - - out, err = p.DurationDefault("baz", 456*time.Second) - assert.NoError(t, err) - assert.Equal(t, 456*time.Second, out) - - p.SetDuration("baz", 789*time.Second) - out, err = p.Duration("baz") - assert.NoError(t, err) - assert.Equal(t, 789*time.Second, out) -} - -func TestParameters_Clone(t *testing.T) { - t.Parallel() - - orig := Parameters{"foo": "bar", "fizz": "buzz"} - - clone := orig.Clone() - assert.Equal(t, orig, clone) - - clone.SetStr("foo", "baz") - assert.NotEqual(t, orig, clone) -} diff --git a/v2/persister.go b/v2/persister.go deleted file mode 100644 index 21d48b7..0000000 --- a/v2/persister.go +++ /dev/null @@ -1,192 +0,0 @@ -package pgs - -import ( - "os" - "path/filepath" - "strings" - - "github.com/golang/protobuf/proto" - plugin_go "github.com/golang/protobuf/protoc-gen-go/plugin" - "github.com/spf13/afero" -) - -type persister interface { - SetDebugger(d Debugger) - SetFS(fs afero.Fs) - SetSupportedFeatures(f *uint64) - AddPostProcessor(proc ...PostProcessor) - Persist(a ...Artifact) *plugin_go.CodeGeneratorResponse -} - -type stdPersister struct { - Debugger - - fs afero.Fs - procs []PostProcessor - supportedFeatures *uint64 -} - -func newPersister() *stdPersister { return &stdPersister{fs: afero.NewOsFs()} } - -func (p *stdPersister) SetDebugger(d Debugger) { p.Debugger = d } -func (p *stdPersister) SetFS(fs afero.Fs) { p.fs = fs } -func (p *stdPersister) SetSupportedFeatures(f *uint64) { p.supportedFeatures = f } -func (p *stdPersister) AddPostProcessor(proc ...PostProcessor) { p.procs = append(p.procs, proc...) } - -func (p *stdPersister) Persist(arts ...Artifact) *plugin_go.CodeGeneratorResponse { - resp := new(plugin_go.CodeGeneratorResponse) - resp.SupportedFeatures = p.supportedFeatures - - for _, a := range arts { - switch a := a.(type) { - case GeneratorFile: - f, err := a.ProtoFile() - p.CheckErr(err, "unable to convert ", a.Name, " to proto") - f.Content = proto.String(p.postProcess(a, f.GetContent())) - p.insertFile(resp, f, a.Overwrite) - case GeneratorTemplateFile: - f, err := a.ProtoFile() - p.CheckErr(err, "unable to convert ", a.Name, " to proto") - f.Content = proto.String(p.postProcess(a, f.GetContent())) - p.insertFile(resp, f, a.Overwrite) - case GeneratorAppend: - f, err := a.ProtoFile() - p.CheckErr(err, "unable to convert append for ", a.FileName, " to proto") - f.Content = proto.String(p.postProcess(a, f.GetContent())) - n, _ := cleanGeneratorFileName(a.FileName) - p.insertAppend(resp, n, f) - case GeneratorTemplateAppend: - f, err := a.ProtoFile() - p.CheckErr(err, "unable to convert append for ", a.FileName, " to proto") - f.Content = proto.String(p.postProcess(a, f.GetContent())) - n, _ := cleanGeneratorFileName(a.FileName) - p.insertAppend(resp, n, f) - case GeneratorInjection: - f, err := a.ProtoFile() - p.CheckErr(err, "unable to convert injection ", a.InsertionPoint, " for ", a.FileName, " to proto") - f.Content = proto.String(p.postProcess(a, f.GetContent())) - p.insertFile(resp, f, false) - case GeneratorTemplateInjection: - f, err := a.ProtoFile() - p.CheckErr(err, "unable to convert injection ", a.InsertionPoint, " for ", a.FileName, " to proto") - f.Content = proto.String(p.postProcess(a, f.GetContent())) - p.insertFile(resp, f, false) - case CustomFile: - p.writeFile( - a.Name, - []byte(p.postProcess(a, a.Contents)), - a.Overwrite, - a.Perms, - ) - case CustomTemplateFile: - content, err := a.render() - p.CheckErr(err, "unable to render CustomTemplateFile: ", a.Name) - content = p.postProcess(a, content) - p.writeFile( - a.Name, - []byte(content), - a.Overwrite, - a.Perms, - ) - case GeneratorError: - if resp.Error == nil { - resp.Error = proto.String(a.Message) - continue - } - resp.Error = proto.String(strings.Join([]string{resp.GetError(), a.Message}, "; ")) - default: - p.Failf("unrecognized artifact type: %T", a) - } - } - - return resp -} - -func (p *stdPersister) tailOfFile(resp *plugin_go.CodeGeneratorResponse, name string) int { - tail := p.indexOfFile(resp, name) - - if tail == -1 { - return -1 - } - - f := resp.GetFile() - for i := tail + 1; i < len(f); i++ { - if f[i].GetName() != "" { - break - } - tail = i - } - - return tail -} - -func (p *stdPersister) indexOfFile(resp *plugin_go.CodeGeneratorResponse, name string) int { - for i, f := range resp.GetFile() { - if f.GetName() == name && f.InsertionPoint == nil { - return i - } - } - - return -1 -} - -func (p *stdPersister) insertFile(resp *plugin_go.CodeGeneratorResponse, - f *plugin_go.CodeGeneratorResponse_File, overwrite bool) { - if overwrite { - if i := p.indexOfFile(resp, f.GetName()); i >= 0 { - resp.File[i] = f - return - } - } - - resp.File = append(resp.File, f) -} - -func (p *stdPersister) insertAppend(resp *plugin_go.CodeGeneratorResponse, - name string, f *plugin_go.CodeGeneratorResponse_File) { - i := p.tailOfFile(resp, name) - p.Assert(i > -1, "append target ", name, " missing") - - resp.File = append( - resp.File[:i+1], - append( - []*plugin_go.CodeGeneratorResponse_File{f}, - resp.File[i+1:]..., - )..., - ) -} - -func (p *stdPersister) writeFile(name string, content []byte, overwrite bool, perms os.FileMode) { - dir := filepath.Dir(name) - p.CheckErr( - p.fs.MkdirAll(dir, 0755), - "unable to create directory:", dir) - - exists, err := afero.Exists(p.fs, name) - p.CheckErr(err, "unable to check file exists:", name) - - if exists { - if !overwrite { - p.Debug("file", name, "exists, skipping") - return - } - p.Debug("file", name, "exists, overwriting") - } - - p.CheckErr( - afero.WriteFile(p.fs, name, content, perms), - "unable to write file:", name) -} - -func (p *stdPersister) postProcess(a Artifact, in string) string { - var err error - b := []byte(in) - for _, pp := range p.procs { - if pp.Match(a) { - b, err = pp.Process(b) - p.CheckErr(err, "failed post-processing") - } - } - - return string(b) -} diff --git a/v2/persister_test.go b/v2/persister_test.go deleted file mode 100644 index 0601caf..0000000 --- a/v2/persister_test.go +++ /dev/null @@ -1,384 +0,0 @@ -package pgs - -import ( - "html/template" - "testing" - - "errors" - - "github.com/spf13/afero" - "github.com/stretchr/testify/assert" -) - -func TestPersister_Persist_Unrecognized(t *testing.T) { - t.Parallel() - - d := InitMockDebugger() - p := dummyPersister(d) - - p.Persist(nil) - - assert.True(t, d.Failed()) -} - -func TestPersister_Persist_GeneratorFile(t *testing.T) { - t.Parallel() - - d := InitMockDebugger() - p := dummyPersister(d) - fs := afero.NewMemMapFs() - p.SetFS(fs) - - resp := p.Persist( - GeneratorFile{ - Name: "foo", - Contents: "bar", - }, - GeneratorFile{ - Name: "quux", - Contents: "baz", - }, - GeneratorFile{ - Name: "foo", - Contents: "fizz", - Overwrite: true, - }) - - assert.Len(t, resp.File, 2) - assert.Equal(t, "foo", resp.File[0].GetName()) - assert.Equal(t, "fizz", resp.File[0].GetContent()) -} - -var genTpl = template.Must(template.New("good").Parse("{{ . }}")) - -func TestPersister_Persist_GeneratorTemplateFile(t *testing.T) { - t.Parallel() - - d := InitMockDebugger() - p := dummyPersister(d) - fs := afero.NewMemMapFs() - p.SetFS(fs) - - resp := p.Persist( - GeneratorTemplateFile{ - Name: "foo", - TemplateArtifact: TemplateArtifact{ - Template: genTpl, - Data: "bar", - }, - }, - GeneratorTemplateFile{ - Name: "quux", - TemplateArtifact: TemplateArtifact{ - Template: genTpl, - Data: "baz", - }, - }, - GeneratorTemplateFile{ - Name: "foo", - TemplateArtifact: TemplateArtifact{ - Template: genTpl, - Data: "fizz", - }, - Overwrite: true, - }, - ) - - assert.Len(t, resp.File, 2) - assert.Equal(t, "foo", resp.File[0].GetName()) - assert.Equal(t, "fizz", resp.File[0].GetContent()) -} - -func TestPersister_Persist_GeneratorAppend(t *testing.T) { - t.Parallel() - - d := InitMockDebugger() - p := dummyPersister(d) - fs := afero.NewMemMapFs() - p.SetFS(fs) - - resp := p.Persist( - GeneratorFile{Name: "foo"}, - GeneratorFile{Name: "bar"}, - GeneratorAppend{ - FileName: "foo", - Contents: "baz", - }, - GeneratorAppend{ - FileName: "bar", - Contents: "quux", - }, - ) - - assert.Len(t, resp.File, 4) - assert.Equal(t, "", resp.File[1].GetName()) - assert.Equal(t, "baz", resp.File[1].GetContent()) - assert.Equal(t, "", resp.File[3].GetName()) - assert.Equal(t, "quux", resp.File[3].GetContent()) - - p.Persist(GeneratorAppend{FileName: "doesNotExist"}) - - assert.True(t, d.Failed()) -} - -func TestPersister_Persist_GeneratorAppendSeveral(t *testing.T) { - t.Parallel() - - d := InitMockDebugger() - p := dummyPersister(d) - fs := afero.NewMemMapFs() - p.SetFS(fs) - - resp := p.Persist( - GeneratorFile{ - Name: "file", - Contents: "foo", - }, - GeneratorAppend{ - FileName: "file", - Contents: "bar", - }, - GeneratorAppend{ - FileName: "file", - Contents: "baz", - }, - ) - - assert.Len(t, resp.File, 3) - assert.Equal(t, "foo", resp.File[0].GetContent()) - assert.Equal(t, "bar", resp.File[1].GetContent()) - assert.Equal(t, "baz", resp.File[2].GetContent()) -} - -func TestPersister_Persist_GeneratorTemplateAppend(t *testing.T) { - t.Parallel() - - d := InitMockDebugger() - p := dummyPersister(d) - fs := afero.NewMemMapFs() - p.SetFS(fs) - - resp := p.Persist( - GeneratorFile{Name: "foo"}, - GeneratorFile{Name: "bar"}, - GeneratorTemplateAppend{ - FileName: "foo", - TemplateArtifact: TemplateArtifact{ - Template: genTpl, - Data: "baz", - }, - }, GeneratorTemplateAppend{ - FileName: "bar", - TemplateArtifact: TemplateArtifact{ - Template: genTpl, - Data: "quux", - }, - }, - ) - - assert.Len(t, resp.File, 4) - assert.Equal(t, "", resp.File[1].GetName()) - assert.Equal(t, "baz", resp.File[1].GetContent()) - assert.Equal(t, "", resp.File[3].GetName()) - assert.Equal(t, "quux", resp.File[3].GetContent()) - - resp = p.Persist(GeneratorTemplateAppend{ - FileName: "doesNotExist", - TemplateArtifact: TemplateArtifact{ - Template: genTpl, - Data: "baz", - }, - }) - - assert.True(t, d.Failed()) -} - -func TestPersister_Persist_GeneratorInjection(t *testing.T) { - t.Parallel() - - d := InitMockDebugger() - p := dummyPersister(d) - fs := afero.NewMemMapFs() - p.SetFS(fs) - - resp := p.Persist(GeneratorInjection{ - FileName: "foo", - InsertionPoint: "bar", - Contents: "baz", - }) - - assert.Len(t, resp.File, 1) - assert.Equal(t, "foo", resp.File[0].GetName()) - assert.Equal(t, "bar", resp.File[0].GetInsertionPoint()) - assert.Equal(t, "baz", resp.File[0].GetContent()) -} - -func TestPersister_Persist_GeneratorTemplateInjection(t *testing.T) { - t.Parallel() - - d := InitMockDebugger() - p := dummyPersister(d) - fs := afero.NewMemMapFs() - p.SetFS(fs) - - resp := p.Persist(GeneratorTemplateInjection{ - FileName: "foo", - InsertionPoint: "bar", - TemplateArtifact: TemplateArtifact{ - Template: genTpl, - Data: "baz", - }, - }) - - assert.Len(t, resp.File, 1) - assert.Equal(t, "foo", resp.File[0].GetName()) - assert.Equal(t, "bar", resp.File[0].GetInsertionPoint()) - assert.Equal(t, "baz", resp.File[0].GetContent()) -} - -func TestPersister_Persist_CustomFile(t *testing.T) { - t.Parallel() - - d := InitMockDebugger() - p := dummyPersister(d) - fs := afero.NewMemMapFs() - p.SetFS(fs) - - p.Persist(CustomFile{ - Name: "foo/bar/baz.txt", - Perms: 0655, - Contents: "fizz", - }) - - b, err := afero.ReadFile(fs, "foo/bar/baz.txt") - assert.NoError(t, err) - assert.Equal(t, "fizz", string(b)) - - p.Persist(CustomFile{ - Name: "foo/bar/baz.txt", - Perms: 0655, - Contents: "buzz", - }) - - b, err = afero.ReadFile(fs, "foo/bar/baz.txt") - assert.NoError(t, err) - assert.Equal(t, "fizz", string(b)) - - p.Persist(CustomFile{ - Name: "foo/bar/baz.txt", - Perms: 0655, - Contents: "buzz", - Overwrite: true, - }) - - b, err = afero.ReadFile(fs, "foo/bar/baz.txt") - assert.NoError(t, err) - assert.Equal(t, "buzz", string(b)) -} - -func TestPersister_Persist_CustomTemplateFile(t *testing.T) { - t.Parallel() - - d := InitMockDebugger() - p := dummyPersister(d) - fs := afero.NewMemMapFs() - p.SetFS(fs) - - p.Persist(CustomTemplateFile{ - Name: "foo/bar/baz.txt", - Perms: 0655, - TemplateArtifact: TemplateArtifact{ - Template: genTpl, - Data: "fizz", - }, - }) - - b, err := afero.ReadFile(fs, "foo/bar/baz.txt") - assert.NoError(t, err) - assert.Equal(t, "fizz", string(b)) - - p.Persist(CustomTemplateFile{ - Name: "foo/bar/baz.txt", - Perms: 0655, - TemplateArtifact: TemplateArtifact{ - Template: genTpl, - Data: "buzz", - }, - }) - - b, err = afero.ReadFile(fs, "foo/bar/baz.txt") - assert.NoError(t, err) - assert.Equal(t, "fizz", string(b)) - - p.Persist(CustomTemplateFile{ - Name: "foo/bar/baz.txt", - Perms: 0655, - TemplateArtifact: TemplateArtifact{ - Template: genTpl, - Data: "buzz", - }, - Overwrite: true, - }) - - b, err = afero.ReadFile(fs, "foo/bar/baz.txt") - assert.NoError(t, err) - assert.Equal(t, "buzz", string(b)) -} - -func TestPersister_AddPostProcessor(t *testing.T) { - t.Parallel() - - p := dummyPersister(InitMockDebugger()) - - good := &mockPP{match: true, out: []byte("good")} - bad := &mockPP{err: errors.New("should not be called")} - - p.AddPostProcessor(good, bad) - out := p.postProcess(GeneratorFile{}, "") - assert.Equal(t, "good", out) -} - -func dummyPersister(d Debugger) *stdPersister { - return &stdPersister{ - Debugger: d, - fs: afero.NewMemMapFs(), - } -} - -func TestPersister_Persist_GeneratorError(t *testing.T) { - t.Parallel() - - cases := map[string]struct { - input []Artifact - output string - }{ - "no errors": { - []Artifact{}, - "", - }, - "one error": { - []Artifact{GeneratorError{Message: "something went wrong"}}, - "something went wrong", - }, - "two errors": { - []Artifact{ - GeneratorError{Message: "something went wrong"}, - GeneratorError{Message: "something else went wrong, too"}, - }, - "something went wrong; something else went wrong, too", - }, - } - for desc, tc := range cases { - t.Run(desc, func(t *testing.T) { - d := InitMockDebugger() - p := dummyPersister(d) - fs := afero.NewMemMapFs() - p.SetFS(fs) - - resp := p.Persist(tc.input...) - - assert.Len(t, resp.File, 0) - assert.Equal(t, tc.output, resp.GetError()) - }) - } -} diff --git a/v2/post_process.go b/v2/post_process.go deleted file mode 100644 index 3f406d7..0000000 --- a/v2/post_process.go +++ /dev/null @@ -1,12 +0,0 @@ -package pgs - -// A PostProcessor modifies the output of an Artifact before final rendering. -type PostProcessor interface { - // Match returns true if the PostProcess should be applied to the Artifact. - // Process is called immediately after Match for the same Artifact. - Match(a Artifact) bool - - // Process receives the rendered artifact and returns the processed bytes or - // an error if something goes wrong. - Process(in []byte) ([]byte, error) -} diff --git a/v2/post_process_test.go b/v2/post_process_test.go deleted file mode 100644 index 3408d36..0000000 --- a/v2/post_process_test.go +++ /dev/null @@ -1,10 +0,0 @@ -package pgs - -type mockPP struct { - match bool - out []byte - err error -} - -func (pp mockPP) Match(a Artifact) bool { return pp.match } -func (pp mockPP) Process(in []byte) ([]byte, error) { return pp.out, pp.err } diff --git a/v2/proto.go b/v2/proto.go deleted file mode 100644 index a3b78df..0000000 --- a/v2/proto.go +++ /dev/null @@ -1,136 +0,0 @@ -package pgs - -import ( - "github.com/golang/protobuf/protoc-gen-go/descriptor" -) - -// Syntax describes the proto syntax used to encode the proto file -type Syntax string - -const ( - // Proto2 syntax permits the use of "optional" and "required" prefixes on - // fields. Most of the field types in the generated go structs are pointers. - // See: https://developers.google.com/protocol-buffers/docs/proto - Proto2 Syntax = "" - - // Proto3 syntax permits the use of "optional" field presence. Non optional fields default to the zero - // value of that particular type if not defined. - // Most of the field types in the generated go structs are value types. - // See: https://github.com/protocolbuffers/protobuf/blob/v3.17.0/docs/field_presence.md#presence-in-proto3-apis - Proto3 Syntax = "proto3" -) - -// SupportsRequiredPrefix returns true if s supports "optional" and -// "required" identifiers on message fields. Only Proto2 syntax supports this -// feature. -func (s Syntax) SupportsRequiredPrefix() bool { return s == Proto2 } - -// String returns a string representation of the syntax. -func (s Syntax) String() string { - return string(s) -} - -// ProtoLabel wraps the FieldDescriptorProto_Label enum for better readability. -// It is a 1-to-1 conversion. -type ProtoLabel descriptor.FieldDescriptorProto_Label - -const ( - // Optional (in the context of Proto2 syntax) identifies that the field may - // be unset in the proto message. In Proto3 syntax, all fields are considered - // Optional and default to their zero value. - Optional = ProtoLabel(descriptor.FieldDescriptorProto_LABEL_OPTIONAL) - - // Required (in the context of Proto2 syntax) identifies that the field must - // be set in the proto message. In Proto3 syntax, no fields can be identified - // as Required. - Required = ProtoLabel(descriptor.FieldDescriptorProto_LABEL_REQUIRED) - - // Repeated identifies that the field either permits multiple entries - // (repeated) or is a map (map). Determining which requires further - // evaluation of the descriptor and whether or not the embedded message is - // identified as a MapEntry (see IsMap on FieldType). - Repeated = ProtoLabel(descriptor.FieldDescriptorProto_LABEL_REPEATED) -) - -// Proto returns the FieldDescriptorProto_Label for this ProtoLabel. This -// method is exclusively used to improve readability without having to switch -// the types. -func (pl ProtoLabel) Proto() descriptor.FieldDescriptorProto_Label { - return descriptor.FieldDescriptorProto_Label(pl) -} - -// ProtoPtr returns a pointer to the FieldDescriptorProto_Label for this -// ProtoLabel. -func (pl ProtoLabel) ProtoPtr() *descriptor.FieldDescriptorProto_Label { - l := pl.Proto() - return &l -} - -// String returns a string representation of the proto label. -func (pl ProtoLabel) String() string { - return pl.Proto().String() -} - -// ProtoType wraps the FieldDescriptorProto_Type enum for better readability -// and utility methods. It is a 1-to-1 conversion. -type ProtoType descriptor.FieldDescriptorProto_Type - -// 1-to-1 mapping of FieldDescriptorProto_Type enum to ProtoType. While all are -// listed here, group types are not supported by this library. -const ( - DoubleT = ProtoType(descriptor.FieldDescriptorProto_TYPE_DOUBLE) - FloatT = ProtoType(descriptor.FieldDescriptorProto_TYPE_FLOAT) - Int64T = ProtoType(descriptor.FieldDescriptorProto_TYPE_INT64) - UInt64T = ProtoType(descriptor.FieldDescriptorProto_TYPE_UINT64) - Int32T = ProtoType(descriptor.FieldDescriptorProto_TYPE_INT32) - Fixed64T = ProtoType(descriptor.FieldDescriptorProto_TYPE_FIXED64) - Fixed32T = ProtoType(descriptor.FieldDescriptorProto_TYPE_FIXED32) - BoolT = ProtoType(descriptor.FieldDescriptorProto_TYPE_BOOL) - StringT = ProtoType(descriptor.FieldDescriptorProto_TYPE_STRING) - GroupT = ProtoType(descriptor.FieldDescriptorProto_TYPE_GROUP) - MessageT = ProtoType(descriptor.FieldDescriptorProto_TYPE_MESSAGE) - BytesT = ProtoType(descriptor.FieldDescriptorProto_TYPE_BYTES) - UInt32T = ProtoType(descriptor.FieldDescriptorProto_TYPE_UINT32) - EnumT = ProtoType(descriptor.FieldDescriptorProto_TYPE_ENUM) - SFixed32 = ProtoType(descriptor.FieldDescriptorProto_TYPE_SFIXED32) - SFixed64 = ProtoType(descriptor.FieldDescriptorProto_TYPE_SFIXED64) - SInt32 = ProtoType(descriptor.FieldDescriptorProto_TYPE_SINT32) - SInt64 = ProtoType(descriptor.FieldDescriptorProto_TYPE_SINT64) -) - -// IsInt returns true if pt maps to an integer-like type. While EnumT types in -// Go are aliases of uint32, to correctly accommodate other languages with -// non-numeric enums, IsInt returns false for EnumT. -func (pt ProtoType) IsInt() bool { - switch pt { - case Int64T, UInt64T, SFixed64, SInt64, Fixed64T, - Int32T, UInt32T, SFixed32, SInt32, Fixed32T: - return true - } - - return false -} - -// IsNumeric returns true if pt maps to a numeric type. While EnumT types in Go -// are aliases of uint32, to correctly accommodate other languages with non-numeric -// enums, IsNumeric returns false for EnumT. -func (pt ProtoType) IsNumeric() bool { return pt == DoubleT || pt == FloatT || pt.IsInt() } - -// Proto returns the FieldDescriptorProto_Type for this ProtoType. This -// method is exclusively used to improve readability without having to switch -// the types. -func (pt ProtoType) Proto() descriptor.FieldDescriptorProto_Type { - return descriptor.FieldDescriptorProto_Type(pt) -} - -// ProtoPtr returns a pointer to the FieldDescriptorProto_Type for this -// ProtoType. -func (pt ProtoType) ProtoPtr() *descriptor.FieldDescriptorProto_Type { - t := pt.Proto() - return &t -} - -// String returns a string representation of the proto type. -func (pt ProtoType) String() string { - return pt.Proto().String() -} diff --git a/v2/proto_test.go b/v2/proto_test.go deleted file mode 100644 index a57297f..0000000 --- a/v2/proto_test.go +++ /dev/null @@ -1,97 +0,0 @@ -package pgs - -import ( - "testing" - - "github.com/golang/protobuf/protoc-gen-go/descriptor" - "github.com/stretchr/testify/assert" -) - -func TestSyntax_SupportsRequiredPrefix(t *testing.T) { - t.Parallel() - assert.True(t, Proto2.SupportsRequiredPrefix()) - assert.False(t, Proto3.SupportsRequiredPrefix()) -} - -func TestSyntax_String(t *testing.T) { - t.Parallel() - - assert.Equal(t, Proto2.String(), "") - assert.Equal(t, Proto3.String(), "proto3") -} - -func TestProtoType_IsInt(t *testing.T) { - t.Parallel() - - yes := []ProtoType{ - Int64T, UInt64T, SFixed64, SInt64, Fixed64T, - Int32T, UInt32T, SFixed32, SInt32, Fixed32T, - } - - no := []ProtoType{ - DoubleT, FloatT, BoolT, StringT, - GroupT, MessageT, BytesT, EnumT, - } - - for _, pt := range yes { - assert.True(t, pt.IsInt()) - } - - for _, pt := range no { - assert.False(t, pt.IsInt()) - } -} - -func TestProtoType_IsNumeric(t *testing.T) { - t.Parallel() - - yes := []ProtoType{ - Int64T, UInt64T, SFixed64, SInt64, Fixed64T, - Int32T, UInt32T, SFixed32, SInt32, Fixed32T, - DoubleT, FloatT, - } - - no := []ProtoType{ - BoolT, StringT, GroupT, - MessageT, BytesT, EnumT, - } - - for _, pt := range yes { - assert.True(t, pt.IsNumeric()) - } - - for _, pt := range no { - assert.False(t, pt.IsNumeric()) - } -} - -func TestProtoType_Proto(t *testing.T) { - t.Parallel() - - pt := BytesT.Proto() - ptPtr := BytesT.ProtoPtr() - assert.Equal(t, descriptor.FieldDescriptorProto_TYPE_BYTES, pt) - assert.Equal(t, pt, *ptPtr) -} - -func TestProtoType_String(t *testing.T) { - t.Parallel() - - assert.Equal(t, DoubleT.String(), "TYPE_DOUBLE") -} - -func TestProtoLabel_Proto(t *testing.T) { - t.Parallel() - - pl := Repeated.Proto() - plPtr := Repeated.ProtoPtr() - - assert.Equal(t, descriptor.FieldDescriptorProto_LABEL_REPEATED, pl) - assert.Equal(t, pl, *plPtr) -} - -func TestProtoLabel_String(t *testing.T) { - t.Parallel() - - assert.Equal(t, Repeated.String(), "LABEL_REPEATED") -} diff --git a/v2/service.go b/v2/service.go deleted file mode 100644 index c7849ef..0000000 --- a/v2/service.go +++ /dev/null @@ -1,104 +0,0 @@ -package pgs - -import ( - "github.com/golang/protobuf/proto" - "github.com/golang/protobuf/protoc-gen-go/descriptor" -) - -// Service describes a proto service definition (typically, gRPC) -type Service interface { - Entity - - // Descriptor returns the underlying proto descriptor for this service - Descriptor() *descriptor.ServiceDescriptorProto - - // Methods returns each rpc method exposed by this service - Methods() []Method - - setFile(f File) - addMethod(m Method) -} - -type service struct { - desc *descriptor.ServiceDescriptorProto - methods []Method - file File - fqn string - - info SourceCodeInfo -} - -func (s *service) Name() Name { return Name(s.desc.GetName()) } -func (s *service) FullyQualifiedName() string { return s.fqn } -func (s *service) Syntax() Syntax { return s.file.Syntax() } -func (s *service) Package() Package { return s.file.Package() } -func (s *service) File() File { return s.file } -func (s *service) BuildTarget() bool { return s.file.BuildTarget() } -func (s *service) SourceCodeInfo() SourceCodeInfo { return s.info } -func (s *service) Descriptor() *descriptor.ServiceDescriptorProto { return s.desc } - -func (s *service) Extension(desc *proto.ExtensionDesc, ext interface{}) (bool, error) { - return extension(s.desc.GetOptions(), desc, &ext) -} - -func (s *service) Imports() (i []File) { - // Mapping for avoiding duplicate entries - mp := make(map[string]File, len(s.methods)) - for _, m := range s.methods { - for _, imp := range m.Imports() { - mp[imp.File().Name().String()] = imp - } - } - for _, f := range mp { - i = append(i, f) - } - return -} - -func (s *service) Methods() []Method { - m := make([]Method, len(s.methods)) - copy(m, s.methods) - return m -} - -func (s *service) setFile(f File) { s.file = f } - -func (s *service) addMethod(m Method) { - m.setService(s) - s.methods = append(s.methods, m) -} - -func (s *service) accept(v Visitor) (err error) { - if v == nil { - return - } - - if v, err = v.VisitService(s); err != nil || v == nil { - return - } - - for _, m := range s.methods { - if err = m.accept(v); err != nil { - return - } - } - - return -} - -func (s *service) childAtPath(path []int32) Entity { - switch { - case len(path) == 0: - return s - case len(path)%2 != 0: - return nil - case path[0] == serviceTypeMethodPath: - return s.methods[path[1]].childAtPath(path[2:]) - default: - return nil - } -} - -func (s *service) addSourceCodeInfo(info SourceCodeInfo) { s.info = info } - -var _ Service = (*service)(nil) diff --git a/v2/service_test.go b/v2/service_test.go deleted file mode 100644 index e9361ce..0000000 --- a/v2/service_test.go +++ /dev/null @@ -1,180 +0,0 @@ -package pgs - -import ( - "testing" - - "errors" - - "github.com/golang/protobuf/proto" - "github.com/golang/protobuf/protoc-gen-go/descriptor" - "github.com/stretchr/testify/assert" -) - -func TestService_Name(t *testing.T) { - t.Parallel() - - s := &service{desc: &descriptor.ServiceDescriptorProto{Name: proto.String("foo")}} - assert.Equal(t, "foo", s.Name().String()) -} - -func TestService_FullyQualifiedName(t *testing.T) { - t.Parallel() - - s := &service{fqn: "foo"} - assert.Equal(t, s.fqn, s.FullyQualifiedName()) -} - -func TestService_Syntax(t *testing.T) { - t.Parallel() - - s := &service{} - f := dummyFile() - f.addService(s) - - assert.Equal(t, f.Syntax(), s.Syntax()) -} - -func TestService_Package(t *testing.T) { - t.Parallel() - - s := &service{} - f := dummyFile() - f.addService(s) - - assert.NotNil(t, s.Package()) - assert.Equal(t, f.Package(), s.Package()) -} - -func TestService_File(t *testing.T) { - t.Parallel() - - s := &service{} - f := dummyFile() - f.addService(s) - - assert.NotNil(t, s.File()) - assert.Equal(t, f, s.File()) -} - -func TestService_BuildTarget(t *testing.T) { - t.Parallel() - - s := &service{} - f := dummyFile() - f.addService(s) - - assert.False(t, s.BuildTarget()) - f.buildTarget = true - assert.True(t, s.BuildTarget()) -} - -func TestService_Descriptor(t *testing.T) { - t.Parallel() - - s := &service{desc: &descriptor.ServiceDescriptorProto{}} - assert.Equal(t, s.desc, s.Descriptor()) -} - -func TestService_Extension(t *testing.T) { - // cannot be parallel - - s := &service{desc: &descriptor.ServiceDescriptorProto{}} - assert.NotPanics(t, func() { s.Extension(nil, nil) }) -} - -func TestService_Imports(t *testing.T) { - t.Parallel() - - s := &service{} - assert.Empty(t, s.Imports()) - s.addMethod(&mockMethod{i: []File{&file{}}}) - assert.Len(t, s.Imports(), 1) - - nf := &file{desc: &descriptor.FileDescriptorProto{ - Name: proto.String("foobar"), - }} - s.addMethod(&mockMethod{i: []File{nf, nf}}) - assert.Len(t, s.Imports(), 2) -} - -func TestService_Methods(t *testing.T) { - t.Parallel() - - s := &service{} - assert.Empty(t, s.Methods()) - s.addMethod(&method{}) - assert.Len(t, s.Methods(), 1) -} - -func TestService_Accept(t *testing.T) { - t.Parallel() - - s := &service{} - s.addMethod(&method{}) - - assert.NoError(t, s.accept(nil)) - - v := &mockVisitor{} - assert.NoError(t, s.accept(v)) - assert.Equal(t, 1, v.service) - assert.Zero(t, v.method) - - v.Reset() - v.err = errors.New("fizz") - v.v = v - assert.Error(t, s.accept(v)) - assert.Equal(t, 1, v.service) - assert.Zero(t, v.method) - - v.Reset() - assert.NoError(t, s.accept(v)) - assert.Equal(t, 1, v.service) - assert.Equal(t, 1, v.method) - - v.Reset() - s.addMethod(&mockMethod{err: errors.New("buzz")}) - assert.Error(t, s.accept(v)) - assert.Equal(t, 1, v.service) - assert.Equal(t, 2, v.method) -} - -func TestService_ChildAtPath(t *testing.T) { - t.Parallel() - - s := &service{} - assert.Equal(t, s, s.childAtPath(nil)) - assert.Nil(t, s.childAtPath([]int32{0})) - assert.Nil(t, s.childAtPath([]int32{0, 0})) -} - -type mockService struct { - Service - i []File - f File - err error -} - -func (s *mockService) Imports() []File { return s.i } - -func (s *mockService) setFile(f File) { s.f = f } - -func (s *mockService) accept(v Visitor) error { - _, err := v.VisitService(s) - if s.err != nil { - return s.err - } - return err -} - -func dummyService() *service { - f := dummyFile() - - s := &service{ - desc: &descriptor.ServiceDescriptorProto{ - Name: proto.String("service"), - }, - } - - f.addService(s) - return s -} diff --git a/v2/source_code_info.go b/v2/source_code_info.go deleted file mode 100644 index 815e2ba..0000000 --- a/v2/source_code_info.go +++ /dev/null @@ -1,56 +0,0 @@ -package pgs - -import ( - "github.com/golang/protobuf/protoc-gen-go/descriptor" -) - -const ( - packagePath int32 = 2 // FileDescriptorProto.Package - messageTypePath int32 = 4 // FileDescriptorProto.MessageType - enumTypePath int32 = 5 // FileDescriptorProto.EnumType - servicePath int32 = 6 // FileDescriptorProto.Service - syntaxPath int32 = 12 // FileDescriptorProto.Syntax - messageTypeFieldPath int32 = 2 // DescriptorProto.Field - messageTypeNestedTypePath int32 = 3 // DescriptorProto.NestedType - messageTypeEnumTypePath int32 = 4 // DescriptorProto.EnumType - messageTypeOneofDeclPath int32 = 8 // DescriptorProto.OneofDecl - enumTypeValuePath int32 = 2 // EnumDescriptorProto.Value - serviceTypeMethodPath int32 = 2 // ServiceDescriptorProto.Method -) - -// SourceCodeInfo represents data about an entity from the source. Currently -// this only contains information about comments protoc associates with -// entities. -// -// All comments have their // or /* */ stripped by protoc. See the -// SourceCodeInfo documentation for more details about how comments are -// associated with entities. -type SourceCodeInfo interface { - // Location returns the SourceCodeInfo_Location from the file descriptor. - Location() *descriptor.SourceCodeInfo_Location - - // LeadingComments returns any comment immediately preceding the entity, - // without any whitespace between it and the comment. - LeadingComments() string - - // LeadingDetachedComments returns each comment block or line above the - // entity but separated by whitespace. - LeadingDetachedComments() []string - - // TrailingComments returns any comment immediately following the entity, - // without any whitespace between it and the comment. If the comment would be - // a leading comment for another entity, it won't be considered a trailing - // comment. - TrailingComments() string -} - -type sci struct { - desc *descriptor.SourceCodeInfo_Location -} - -func (info sci) Location() *descriptor.SourceCodeInfo_Location { return info.desc } -func (info sci) LeadingComments() string { return info.desc.GetLeadingComments() } -func (info sci) LeadingDetachedComments() []string { return info.desc.GetLeadingDetachedComments() } -func (info sci) TrailingComments() string { return info.desc.GetTrailingComments() } - -var _ SourceCodeInfo = sci{} diff --git a/v2/source_code_info_test.go b/v2/source_code_info_test.go deleted file mode 100644 index a10764e..0000000 --- a/v2/source_code_info_test.go +++ /dev/null @@ -1,26 +0,0 @@ -package pgs - -import ( - "testing" - - "github.com/golang/protobuf/proto" - "github.com/golang/protobuf/protoc-gen-go/descriptor" - "github.com/stretchr/testify/assert" -) - -func TestSourceCodeInfo(t *testing.T) { - t.Parallel() - - desc := &descriptor.SourceCodeInfo_Location{ - LeadingComments: proto.String("leading"), - TrailingComments: proto.String("trailing"), - LeadingDetachedComments: []string{"detached"}, - } - - info := sci{desc} - - assert.Equal(t, desc, info.Location()) - assert.Equal(t, "leading", info.LeadingComments()) - assert.Equal(t, "trailing", info.TrailingComments()) - assert.Equal(t, []string{"detached"}, info.LeadingDetachedComments()) -} diff --git a/v2/tools.go b/v2/tools.go deleted file mode 100644 index 37283e2..0000000 --- a/v2/tools.go +++ /dev/null @@ -1,7 +0,0 @@ -// +build tools - -package tools - -import ( - _ "github.com/golang/protobuf/protoc-gen-go" -) diff --git a/v2/wkt.go b/v2/wkt.go deleted file mode 100644 index 5d6c86e..0000000 --- a/v2/wkt.go +++ /dev/null @@ -1,73 +0,0 @@ -package pgs - -// WellKnownTypePackage is the proto package name where all Well Known Types -// currently reside. -const WellKnownTypePackage Name = "google.protobuf" - -// WellKnownType (WKT) encapsulates the Name of a Message from the -// `google.protobuf` package. Most official protoc plugins special case code -// generation on these messages. -type WellKnownType Name - -// 1-to-1 mapping of the WKT names to WellKnownTypes. -const ( - // UnknownWKT indicates that the type is not a known WKT. This value may be - // returned erroneously mapping a Name to a WellKnownType or if a WKT is - // added to the `google.protobuf` package but this library is outdated. - UnknownWKT WellKnownType = "Unknown" - - AnyWKT WellKnownType = "Any" - DurationWKT WellKnownType = "Duration" - EmptyWKT WellKnownType = "Empty" - StructWKT WellKnownType = "Struct" - TimestampWKT WellKnownType = "Timestamp" - ValueWKT WellKnownType = "Value" - ListValueWKT WellKnownType = "ListValue" - DoubleValueWKT WellKnownType = "DoubleValue" - FloatValueWKT WellKnownType = "FloatValue" - Int64ValueWKT WellKnownType = "Int64Value" - UInt64ValueWKT WellKnownType = "UInt64Value" - Int32ValueWKT WellKnownType = "Int32Value" - UInt32ValueWKT WellKnownType = "UInt32Value" - BoolValueWKT WellKnownType = "BoolValue" - StringValueWKT WellKnownType = "StringValue" - BytesValueWKT WellKnownType = "BytesValue" -) - -var wktLookup = map[Name]WellKnownType{ - "Any": AnyWKT, - "Duration": DurationWKT, - "Empty": EmptyWKT, - "Struct": StructWKT, - "Timestamp": TimestampWKT, - "Value": ValueWKT, - "ListValue": ListValueWKT, - "DoubleValue": DoubleValueWKT, - "FloatValue": FloatValueWKT, - "Int64Value": Int64ValueWKT, - "UInt64Value": UInt64ValueWKT, - "Int32Value": Int32ValueWKT, - "UInt32Value": UInt32ValueWKT, - "BoolValue": BoolValueWKT, - "StringValue": StringValueWKT, - "BytesValue": BytesValueWKT, -} - -// LookupWKT returns the WellKnownType related to the provided Name. If the -// name is not recognized, UnknownWKT is returned. -func LookupWKT(n Name) WellKnownType { - if wkt, ok := wktLookup[n]; ok { - return wkt - } - - return UnknownWKT -} - -// Name converts the WellKnownType to a Name. This is a convenience method. -func (wkt WellKnownType) Name() Name { return Name(wkt) } - -// Valid returns true if the WellKnownType is recognized by this library. -func (wkt WellKnownType) Valid() bool { - _, ok := wktLookup[wkt.Name()] - return ok -} diff --git a/v2/wkt_test.go b/v2/wkt_test.go deleted file mode 100644 index c67f39a..0000000 --- a/v2/wkt_test.go +++ /dev/null @@ -1,58 +0,0 @@ -package pgs - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestLookupWKT(t *testing.T) { - t.Parallel() - - tests := []struct { - name Name - expected WellKnownType - }{ - {"Any", AnyWKT}, - {"Duration", DurationWKT}, - {"Empty", EmptyWKT}, - {"Foobar", UnknownWKT}, - } - - for _, test := range tests { - tc := test - t.Run(tc.name.String(), func(t *testing.T) { - t.Parallel() - assert.Equal(t, tc.expected, LookupWKT(tc.name)) - }) - } -} - -func TestWellKnownType_Name(t *testing.T) { - t.Parallel() - - wkt := WellKnownType("Foobar") - assert.Equal(t, Name("Foobar"), wkt.Name()) -} - -func TestWellKnownType_Valid(t *testing.T) { - t.Parallel() - - tests := []struct { - wkt WellKnownType - expected bool - }{ - {AnyWKT, true}, - {Int64ValueWKT, true}, - {UnknownWKT, false}, - {WellKnownType("Foobar"), false}, - } - - for _, test := range tests { - tc := test - t.Run(tc.wkt.Name().String(), func(t *testing.T) { - t.Parallel() - assert.Equal(t, tc.expected, tc.wkt.Valid()) - }) - } -} diff --git a/v2/workflow.go b/v2/workflow.go deleted file mode 100644 index 1744426..0000000 --- a/v2/workflow.go +++ /dev/null @@ -1,109 +0,0 @@ -package pgs - -import ( - "io/ioutil" - "sync" - - "github.com/golang/protobuf/proto" - plugin_go "github.com/golang/protobuf/protoc-gen-go/plugin" -) - -type workflow interface { - Init(*Generator) AST - Run(AST) []Artifact - Persist([]Artifact) -} - -// standardWorkflow describes a typical protoc-plugin flow, with the only -// exception being the behavior of the persister directly writing custom file -// artifacts to disk (instead of via the plugin's output to protoc). -type standardWorkflow struct { - *Generator - BiDi bool -} - -func (wf *standardWorkflow) Init(g *Generator) AST { - wf.Generator = g - - wf.Debug("reading input") - data, err := ioutil.ReadAll(g.in) - wf.CheckErr(err, "reading input") - - wf.Debug("parsing input proto") - req := new(plugin_go.CodeGeneratorRequest) - err = proto.Unmarshal(data, req) - wf.CheckErr(err, "parsing input proto") - wf.Assert(len(req.FileToGenerate) > 0, "no files to generate") - - wf.Debug("parsing command-line params") - wf.params = ParseParameters(req.GetParameter()) - for _, pm := range wf.paramMutators { - pm(wf.params) - } - - if wf.BiDi { - return ProcessCodeGeneratorRequestBidirectional(g, req) - } - - return ProcessCodeGeneratorRequest(g, req) -} - -func (wf *standardWorkflow) Run(ast AST) (arts []Artifact) { - ctx := Context(wf.Debugger, wf.params, wf.params.OutputPath()) - - wf.Debug("initializing modules") - for _, m := range wf.mods { - m.InitContext(ctx.Push(m.Name())) - } - - wf.Debug("executing modules") - for _, m := range wf.mods { - arts = append(arts, m.Execute(ast.Targets(), ast.Packages())...) - } - - return -} - -func (wf *standardWorkflow) Persist(arts []Artifact) { - resp := wf.persister.Persist(arts...) - - data, err := proto.Marshal(resp) - wf.CheckErr(err, "marshaling output proto") - - n, err := wf.out.Write(data) - wf.CheckErr(err, "writing output proto") - wf.Assert(len(data) == n, "failed to write all output") - - wf.Debug("rendering successful") -} - -// onceWorkflow wraps an existing workflow, executing its methods exactly -// once. Subsequent calls will ignore their inputs and use the previously -// provided values. -type onceWorkflow struct { - workflow - - initOnce sync.Once - ast AST - - runOnce sync.Once - arts []Artifact - - persistOnce sync.Once -} - -func (wf *onceWorkflow) Init(g *Generator) AST { - wf.initOnce.Do(func() { wf.ast = wf.workflow.Init(g) }) - return wf.ast -} - -func (wf *onceWorkflow) Run(ast AST) []Artifact { - wf.runOnce.Do(func() { - wf.arts = wf.workflow.Run(ast) - }) - return wf.arts -} - -func (wf *onceWorkflow) Persist(artifacts []Artifact) { - wf.persistOnce.Do(func() { wf.workflow.Persist(artifacts) }) -} diff --git a/v2/workflow_test.go b/v2/workflow_test.go deleted file mode 100644 index a5a64d3..0000000 --- a/v2/workflow_test.go +++ /dev/null @@ -1,100 +0,0 @@ -package pgs - -import ( - "bytes" - "io/ioutil" - "testing" - - "github.com/golang/protobuf/proto" - plugin_go "github.com/golang/protobuf/protoc-gen-go/plugin" - "github.com/stretchr/testify/assert" -) - -func TestStandardWorkflow_Init(t *testing.T) { - t.Parallel() - - req := &plugin_go.CodeGeneratorRequest{FileToGenerate: []string{"foo"}} - b, err := proto.Marshal(req) - assert.NoError(t, err) - - mutated := false - - g := Init(ProtocInput(bytes.NewReader(b)), MutateParams(func(p Parameters) { mutated = true })) - g.workflow.Init(g) - - assert.True(t, mutated) - - t.Run("bidi", func(t *testing.T) { - mutated = false - g = Init(ProtocInput(bytes.NewReader(b)), BiDirectional(), MutateParams(func(p Parameters) { mutated = true })) - g.workflow.Init(g) - - assert.True(t, mutated) - }) -} - -func TestStandardWorkflow_Run(t *testing.T) { - t.Parallel() - - g := Init() - g.workflow = &standardWorkflow{Generator: g} - g.params = Parameters{} - - m := newMockModule() - m.name = "foo" - - g.RegisterModule(m) - g.workflow.Run(&graph{}) - - assert.True(t, m.executed) -} - -func TestStandardWorkflow_Persist(t *testing.T) { - t.Parallel() - - g := Init(ProtocOutput(ioutil.Discard)) - g.workflow = &standardWorkflow{Generator: g} - g.persister = dummyPersister(g.Debugger) - - assert.NotPanics(t, func() { g.workflow.Persist(nil) }) -} - -func TestOnceWorkflow(t *testing.T) { - t.Parallel() - - d := &dummyWorkflow{ - AST: &graph{}, - Artifacts: []Artifact{&CustomFile{}}, - } - wf := &onceWorkflow{workflow: d} - - ast := wf.Init(nil) - arts := wf.Run(ast) - wf.Persist(arts) - - assert.True(t, d.initted) - assert.True(t, d.run) - assert.True(t, d.persisted) - - d = &dummyWorkflow{} - wf.workflow = d - - assert.Equal(t, ast, wf.Init(nil)) - assert.Equal(t, arts, wf.Run(ast)) - wf.Persist(arts) - - assert.False(t, d.initted) - assert.False(t, d.run) - assert.False(t, d.persisted) -} - -type dummyWorkflow struct { - AST AST - Artifacts []Artifact - - initted, run, persisted bool -} - -func (wf *dummyWorkflow) Init(g *Generator) AST { wf.initted = true; return wf.AST } -func (wf *dummyWorkflow) Run(ast AST) []Artifact { wf.run = true; return wf.Artifacts } -func (wf *dummyWorkflow) Persist(arts []Artifact) { wf.persisted = true }