pgs

package module
v0.3.7 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 23, 2018 License: Apache-2.0 Imports: 28 Imported by: 0

README

protoc-gen-star (PGS) Build Status

!!! THIS PROJECT IS A WORK-IN-PROGRESS | THE API SHOULD BE CONSIDERED UNSTABLE !!!

PGS is a protoc plugin library for efficient proto-based code generation

package main

import "github.com/lyft/protoc-gen-star"

func main() {
	pgs.Init(pgs.IncludeGo()).
		RegisterPlugin(&myProtocGenGoPlugin{}).
		RegisterModule(&myPGSModule{}).
		RegisterPostProcessor(&myPostProcessor{}).
		Render()
}

protoc-gen-star (PGS) is built on top of the official protoc-gen-go (PGG) protoc plugin. PGG contains a mechanism for extending its behavior with plugins (for instance, gRPC support via a plugin). However, this feature is not accessible from the outside and requires either forking PGG or replicating its behavior using its library code. Further still, the PGG plugins are designed specifically for extending the officially generated Go code, not creating other new files or packages.

PGS leverages the existing PGG library code to properly build up the Protocol Buffer (PB) descriptors' dependency graph before handing it off to custom Modules to generate anything above-and-beyond the officially generated code. In fact, by default PGS does not generate the official Go code. While PGS is written in Go and relies on PGG, this library can be used to generate code in any language.

Features

Documentation

While this README seeks to describe many of the nuances of protoc plugin development and using PGS, the true documentation source is the code itself. The Go language is self-documenting and provides tools for easily reading through it and viewing examples. Until this package is open sourced, the documentation can be viewed locally by running make docs, which will start a godoc server and open the documentation in the default browser.

Roadmap
  • Full support for official Go PB output and protoc-gen-go plugins, can replace protoc-gen-go
  • Interface-based and fully-linked dependency graph with access to raw descriptors
  • Built-in context-aware debugging capabilities
  • Exhaustive, near 100% unit test coverage
  • End-to-end testable via overrideable IO
  • Visitor pattern and helpers for efficiently walking the dependency graph
  • BuildContext to facilitate complex generation
  • Parsed, typed command-line Parameters access
  • Extensible PluginBase for quickly creating protoc-gen-go plugins
  • Extensible ModuleBase for quickly creating Modules and facilitating code generation
  • Configurable post-processing (eg, gofmt/goimports) of generated files
  • Support processing proto files from multiple packages (normally disallowed by protoc-gen-go)
  • Load plugins/modules at runtime using Go shared libraries
  • Load comments from proto files into gathered AST for easy access
  • More intelligent Go import path resolution
Examples

protoc-gen-example, can be found in the testdata directory. It includes both Plugin and Module implementations using a variety of the features available. It's protoc execution is included in the demo Makefile target. Test examples are also accessible via the documentation by running make docs.

How It Works

The protoc Flow

Because the process is somewhat confusing, this section will cover the entire flow of how proto files are converted to generated code, using a hypothetical PGS plugin: protoc-gen-myplugin. A typical execution looks like this:

protoc \
	-I . \
	--myplugin_out="plugins=grpc:../generated" \
	./pkg/*.proto

protoc, the PB compiler, is configured using a set of flags (documented under protoc -h) and handed a set of files as arguments. In this case, the I flag can be specified multiple times and is the lookup path it should use for imported dependencies in a proto file. By default, the official descriptor protos are already included.

myplugin_out tells protoc to use the protoc-gen-myplugin protoc-plugin. These plugins are automatically resolved from the system's PATH environment variable, or can be explicitly specified with another flag. The official protoc-plugins (eg, protoc-gen-python) are already registered with protoc. The flag's value is specific to the particular plugin, with the exception of the :../generated suffix. This suffix indicates the root directory in which protoc will place the generated files from that package (relative to the current working directory). This generated output directory is not propagated to protoc-gen-myplugin, however, so it needs to be duplicated in the left-hand side of the flag. PGS supports this via an output_path parameter.

protoc parses the passed in proto files, ensures they are syntactically correct, and loads any imported dependencies. It converts these files and the dependencies into descriptors (which are themselves PB messages) and creates a CodeGeneratorRequest (yet another PB). protoc serializes this request and then executes each configured protoc-plugin, sending the payload via stdin.

protoc-gen-myplugin starts up, receiving the request payload, which it unmarshals. There are two phases to a PGS-based protoc-plugin. First, the standard PGG process is executed against the input. This allows for generation of the official Go code if desired, as well as applying any PGG plugins we've specified in its protoc flag (in this case, we opted to use grpc). PGS, also injects a plugin here called the gatherer, which constructs a dependency graph from the incoming descriptors. This process populates a CodeGeneratorResponse PB message containing the files to generate.

When this step is complete, PGS then executes any registered Modules, handing it the constructed graph. Modules can be written to generate more files, adding them to the response PB, writing them to disk directly, or just performing some form of validation over the provided graph without any other side effects. Modules provide the most flexibility in terms of operating against the PBs.

Once all Modules are complete, protoc-gen-myplugin serializes the CodeGeneratorResponse and writes the data to its stdout. protoc receives this payload, unmarshals it, and writes any requested files to disk after all its plugins have returned. This whole flow looked something like this:

foo.proto → protoc → CodeGeneratorRequest → protoc-gen-myplugin → CodeGeneratorResponse → protoc → foo.pb.go

The PGS library hides away nearly all of this complexity required to implement a protoc-plugin!

Plugins

Plugins in this context refer to libraries registered with the PGG library to extend the functionality of the offiically generated Go code. The only officially supported extension is grpc, which generates the server and client code for services defined in the proto files. This is one of the best ways to extend the behavior of the already generated code within its package.

PGS provides a PluginBase struct to simplify development of these plugins. Out of the box, it satisfies the interface for a generator.Plugin, only requiring the creation of the Name and Generate methods. PluginBase is best used as an anonymous embedded field of a wrapping Plugin implementation. A minimal plugin would look like the following:

// GraffitiPlugin tags the generated Go source
type graffitiPlugin struct {
	*pgs.PluginBase
	tag string
}

// New configures the plugin with an instance of PluginBase and captures the tag
// which will be used during code generation.
func New(tag string) pgs.Plugin { 
	return &graffitiPlugin{
		PluginBase: new(pgs.PluginBase),
		tag:        tag,
	}
}

// Name is the identifier used in the protoc execution to enable this plugin for 
// code generation.
func (p *graffitiPlugin) Name() string { return "graffiti" }

// Generate is handed each file descriptor loaded by protoc, including 
// dependencies not targeted for building. Don't worry though, the underlying 
// library ensures that writes only occur for those specified by protoc.
func (p *graffitiPlugin) Generate(f *generator.FileDescriptor) {
	p.Push(f.GetName()).Debug("tagging")
	p.C(80, p.tag)
	p.Pop()
}

PluginBase exposes a PGS BuildContext instance, already prefixed with the plugin's name. Calling Push and Pop allows adding further information to error and debugging messages. Above, the name of the file being generated is pushed onto the context before logging the "tagging" debug message.

The base also provides helper methods for rendering into the output file. p.P prints a line with arbitrary arguments, similar to fmt.Println. p.C renders a comment in a similar fashion to p.P but intelligently wraps the comment to multiple lines at the specified width (above, 80 is used to wrap the supplied tag value). While p.P and p.C are very procedural, sometimes smarter generation is required: p.T renders Go templates (of either the text or html variety).

Typically, plugins are registered globally, usually within an init method on the plugin's package, but PGS provides some utilities to facilitate development. When registering it with a PGS Generator, however, the init methodology should be avoided in favor of the following:

g := pgs.Init(pgs.IncludeGo())
g.RegisterPlugin(graffiti.New("rodaine was here"))

IncludeGo must be specified or none of the official Go code will be generated. If the plugin also implements the PGS Plugin interface (which is achieved for free by composing over PluginBase), a shared pre-configured BuildContext will be provided to the plugin for consistent logging and error handling mechanisms.

Modules

While plugins allow for injecting into the PGG generated code file-by-file, some code generation tasks require knowing the entire dependency graph of a proto file first or intend to create files on disk outside of the output directory specified by protoc (or with custom permissions). Modules fill this gap.

PGS Modules are evaluated after the normal PGG flow and are handed a complete graph of the PB entities from the gatherer that are targeted for generation as well as all dependencies. A Module can then add files to the protoc CodeGeneratorResponse or write files directly to disk as Artifacts.

PGS provides a ModuleBase struct to simplify developing modules. Out of the box, it satisfies the interface for a Module, only requiring the creation of Name and Execute methods. ModuleBase is best used as an anonyomous embedded field of a wrapping Module implementation. A minimal module would look like the following:

// ReportModule creates a report of all the target messages generated by the 
// protoc run, writing the file into the /tmp directory.
type reportModule struct {
	*pgs.ModuleBase
}

// New configures the module with an instance of ModuleBase
func New() pgs.Module { return &reportModule{&pgs.ModuleBase{}} }

// Name is the identifier used to identify the module. This value is 
// automatically attached to the BuildContext associated with the ModuleBase.
func (m *reportModule) Name() string { return "reporter" }

// Execute is passed the target pkg as well as its dependencies in the pkgs map.
// The implementation should return a slice of Artifacts that represent the 
// files to be generated. In this case, "/tmp/report.txt" will be created 
// outside of the normal protoc flow.
func (m *reportModule) Execute(pkg pgs.Package, pkgs map[string]pgs.Package) []pgs.Artifact {
	buf := &bytes.Buffer{}

	for _, f := range pkg.Files() {
		m.Push(f.Name().String()).Debug("reporting")

		fmt.Fprintf(buf, "--- %v ---", f.Name())
	
		for i, msg := range f.AllMessages() {
			fmt.Fprintf(buf, "%03d. %v", msg.Name())
		}

		m.Pop()
	}

	m.OverwriteCustomFile(
		"/tmp/report.txt",
		buf.String(),
		0644,
	)

	return m.Artifacts()
}

ModuleBase exposes a PGS BuildContext instance, already prefixed with the module's name. Calling Push and Pop allows adding further information to error and debugging messages. Above, each file from the target package is pushed onto the context before logging the "reporting" debug message.

The base also provides helper methods for adding or overwriting both protoc-generated and custom files. The above execute method creates a custom file at /tmp/report.txt specifying that it should overwrite an existing file with that name. If it instead called AddCustomFile and the file existed, no file would have been generated (though a debug message would be logged out). Similar methods exist for adding generator files, appends, and injections. Likewise, methods such as AddCustomTemplateFile allows for Templates to be rendered instead.

After all modules have been executed, the returned Artifacts are either placed into the CodeGenerationResponse payload for protoc or written out to the file system. For testing purposes, the file system has been abstracted such that a custom one (such as an in-memory FS) can be provided to the PGS generator with the FileSystem InitOption.

Modules are registered with PGS similar to Plugins:

g := pgs.Init(pgs.IncludeGo())
g.RegisterModule(reporter.New())
Multi-Package Aware Modules

If the MultiPackage InitOption is enabled and multiple packages are passed into the PGS plugin, a Module can be upgraded to a MultiModule interface to support handling more than one target package simultaneously. Implementing this on the reportModule above might look like the following:

// MultiExecute satisfies the MultiModule interface. Instead of calling Execute 
// and generating a file for each target package, the report can be written 
// including all files from all packages in one.
func (m *reportModule) MultiExecute(targets map[string]Package, pkgs map[string]Package) []Artifact {
	buf := &bytes.Buffer{}

	for _, pkg := range targets {
		m.Push(pkg.Name().String())
		for _, f := range pkg.Files() {
			m.Push(f.Name().String()).Debug("reporting")

			fmt.Fprintf(buf, "--- %v ---", f.Name())
		
			for i, msg := range f.AllMessages() {
				fmt.Fprintf(buf, "%03d. %v", msg.Name())
			}

			m.Pop()
		}
		m.Pop()
	}

	m.OverwriteCustomFile(
		"/tmp/report.txt",
		buf.String(),
		0644,
	)

	return m.Artifacts()
}

Without MultiExecute, the module's Execute method would be called for each individual target Package processed. In the above example, the report file would be created for each, possibly overwriting each other. If a Module implements MultiExecute, however, the method recieves all target packages at once and can choose how to process them, in this case, creating a single report file for all.

See the Multi-Package Workflow section below for more details.

Post Processing

Artifacts generated by Modules sometimes require some mutations prior to writing to disk or sending in the reponse to protoc. This could range from running gofmt against Go source or adding copyright headers to all generated source files. To simplify this task in PGS, a PostProcessor can be utilized. A minimal looking PostProcessor implementation might look like this:

// New returns a PostProcessor that adds a copyright comment to the top
// of all generated files.
func New(owner string) pgs.PostProcessor { return copyrightPostProcessor{owner} }

type copyrightPostProcessor struct {
	owner string
}

// Match returns true only for Custom and Generated files (including templates).
func (cpp copyrightPostProcessor) Match(a pgs.Artifact) bool {
	switch a := a.(type) {
	case pgs.GeneratorFile, pgs.GeneratorTemplateFile, 
		pgs.CustomFile, pgs.CustomTemplateFile:
			return true
	default:
			return false
	}
}

// Process attaches the copyright header to the top of the input bytes
func (cpp copyrightPostProcessor) Process(in []byte) (out []byte, err error) {
	cmt := fmt.Sprintf("// Copyright © %d %s. All rights reserved\n", 
		time.Now().Year(), 
		cpp.owner)

	return append([]byte(cmt), in...), nil
}

The copyrightPostProcessor struct satisfies the PostProcessor interface by implementing the Match and Process methods. After PGS recieves all Artifacts, each is handed in turn to each registered processor's Match method. In the above case, we return true if the file is a part of the targeted Artifact types. If true is returned, Process is immediately called with the rendered contents of the file. This method mutates the input, returning the modified value to out or an error if something goes wrong. Above, the notice is prepended to the input.

PostProcessors are registered with PGS similar to Plugins and Modules:

g := pgs.Init(pgs.IncludeGo())
g.RegisterModule(some.NewModule())
g.RegisterPostProcessor(copyright.New("PGS Authors"))

Protocol Buffer AST

While protoc ensures that all the dependencies required to generate a proto file are loaded in as descriptors, it's up to the protoc-plugins to recognize the relationships between them. PGG handles this to some extent, but does not expose it in a easily accessible or testable manner outside of its sub-plugins and standard generation. To get around this, PGS uses the gatherer plugin to construct an abstract syntax tree (AST) of all the Entities loaded into the plugin. This AST is provided to every Module to facilitate code generation.

Hierarchy

The hierarchy generated by the PGS gatherer is fully linked, starting at a top-level Package down to each individual Field of a Message. The AST can be represented with the following digraph:

A Package describes a set of Files loaded within the same namespace. As would be expected, a File represents a single proto file, which contains any number of Message, Enum or Service entities. An Enum describes an integer-based enumeration type, containing each individual EnumValue. A Service describes a set of RPC Methods, which in turn refer to their input and output Messages.

A Message can contain other nested Messages and Enums as well as each of its Fields. For non-scalar types, a Field may also reference its Message or Enum type. As a mechanism for achieving union types, a Message can also contain OneOf entities that refer to some of its Fields.

Visitor Pattern

The structure of the AST can be fairly complex and unpredictable. Likewise, Module's are typically concerned with only a subset of the entities in the graph. To separate the Module's algorithm from understanding and traversing the structure of the AST, PGS implements the Visitor pattern to decouple the two. Implementing this interface is straightforward and can greatly simplify code generation.

Two base Visitor structs are provided by PGS to simplify developing implementations. First, the NilVisitor returns an instance that short-circuits execution for all Entity types. This is useful when certain branches of the AST are not interesting to code generation. For instance, if the Module is only concerned with Services, it can use a NilVisitor as an anonymous field and only implement the desired interface methods:

// ServiceVisitor logs out each Method's name
type serviceVisitor struct {
	pgs.Visitor
	pgs.DebuggerCommon
}

func New(d pgs.DebuggerCommon) pgs.Visitor { 
	return serviceVistor{
		Visitor:        pgs.NilVisitor(),
		DebuggerCommon: d,
	} 
}

// Passthrough Packages, Files, and Services. All other methods can be 
// ignored since Services can only live in Files and Files can only live in a 
// Package.
func (v serviceVisitor) VisitPackage(pgs.Package) (pgs.Visitor, error) { return v, nil }
func (v serviceVisitor) VisitFile(pgs.File) (pgs.Visitor, error)       { return v, nil }
func (v serviceVisitor) VisitService(pgs.Service) (pgs.Visitor, error) { return v, nil }

// VisitMethod logs out ServiceName#MethodName for m.
func (v serviceVisitor) VisitMethod(m pgs.Method) (pgs.Vistitor, error) {
	v.Logf("%v#%v", m.Service().Name(), m.Name())
	return nil, nil
}

If access to deeply nested Nodes is desired, a PassthroughVisitor can be used instead. Unlike NilVisitor and as the name suggests, this implementation passes through all nodes instead of short-circuiting on the first unimplemented interface method. Setup of this type as an anonymous field is a bit more complex but avoids implementing each method of the interface explicitly:

type fieldVisitor struct {
	pgs.Visitor
	pgs.DebuggerCommon
}

func New(d pgs.DebuggerCommon) pgs.Visitor {
	v := &fieldVisitor{DebuggerCommon: d}
	v.Visitor = pgs.PassThroughVisitor(v)
	return v
}

func (v *fieldVisitor) VisitField(f pgs.Field) (pgs.Visitor, error) {
	v.Logf("%v.%v", f.Message().Name(), f.Name())
	return nil, nil
}

Walking the AST with any Visitor is straightforward:

v := visitor.New(d)
err := pgs.Walk(v, pkg)

All Entity types and Package can be passed into Walk, allowing for starting a Visitor lower than the top-level Package if desired.

Build Context

Plugins and Modules registered with the PGS Generator are initialized with an instance of BuildContext that encapsulates contextual paths, debugging, and parameter information.

Output Paths

The BuildContext's OutputPath method returns the output directory that the PGS plugin is targeting. For Plugins, this path is initially . and is relative to the generation output directory specified in the protoc execution. For Modules, this path is also initially . but refers to the directory in which protoc is executed. This default behavior can be overridden for Modules by providing an output_path in the flag.

This value can be used to create file names for Artifacts, using JoinPath(name ...string) which is essentially an alias for filepath.Join(ctx.Outpath, name...). Manually tracking directories relative to the OutputPath can be tedious, especially if the names are dynamic. Instead, a BuildContext can manage these, via PushDir and PopDir.

ctx.OutputPath()                // foo
ctx.JoinPath("fizz", "buzz.go") // foo/fizz/buzz.go

ctx = ctx.PushDir("bar/baz")
ctx.OutputPath()                // foo/bar/baz
ctx.JoinPath("quux.go")         // foo/bar/baz/quux.go

ctx = ctx.PopDir()
ctx.OutputPath()                // foo

Both PluginBase and ModuleBase wrap these methods to mutate their underlying BuildContexts. Those methods should be used instead of the ones on the contained BuildContext directly.

Debugging

The BuildContext exposes a DebuggerCommon interface which provides utilities for logging, error checking, and assertions. Log and the formatted Logf print messages to os.Stderr, typically prefixed with the Plugin or Module name. Debug and Debugf behave the same, but only print if enabled via the DebugMode or DebugEnv InitOptions.

Fail and Failf immediately stops execution of the protoc-plugin and causes protoc to fail generation with the provided message. CheckErr and Assert also fail with the provided messages if an error is passed in or if an expression evaluates to false, respectively.

Additional contextual prefixes can be provided by calling Push and Pop on the BuildContext. This behavior is similar to PushDir and PopDir but only impacts log messages. Both PluginBase and ModuleBase wrap these methods to mutate their underlying BuildContexts. Those methods should be used instead of the ones on the contained BuildContext directly.

Parameters

The BuildContext also provides access to the pre-processed Parameters from the specified protoc flag. PGG allows for certain KV pairs in the parameters body, such as "plugins", "import_path", and "import_prefix" as well as import maps with the "M" prefix. PGS exposes these plus typed access to any other KV pairs passed in. The only PGS-specific key expected is "output_path", which is utilized by the a module's BuildContext for its OutputPath.

PGS permits mutating the Parameters via the MutateParams InitOption. By passing in a ParamMutator function here, these KV pairs can be modified or verified prior to the PGG workflow begins.

Execution Workflows

Internally, PGS determines its behavior based off workflows. These are not publicly exposed to the API but can be modified based off InitOptions when initializing the Generator.

Standard Workflow

The standard workflow follows the steps described above in The protoc Flow. This is the out-of-the-box behavior of PGS-based plugins.

Multi-Package Workflow

Due to purely philosophical reasons, PGG does not support passing in more than one package (ie, directory) of proto files at a time. In most circumstances, this is OK (if a bit annoying), however there are some generation patterns that may require loading in multiple packages/directories of protos simultaneously. By enabling this workflow, a PGS plugin will support running against multiple packages.

This is achieved by splitting the CodeGeneratorRequest into multiple sub-requests, spawning a handful of child processes of the PGS plugin, and executing the PGG workflow against each sub-request independently. The parent process acts like protoc in this case, and captures the response of these before merging them together into a single CodeGeneratorResponse. Modules are not executed in the child processes; instead, the parent process executes them. If a Module implements the MultiModule interface, the MultiExecute method will be called with all target Packages simultaneously. Otherwise, the Execute method is called separately for each target Package.

CAVEATS: This workflow significantly changes the behavior from the Standard workflow and should be considered experimental. Also, the ProtocInput InitOption cannot be specified alongside this workflow. Changing the input will prevent the sub-requests from being properly executed. (A future update may make this possible.) Only enable this option if your plugin necessitates multi-package support.

To enable this workflow, pass the MultiPackage InitOption to Init.

Exclude Go Workflow

It is not always desirable for a PGS plugin to also generate the official Go source code coming from the PGG library (eg, when not generating Go code). In fact, by default, these files are not generated by PGS plugins. This is achieved by this workflow which decorates another workflow (typically, Standard or Multi-Package) to remove these files from the set of generated files.

To disable this workflow, pass the IncludeGo InitOption to Init.

PGS Development & Make Targets

PGS seeks to provide all the tools necessary to rapidly and ergonomically extend and build on top of the Protocol Buffer IDL. Whether the goal is to modify the official protoc-gen-go output or create entirely new files and packages, this library should offer a user-friendly wrapper around the complexities of the PB descriptors and the protoc-plugin workflow.

Setup

For developing on PGS, you should install the package within the GOPATH. PGS uses glide for dependency management.

go get -u github.com/lyft/protoc-gen-star
cd $GOPATH/github.com/lyft/protoc-gen-star
make install 

To upgrade dependencies, please make the necessary modifications in glide.yaml and run glide update.

Linting & Static Analysis

To avoid style nits and also to enforce some best practices for Go packages, PGS requires passing golint, go vet, and go fmt -s for all code changes.

make lint
Testing

PGS strives to have near 100% code coverage by unit tests. Most unit tests are run in parallel to catch potential race conditions. There are three ways of running unit tests, each taking longer than the next but providing more insight into test coverage:

# run unit tests without race detection or code coverage reporting
make quick 

# run unit tests with race detection and code coverage
make tests 

# run unit tests with race detection and generates a code coverage report, opening in a browser
make cover 
Documentation

As PGS is intended to be an open-source utility, good documentation is important for consumers. Go is a self-documenting language, and provides a built in utility to view locally: godoc. The following command starts a godoc server and opens a browser window to this package's documentation. If you see a 404 or unavailable page initially, just refresh.

make docs
Demo

PGS comes with a "kitchen sink" example: protoc-gen-example. This protoc plugin built on top of PGS prints out the target package's AST as a tree to stderr. This provides an end-to-end way of validating each of the nuanced types and nesting in PB descriptors:

make demo
CI

PGS uses TravisCI to validate all code changes. Please view the configuration for what tests are involved in the validation.

Documentation

Overview

Package pgs provides a library for building protoc plugins

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func C

func C(wrap int, args ...interface{}) string

C returns a comment block, wrapping when the line's length will exceed wrap.

func C80

func C80(args ...interface{}) string

C80 is an alias for C(80, args...)

func Walk

func Walk(v Visitor, n Node) error

Walk applies a depth-first visitor pattern with v against Node n.

Types

type Artifact

type Artifact interface {
	// contains filtered or unexported methods
}

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 BuildContext

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
}

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.

func Context

func Context(d Debugger, params Parameters, output string) BuildContext

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.

type Commenter added in v0.3.2

type Commenter interface {
	Comments() string
}

Commenter is a interface used by any node or entity which could have comments.

type CustomFile

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
}

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 CustomTemplateFile

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
}

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 Debugger

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
}

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 DebuggerCommon

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)
}

DebuggerCommon contains shared features of Debugger and Debugger-like types (such as BuildContext).

type Entity

type Entity interface {
	Node
	Commenter

	// The Name of the entity
	Name() Name

	// The fully qualified name of the entity. For example, a message
	// 'HelloRequest' in a 'helloworld' package it 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 all external packages required by this entity.
	Imports() []Package

	// 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 is
	// returned if the extension is not found or there is a type mismatch between
	// desc and ext. The ok value will be true if the extension was found.
	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
}

Entity describes any member of the proto AST that is extensible via options. All nodes file and below are considered entities.

type Enum

type Enum interface {
	Entity

	// TypeName returns the type of this enum as it would be created in Go.
	// This value will only differ from Name for nested enums.
	TypeName() TypeName

	// Descriptor returns the proto descriptor for this Enum
	Descriptor() *generator.EnumDescriptor

	// Parent resolves to either a Message or File that directly contains this
	// Enum.
	Parent() ParentEntity

	// Values returns each defined enumeration value.
	Values() []EnumValue
	// contains filtered or unexported methods
}

Enum describes an enumeration type. Its parent can be either a Message or a File.

type EnumValue

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
	// contains filtered or unexported methods
}

An EnumValue describes a name-value pair for an entry in an enum.

type Field

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.
	InOneOf() bool

	// OneOf returns the OneOf that this field is apart of. Nil is returned if
	// the field is not within a OneOf.
	OneOf() OneOf

	// Type returns the FieldType of this Field.
	Type() FieldType
	// contains filtered or unexported methods
}

A Field describes a member of a Message. A field may also be a member of a OneOf on the Message.

type FieldType

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

	// Name returns the TypeName for this Field, which represents the type of the
	// field as it would exist in Go source code.
	Name() TypeName

	// 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 message's syntax is not Proto2 or
	// the field is prefixed as optional.
	IsOptional() bool

	// IsRequired returns true if and only if the field is prefixed as required.
	IsRequired() bool

	// IsSlice returns true if the field is represented in Go as a slice. This
	// method returns true only for repeated and bytes-type fields.
	IsSlice() 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 packages required by this field.
	Imports() []Package

	// 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
	// contains filtered or unexported methods
}

FieldType describes the type of a Field.

type FieldTypeElem

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

	// Name returns the TypeName describing this component (independent of the
	// parent FieldType).
	Name() TypeName

	// Imports includes all external packages required by this field.
	Imports() []Package

	// 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
	// contains filtered or unexported methods
}

FieldTypeElem describes a component of a FieldType. This type only shows up in repeated and map FieldTypes.

type File

type File interface {
	ParentEntity

	// InputPath returns the input FilePath of the generated Go code. This is
	// equivalent to the value returned by Name.
	InputPath() FilePath

	// OutputPath returns the output filepath of the generated Go code
	OutputPath() FilePath

	// Descriptor returns the underlying descriptor for the proto file
	Descriptor() *generator.FileDescriptor

	// Services returns the top-level services from this proto file.
	Services() []Service
	// contains filtered or unexported methods
}

File describes the contents of a single proto file.

type FilePath

type FilePath string

A FilePath describes the name of a file or directory. This type simplifies path related operations.

func JoinPaths

func JoinPaths(elem ...string) FilePath

JoinPaths is an convenient alias around filepath.Join, to easily create FilePath types.

func (FilePath) Base

func (n FilePath) Base() string

Base returns the base of the current FilePath (the last element in the path). This method is an alias around filepath.Base.

func (FilePath) BaseName

func (n FilePath) BaseName() string

BaseName returns the Base of the current FilePath without Ext.

func (FilePath) Dir

func (n FilePath) Dir() FilePath

Dir returns the parent directory of the current FilePath. This method is an alias around filepath.Dir.

func (FilePath) Ext

func (n FilePath) Ext() 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 (FilePath) Pop

func (n FilePath) Pop() FilePath

Pop returns a new FilePath with the last element removed

func (FilePath) Push

func (n FilePath) Push(elem string) FilePath

Push returns a new FilePath with elem added to the end

func (FilePath) SetBase

func (n FilePath) SetBase(base string) FilePath

SetBase returns a new FilePath with the base element replaced with base.

func (FilePath) SetExt

func (n FilePath) SetExt(ext string) FilePath

SetExt returns a new FilePath with the extension replaced with ext.

func (FilePath) String

func (n FilePath) String() string

String satisfies the strings.Stringer interface.

type Generator

type Generator struct {
	Debugger
	// contains filtered or unexported fields
}

Generator replaces the standard protoc-gen-go generator.Generator. It permits the registration of both standard protoc-gen-go plugins (eg, grpc) that are in-band with the officially generated code as well as Modules which enable creating out-of-band code using a computed protobuf AST.

func Init

func Init(opts ...InitOption) *Generator

Init configures a new Generator. InitOptions may be provided as well to modify the behavior of the generator.

func (*Generator) AST

func (g *Generator) AST() (targets map[string]Package, pkgs map[string]Package)

AST returns the target Packages as well as all loaded Packages from the gatherer. Calling this method will trigger running the underlying protoc-gen-go workflow, including any registered plugins. Render can be safely called before or after this method without impacting module execution or writing the output to the output io.Writer. This method is particularly useful for integration-type tests.

func (*Generator) RegisterModule

func (g *Generator) RegisterModule(m ...Module) *Generator

RegisterModule should be called after Init but before Render to attach a custom Module to the Generator. This method can be called multiple times.

func (*Generator) RegisterPlugin

func (g *Generator) RegisterPlugin(p ...generator.Plugin) *Generator

RegisterPlugin attaches protoc-gen-go plugins to the Generator. If p implements the protoc-gen-star Plugin interface, a Debugger will be passed in. This method is solely a wrapper around generator.RegisterPlugin. When designing these, all context should be cleared when Init is called. Note that these are currently global in scope and not specific to this generator instance.

func (*Generator) RegisterPostProcessor

func (g *Generator) RegisterPostProcessor(p ...PostProcessor) *Generator

RegisterPostProcessor should be called after Init but 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. Only Artifacts generated by Modules are processed.

func (*Generator) Render

func (g *Generator) Render()

Render emits all generated files from the plugins and Modules to the output io.Writer. If out is nil, os.Stdout is used. Render can only be called once and should be preceded by Init and any number of RegisterPlugin and/or RegisterModule calls.

type GeneratorAppend

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
}

A GeneratorAppend Artifact appends content to the end of the specified protoc generated file. This Artifact can only be used if another Plugin or Module generates a file with the same name.

func (GeneratorAppend) ProtoFile

func (f GeneratorAppend) ProtoFile() (*plugin_go.CodeGeneratorResponse_File, error)

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.

type GeneratorArtifact

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)
}

GeneratorArtifact describes an Artifact that uses protoc for code generation.

type GeneratorFile

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
}

A GeneratorFile Artifact describes a file to be generated using protoc.

func (GeneratorFile) ProtoFile

func (f GeneratorFile) ProtoFile() (*plugin_go.CodeGeneratorResponse_File, error)

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.

type GeneratorInjection

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
}

A GeneratorInjection Artifact inserts content into a protoc generated file at the specified insertion point. The target file does not need to generated by this protoc-plugin but must be generated by an prior plugin executed by protoc.

func (GeneratorInjection) ProtoFile

func (f GeneratorInjection) ProtoFile() (*plugin_go.CodeGeneratorResponse_File, error)

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.

type GeneratorTemplateAppend

type GeneratorTemplateAppend struct {
	GeneratorArtifact
	TemplateArtifact

	// Filename of the file to append to, relative to the protoc-plugin's generation
	// output directory.
	FileName string
}

A GeneratorTemplateAppend appends content to a protoc generated file from a Template. See GeneratorAppend for limitations.

func (GeneratorTemplateAppend) ProtoFile

func (f GeneratorTemplateAppend) ProtoFile() (*plugin_go.CodeGeneratorResponse_File, error)

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.

type GeneratorTemplateFile

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
}

A GeneratorTemplateFile describes a file to be generated using protoc from a Template.

func (GeneratorTemplateFile) ProtoFile

func (f GeneratorTemplateFile) ProtoFile() (*plugin_go.CodeGeneratorResponse_File, error)

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.

type GeneratorTemplateInjection

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
}

A GeneratorTemplateInjection Artifact inserts content rendered from a Template into a protoc generated file at the specified insertion point. The target file does not need to generated by this protoc-plugin but must be generated by an prior plugin executed by protoc.

func (GeneratorTemplateInjection) ProtoFile

func (f GeneratorTemplateInjection) ProtoFile() (*plugin_go.CodeGeneratorResponse_File, error)

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.

type InitOption

type InitOption func(g *Generator)

An InitOption modifies the behavior of a Generator at initialization.

func DebugEnv

func DebugEnv(f string) InitOption

DebugEnv enables verbose logging only if the passed in environment variable is non-empty.

func DebugMode

func DebugMode() InitOption

DebugMode enables verbose logging for module development and debugging.

func FileSystem

func FileSystem(fs afero.Fs) InitOption

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 IncludeGo

func IncludeGo() InitOption

IncludeGo permits generation of the standard Go protocol buffer code alongside any custom modules. By default, none of the standard protoc-gen-go code is generated.

func MultiPackage

func MultiPackage() InitOption

MultiPackage indicates that the Generator should expect files from multiple packages simultaneously. Normally, protoc-gen-go disallows running against files from more than one package at a time.

func MutateParams

func MutateParams(pm ParamMutator) InitOption

MutateParams applies pm to the parameters passed in from protoc. The ParamMutator is applied prior to executing the protoc-gen-go workflow.

func ProtocInput

func ProtocInput(r io.Reader) InitOption

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 ProtocOutput

func ProtocOutput(w io.Writer) InitOption

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 RequirePlugin

func RequirePlugin(name ...string) InitOption

RequirePlugin force-enables any plugins with name, regardless of the parameters passed in from protoc.

type Message

type Message interface {
	ParentEntity

	// TypeName returns the type of this message as it would be created in Go.
	// This value will only differ from Name for nested messages.
	TypeName() TypeName

	// Descriptor returns the underlying proto descriptor for this message
	Descriptor() *generator.Descriptor

	// 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

	// OneOfs returns the OneOfs contained within this Message.
	OneOfs() []OneOf

	// 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
	// contains filtered or unexported methods
}

Message describes a proto message, akin to a struct in Go. 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 Method

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 this 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
	// contains filtered or unexported methods
}

Method describes a method on a proto service

type Module

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 Package as well as all
	// loaded Packages from the gatherer. The module should return a slice of
	// Artifacts that it would like to be generated. If a Module is used in
	// multi-package mode and this module does not implement MultiModule, Execute
	// will be called multiple times for each target Package.
	Execute(target Package, packages map[string]Package) []Artifact
}

Module describes the interface for a domain-specific code generation module that can be registered with the pgs generator. A module should be used over a generator.Plugin if generating code NOT included in the *.pg.go file is desired.

type ModuleBase

type ModuleBase struct {
	BuildContext
	// contains filtered or unexported fields
}

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.

func (*ModuleBase) AddArtifact

func (m *ModuleBase) AddArtifact(a ...Artifact)

AddArtifact adds a 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 (*ModuleBase) AddCustomFile

func (m *ModuleBase) AddCustomFile(name, content string, perms os.FileMode)

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 (*ModuleBase) AddCustomTemplateFile

func (m *ModuleBase) AddCustomTemplateFile(name string, tpl Template, data interface{}, perms os.FileMode)

AddCustomTemplateFile behaves the same as AddCustomFile, however the contents are rendered from the provided tpl and data.

func (*ModuleBase) AddGeneratorAppend

func (m *ModuleBase) AddGeneratorAppend(name, content string)

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 (*ModuleBase) AddGeneratorFile

func (m *ModuleBase) AddGeneratorFile(name, content string)

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 (*ModuleBase) AddGeneratorInjection

func (m *ModuleBase) AddGeneratorInjection(name, point, content string)

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 (*ModuleBase) AddGeneratorTemplateAppend

func (m *ModuleBase) AddGeneratorTemplateAppend(name string, tpl Template, data interface{})

AddGeneratorTemplateAppend behaves the same as AddGeneratorAppend, however the contents are rendered from the provided tpl and data.

func (*ModuleBase) AddGeneratorTemplateFile

func (m *ModuleBase) AddGeneratorTemplateFile(name string, tpl Template, data interface{})

AddGeneratorTemplateFile behaves the same as AddGeneratorFile, however the contents are rendered from the provided tpl and data.

func (*ModuleBase) AddGeneratorTemplateInjection

func (m *ModuleBase) AddGeneratorTemplateInjection(name, point string, tpl Template, data interface{})

AddGeneratorTemplateInjection behaves the same as AddGeneratorInjection, however the contents are rendered from the provided tpl and data.

func (*ModuleBase) Artifacts

func (m *ModuleBase) Artifacts() []Artifact

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 (*ModuleBase) Execute

func (m *ModuleBase) Execute(target Package, packages map[string]Package) []Artifact

Execute satisfies the Module interface, however this method will fail and must be overridden by a parent struct.

func (*ModuleBase) InitContext

func (m *ModuleBase) InitContext(c BuildContext)

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 (*ModuleBase) Name

func (m *ModuleBase) Name() string

Name satisfies the Module interface, however this method will panic and must be overridden by a parent struct.

func (*ModuleBase) OverwriteCustomFile

func (m *ModuleBase) OverwriteCustomFile(name, content string, perms os.FileMode)

OverwriteCustomFile behaves the same as AddCustomFile, however if the file already exists, it will be overwritten with this one.

func (*ModuleBase) OverwriteCustomTemplateFile

func (m *ModuleBase) OverwriteCustomTemplateFile(name string, tpl Template, data interface{}, perms os.FileMode)

OverwriteCustomTemplateFile behaves the same as OverwriteCustomFile, however the contents are rendered from the provided tpl and data.

func (*ModuleBase) OverwriteGeneratorFile

func (m *ModuleBase) OverwriteGeneratorFile(name, content string)

OverwriteGeneratorFile behaves the same as AddGeneratorFile, however if a previously executed Plugin or Module has created a file with the same name, it will be overwritten with this one.

func (*ModuleBase) OverwriteGeneratorTemplateFile

func (m *ModuleBase) OverwriteGeneratorTemplateFile(name string, tpl Template, data interface{})

OverwriteGeneratorTemplateFile behaves the same as OverwriteGeneratorFile, however the contents are rendered from the provided tpl and data.

func (*ModuleBase) Pop

func (m *ModuleBase) Pop() BuildContext

Pop removes the last push from the Module's BuildContext. This method should only be called after a paired Push or PushDir.

func (*ModuleBase) PopDir

func (m *ModuleBase) PopDir() BuildContext

PopDir removes the last PushDir from the Module's BuildContext. This method should only be called after a paired PushDir.

func (*ModuleBase) Push

func (m *ModuleBase) Push(prefix string) BuildContext

Push adds a prefix to the Module's BuildContext. Pop should be called when the context is complete.

func (*ModuleBase) PushDir

func (m *ModuleBase) PushDir(dir string) BuildContext

PushDir changes the OutputPath of the Module's BuildContext. Pop (or PopDir) should be called when that context is complete.

type MultiModule

type MultiModule interface {
	// MultiExecute is called instead of Execute for multi-package protoc
	// executions. If this method is not present on a Module, Execute is called
	// individually for each target package.
	MultiExecute(targets map[string]Package, packages map[string]Package) []Artifact
}

MultiModule adds special behavior to a Module that expects multi-package mode to be enabled for this protoc-plugin.

type Name

type Name string

A Name describes a symbol (Message, Field, Enum, Service, Field) of the Entity. It can be converted to multiple forms using the provided helper methods, or a custom transform can be used to modify its behavior.

func (Name) LowerCamelCase

func (n Name) LowerCamelCase() Name

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.

Example
names := []string{
	"foo_bar",
	"myJSON",
	"PDFTemplate",
}

for _, n := range names {
	fmt.Println(Name(n).LowerCamelCase())
}
Output:

fooBar
myJSON
pdfTemplate

func (Name) LowerDotNotation

func (n Name) LowerDotNotation() Name

LowerDotNotation converts Name n to lower dot notation, where each part is lower-cased and concatenated with periods.

Example
names := []string{
	"foo_bar",
	"myJSON",
	"PDFTemplate",
}

for _, n := range names {
	fmt.Println(Name(n).LowerDotNotation())
}
Output:

foo.bar
my.json
pdf.template

func (Name) LowerSnakeCase

func (n Name) LowerSnakeCase() Name

LowerSnakeCase converts Name n to lower-snake-case, where each part is lower-cased and concatenated with underscores.

Example
names := []string{
	"foo_bar",
	"myJSON",
	"PDFTemplate",
}

for _, n := range names {
	fmt.Println(Name(n).LowerSnakeCase())
}
Output:

foo_bar
my_json
pdf_template

func (Name) PGGUpperCamelCase

func (n Name) PGGUpperCamelCase() Name

PGGUpperCamelCase converts Name n to the protoc-gen-go defined upper camelcase. The rules are slightly different from 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). In addition, this method ensures the Name does not conflict with one of the generated method names, appending the fields with an underscore in the same manner as protoc-gen-go.

See: https://godoc.org/github.com/golang/protobuf/protoc-gen-go/generator#CamelCase

Example
names := []string{
	"foo_bar",
	"myJSON",
	"PDFTemplate",
	"_my_field_name_2",
	"my.field",
	"my_Field",
}

for _, n := range names {
	fmt.Println(Name(n).PGGUpperCamelCase())
}
Output:

FooBar
MyJSON
PDFTemplate
XMyFieldName_2
My.field
My_Field

func (Name) ScreamingSnakeCase

func (n Name) ScreamingSnakeCase() Name

ScreamingSnakeCase converts Name n to screaming-snake-case, where each part is all-caps and concatenated with underscores.

Example
names := []string{
	"foo_bar",
	"myJSON",
	"PDFTemplate",
}

for _, n := range names {
	fmt.Println(Name(n).ScreamingSnakeCase())
}
Output:

FOO_BAR
MY_JSON
PDF_TEMPLATE

func (Name) Split

func (n Name) Split() (parts []string)

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 (Name) String

func (n Name) String() string

String satisfies the strings.Stringer interface.

func (Name) Transform

func (n Name) Transform(mod, first NameTransformer, sep string) Name

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 (Name) UpperCamelCase

func (n Name) UpperCamelCase() Name

UpperCamelCase converts Name n to upper camelcase, where each part is title-cased and concatenated with no separator.

Example
names := []string{
	"foo_bar",
	"myJSON",
	"PDFTemplate",
}

for _, n := range names {
	fmt.Println(Name(n).UpperCamelCase())
}
Output:

FooBar
MyJSON
PDFTemplate

func (Name) UpperDotNotation

func (n Name) UpperDotNotation() Name

UpperDotNotation converts Name n to upper dot notation, where each part is title-cased and concatenated with periods.

Example
names := []string{
	"foo_bar",
	"myJSON",
	"PDFTemplate",
}

for _, n := range names {
	fmt.Println(Name(n).UpperDotNotation())
}
Output:

Foo.Bar
My.JSON
PDF.Template

func (Name) UpperSnakeCase

func (n Name) UpperSnakeCase() Name

UpperSnakeCase converts Name n to upper-snake-case, where each part is title-cased and concatenated with underscores.

Example
names := []string{
	"foo_bar",
	"myJSON",
	"PDFTemplate",
}

for _, n := range names {
	fmt.Println(Name(n).UpperSnakeCase())
}
Output:

Foo_Bar
My_JSON
PDF_Template

type NameTransformer

type NameTransformer func(string) string

NameTransformer is a function that mutates a string. Many of the methods in the standard strings package satisfy this signature.

func (NameTransformer) Chain

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.

type Node

type Node interface {
	// contains filtered or unexported methods
}

Node represents any member of the proto descriptor AST. Typically, the highest level Node is the Package.

type OneOf

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
	// contains filtered or unexported methods
}

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 Package

type Package interface {
	Node
	Commenter

	// The name of the proto package. This may or may not be the same as the Go
	// package name.
	ProtoName() Name

	// The name of the Go package. This is guaranteed to be unique.
	GoName() Name

	// The fully qualified import path for this Go Package
	ImportPath() string

	// All the files loaded for this Package
	Files() []File
	// contains filtered or unexported methods
}

Package is a container that encapsulates all the files under a single package namespace. Specifically, this would be all the proto files loaded within the same directory (not recursively). While a proto file's package technically can differ from its sibling files, PGS will throw an error as this is typically a mistake or bad practice.

type ParamMutator

type ParamMutator func(p Parameters)

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 Parameters

type Parameters map[string]string

Parameters provides a convenience for accessing and modifying the parameters passed into the protoc-gen-star plugin.

func ParseParameters

func ParseParameters(p string) (params Parameters)

ParseParameters converts the raw params string provided to protoc into a representative mapping.

func (Parameters) AddImportMapping

func (p Parameters) AddImportMapping(proto, pkg string)

AddImportMapping adds a proto file to Go package import mapping to the parameters.

func (Parameters) AddPlugin

func (p Parameters) AddPlugin(name ...string)

AddPlugin adds name to the list of plugins in the parameters. If all plugins are enabled, this method is a noop.

func (Parameters) Bool

func (p Parameters) Bool(name string) (bool, error)

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 (Parameters) BoolDefault

func (p Parameters) BoolDefault(name string, def bool) (bool, error)

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 (Parameters) Duration

func (p Parameters) Duration(name string) (time.Duration, error)

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 (Parameters) DurationDefault

func (p Parameters) DurationDefault(name string, def time.Duration) (time.Duration, error)

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 (Parameters) EnableAllPlugins

func (p Parameters) EnableAllPlugins()

EnableAllPlugins changes the parameters to enable all registered sub-plugins.

func (Parameters) Float

func (p Parameters) Float(name string) (float64, error)

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 (Parameters) FloatDefault

func (p Parameters) FloatDefault(name string, def float64) (float64, error)

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 (Parameters) HasPlugin

func (p Parameters) HasPlugin(name string) bool

HasPlugin returns true if the plugin name is enabled in the parameters. This method will always return true if all plugins are enabled.

func (Parameters) ImportMap

func (p Parameters) ImportMap() map[string]string

ImportMap returns the protoc-gen-go import map overrides. 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 (Parameters) ImportPath

func (p Parameters) ImportPath() string

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 (Parameters) ImportPrefix

func (p Parameters) ImportPrefix() string

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 (Parameters) Int

func (p Parameters) Int(name string) (int, error)

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 (Parameters) IntDefault

func (p Parameters) IntDefault(name string, def int) (int, error)

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 (Parameters) OutputPath

func (p Parameters) OutputPath() string

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 (Parameters) Paths added in v0.3.4

func (p Parameters) Paths() PathType

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 (Parameters) Plugins

func (p Parameters) Plugins() (plugins []string, all bool)

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 (Parameters) SetBool

func (p Parameters) SetBool(name string, b bool)

SetBool sets the parameter name to b.

func (Parameters) SetDuration

func (p Parameters) SetDuration(name string, d time.Duration)

SetDuration sets the parameter name to d.

func (Parameters) SetFloat

func (p Parameters) SetFloat(name string, f float64)

SetFloat sets the parameter name to f.

func (Parameters) SetImportPath

func (p Parameters) SetImportPath(path string)

SetImportPath sets the protoc-gen-go ImportPath parameter. This is useful for overriding the behavior of the ImportPath at runtime.

func (Parameters) SetImportPrefix

func (p Parameters) SetImportPrefix(prefix string)

SetImportPrefix sets the protoc-gen-go ImportPrefix parameter. This is useful for overriding the behavior of the ImportPrefix at runtime.

func (Parameters) SetInt

func (p Parameters) SetInt(name string, i int)

SetInt sets the parameter name to i.

func (Parameters) SetOutputPath

func (p Parameters) SetOutputPath(path string)

SetOutputPath sets the protoc-gen-star OutputPath parameter. This is useful for overriding the behavior of the ImportPath at runtime.

func (Parameters) SetPaths added in v0.3.4

func (p Parameters) SetPaths(pt PathType)

SetPaths sets the protoc-gen-go Paths parameter. This is useful for overriding the behavior of Paths at runtime.

func (Parameters) SetStr

func (p Parameters) SetStr(name string, s string)

SetStr sets the parameter name to s.

func (Parameters) SetUint

func (p Parameters) SetUint(name string, ui uint)

SetUint sets the parameter name to ui.

func (Parameters) Str

func (p Parameters) Str(name string) string

Str returns the parameter with name, returning an empty string if it is not set.

func (Parameters) StrDefault

func (p Parameters) StrDefault(name string, def string) string

StrDefault returns the parameter with name, or if it is unset, returns the def default value.

func (Parameters) String

func (p Parameters) String() string

String satisfies the string.Stringer interface. This method returns p in the format it is providing to the protoc execution.

func (Parameters) Uint

func (p Parameters) Uint(name string) (uint, error)

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 (Parameters) UintDefault

func (p Parameters) UintDefault(name string, def uint) (uint, error)

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.

type 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.
	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
	// contains filtered or unexported methods
}

A ParentEntity is any Entity type that can contain messages and/or enums. File and Message types implement ParentEntity.

type PathType added in v0.3.4

type PathType string

PathType describes how the generated file paths should be constructed.

const (

	// ImportPath is the default and outputs the file based off the go import
	// path defined in the go_package option.
	ImportPath PathType = ""

	// SourceRelative indicates files should be output relative to the path of
	// the source file.
	SourceRelative PathType = "source_relative"
)

type Plugin

type Plugin interface {
	generator.Plugin

	// InitContext is called before the Plugin's Init method and is passed a
	// pre-configured BuildContext instance.
	InitContext(c BuildContext)
}

Plugin describes an official protoc-gen-go plugin that will also be passed a pre-configured debugger for use. The plugin must be registered via Generator.RegisterPlugin for it to be properly initialized.

type PluginBase

type PluginBase struct {
	BuildContext

	Generator ProtocGenGo

	Imports map[string]string
	// contains filtered or unexported fields
}

PluginBase provides utility methods and a base implementation for the protoc-gen-go sub-plugin workflow.

func (*PluginBase) AddImport

func (p *PluginBase) AddImport(pkg, path string, fd *generator.FileDescriptor) (uniquePkg string)

AddImport safely registers an import at path with the target pkg name. The returned uniquePkg should be used within the code to avoid naming collisions. If referencing an entity from a protocol buffer, provide its FileDescriptor fd, otherwise leave it as nil. The Imports are cleared after GenerateImports is called.

func (*PluginBase) BuildTarget

func (p *PluginBase) BuildTarget(proto string) bool

BuildTarget returns true if the specified proto filename was an input to protoc. This method is useful to determine if generation logic should be executed against it or if it is only loaded as a dependency. This method expects the value returned by generator.FileDescriptor.GetName or descriptor.FileDescriptorProto.GetName methods.

func (*PluginBase) BuildTargetObj

func (p *PluginBase) BuildTargetObj(o generator.Object) bool

BuildTargetObj returns whether or not a generator.Object was loaded from a BuildTarget file.

func (*PluginBase) C

func (p *PluginBase) C(wrap int, args ...interface{})

C behaves like the P method, but prints a comment block. The wrap parameter indicates what width to wrap the comment at.

func (*PluginBase) C80

func (p *PluginBase) C80(args ...interface{})

C80 curries the C method with the traditional width of 80 characters, calling p.C(80, args...).

func (*PluginBase) Generate

func (p *PluginBase) Generate(file *generator.FileDescriptor)

Generate satisfies the protoc-gen-go plugin interface, however this method will fail and must be overridden by a parent struct.

func (*PluginBase) GenerateImports

func (p *PluginBase) GenerateImports(file *generator.FileDescriptor)

GenerateImports adds the imported packages to the top of the file to be generated, using the packages included in b.Imports. This method satisfies the GenerateImports method for the protoc-gen-go plugin, and is called after Generate for each particular FileDescriptor. The added Imports are cleared after this call is completed.

func (*PluginBase) In

func (p *PluginBase) In()

In wraps the generator's In command, indenting the output by one tab.

func (*PluginBase) Init

func (p *PluginBase) Init(g *generator.Generator)

Init sets up the plugin with a reference to the generator. This method satisfies the Init method for the protoc-gen-go plugin.

func (*PluginBase) InitContext

func (p *PluginBase) InitContext(c BuildContext)

InitContext populates this Plugin with the BuildContext from the parent Generator, allowing for easy debug logging, and error checking. This method is called prior to Init for plugins registered directly with the generator.

func (*PluginBase) Name

func (p *PluginBase) Name() string

Name satisfies the protoc-gen-go plugin interface, however this method will fail and must be overridden by a parent struct. PluginBase should be used as an anonymously embedded field of an actual Plugin implementation. The only methods that need to be overridden are Name and Generate.

func (*PluginBase) Out

func (p *PluginBase) Out()

Out wraps the generator's Out command, outdenting the output by one tab.

func (*PluginBase) P

func (p *PluginBase) P(args ...interface{})

P wraps the generator's P method, printing the arguments to the generated output. It handles strings and int32s, plus handling indirections because they may be *string, etc.

func (*PluginBase) Pop

func (p *PluginBase) Pop() BuildContext

Pop removes the last push from the plugin's BuildContext. This method should only be called after a paired Push or PushDir.

func (*PluginBase) PopDir

func (p *PluginBase) PopDir() BuildContext

PopDir removes the last PushDir from the plugin's BuildContext. This method should only be called after a paired PushDir.

func (*PluginBase) Push

func (p *PluginBase) Push(prefix string) BuildContext

Push adds a prefix to the plugin's BuildContext. Pop should be called when that context is complete.

func (*PluginBase) PushDir

func (p *PluginBase) PushDir(dir string) BuildContext

PushDir changes the OutputPath of the plugin's BuildContext. Pop (or PopDir) should be called when that context is complete.

func (*PluginBase) T

func (p *PluginBase) T(tpl Template, data interface{})

T renders tpl into the target file, using data. The plugin is terminated if there is an error executing the template. Both text/template and html/template packages are compatible with this method.

type PostProcessor

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)
}

A PostProcessor modifies the output of an Artifact before final rendering. PostProcessors are only applied to Artifacts created by Modules.

func GoFmt

func GoFmt() PostProcessor

GoFmt returns a PostProcessor that runs gofmt on any files ending in ".go"

type ProtoLabel

ProtoLabel wraps the FieldDescriptorProto_Label enum for better readability. It is a 1-to-1 conversion.

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 = 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 = ProtoLabel(descriptor.FieldDescriptorProto_LABEL_REQUIRED)

	// Repeated identifies that the field either permits multiple entries
	// (repeated) or is a map (map<key,val>). 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 = ProtoLabel(descriptor.FieldDescriptorProto_LABEL_REPEATED)
)

func (ProtoLabel) Proto

Proto returns the FieldDescriptorProto_Label for this ProtoLabel. This method is exclusively used to improve readability without having to switch the types.

type ProtoType

ProtoType wraps the FieldDescriptorProto_Type enum for better readability and utility methods. It is a 1-to-1 conversion.

func (ProtoType) IsInt

func (pt ProtoType) IsInt() bool

IsInt returns true if pt maps to an integer-like type. While EnumT types in Go are aliases of uint32, to correctly accomodate other languages with non-numeric enums, IsInt returns false for EnumT.

func (ProtoType) IsNumeric

func (pt ProtoType) IsNumeric() bool

IsNumeric returns true if pt maps to a numeric type. While EnumT types in Go are aliases of uint32, to correctly accomodate other languages with non-numeric enums, IsNumeric returns false for EnumT.

func (ProtoType) IsSlice

func (pt ProtoType) IsSlice() bool

IsSlice returns true if the type is represented as a slice/array. At this time, only BytesT satisfies this condition.

func (ProtoType) Proto

Proto returns the FieldDescriptorProto_Type for this ProtoType. This method is exclusively used to improve readability without having to switch the types.

type ProtocGenGo

type ProtocGenGo interface {
	// Unwrap returns the underlying generator.Generator instance. Typically this
	// is called to access public fields off this struct.
	Unwrap() *generator.Generator

	// The following methods/interfaces match the interface of a protoc-gen-go
	// generator.Generator struct.
	io.Writer
	Error(err error, msgs ...string)
	Fail(msgs ...string)
	ObjectNamed(n string) generator.Object
	GoType(message *generator.Descriptor, field *descriptor.FieldDescriptorProto) (typ string, wire string)
	GoPackageName(importPath generator.GoImportPath) generator.GoPackageName
	P(args ...interface{})
	In()
	Out()
	// contains filtered or unexported methods
}

ProtocGenGo is a superset of the generator.Generator API from the protoc-gen-go library. It exposes many of the members of the original struct, but also exposes others that permit easier testing of code that relies upon accessing protected members.

func Wrap

Wrap converts a generator.Generator instance into a type that satisfies the ProtocGenGo interface.

type Service

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
	// contains filtered or unexported methods
}

Service describes an proto service

type Syntax

type Syntax string

Syntax describes the proto syntax used to encode the proto file

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 = "proto2"

	// Proto3 syntax only allows for optional fields, but defaults to the zero
	// value of that particular type. Most of the field types in the generated go
	// structs are value types.
	// See: https://developers.google.com/protocol-buffers/docs/proto3
	Proto3 Syntax = "proto3"
)

func (Syntax) SupportsRequiredPrefix

func (s Syntax) SupportsRequiredPrefix() bool

SupportsRequiredPrefix returns true if s supports "optional" and "required" identifiers on message fields. Only Proto2 syntax supports this feature.

type Template

type Template interface {
	Name() string
	Execute(wr io.Writer, data interface{}) error
}

Template describes a template used to render content. Both the text/template and html/template packages satisfy this interface.

type TemplateArtifact

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{}
}

TemplateArtifact contains the shared logic used by Artifacts that render their contents using a Template.

type TypeName

type TypeName string

A TypeName describes the name of a type (type on a field, or method signature)

func (TypeName) Element

func (n TypeName) Element() TypeName

Element returns the TypeName of the element of n. For types other than slices and maps, this just returns n.

Example
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 (TypeName) Key

func (n TypeName) Key() TypeName

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.

Example
types := []string{
	"int",
	"*my.Type",
	"[]string",
	"map[string]*io.Reader",
}

for _, t := range types {
	fmt.Println(TypeName(t).Key())
}
Output:


int
string

func (TypeName) Pointer

func (n TypeName) Pointer() TypeName

Pointer converts TypeName n to it's pointer type. If n is already a pointer, slice, or map, it is returned unmodified.

Example
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 (TypeName) String

func (n TypeName) String() string

String satisfies the strings.Stringer interface.

func (TypeName) Value

func (n TypeName) Value() TypeName

Value converts TypeName n to it's value type. If n is already a value type, slice, or map it is returned unmodified.

Example
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

type Visitor

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)
	VisitOneOf(OneOf) (v Visitor, err error)
	VisitService(Service) (v Visitor, err error)
	VisitMethod(Method) (v Visitor, err 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.

func NilVisitor

func NilVisitor() Visitor

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.

Example
package main

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 main() {
	n := enumNode()
	p := EnumPrinter()

	if err := Walk(p, n); err != nil {
		panic(err)
	}

}

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{rawDesc: &descriptor.EnumDescriptorProto{Name: proto.String("Foo")}})

	m := &msg{}
	m.addMessage(sm)
	m.addEnum(&enum{rawDesc: &descriptor.EnumDescriptorProto{Name: proto.String("Bar")}})

	return m
}
Output:

Bar
Foo

func PassThroughVisitor

func PassThroughVisitor(v Visitor) 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.

Example
package main

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 main() {
	n := fieldNode()
	p := FieldPrinter()

	if err := Walk(p, n); err != nil {
		panic(err)
	}

}

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
}
Output:

Foo
Bar

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL