parser

package
v1.1.5 Latest Latest
Warning

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

Go to latest
Published: May 24, 2023 License: MIT Imports: 17 Imported by: 0

Documentation

Index

Constants

View Source
const DefaultServiceVersion = "0.0.1"

DefaultServiceVersion defines the version we'll assign to all parsed services if they do not have the VERSION doc option.

Variables

View Source
var ErrMissingGoMod = fmt.Errorf("unable to find 'go.mod' for project")

ErrMissingGoMod is the error returned when the project we're parsing does not have a 'go.mod' file in it.

View Source
var ErrMultiplePackages = fmt.Errorf("multiple packages defined in input path; should be one")

ErrMultiplePackages is the error returned when you try to parse multiple packages for types at once.

View Source
var ErrMultipleServices = fmt.Errorf("do not define multiple services in a single file")

ErrMultipleServices is the error returned when you define multiple "XyzService" interfaces in one file.

View Source
var ErrNoServices = fmt.Errorf("file does not contain any service interfaces")

ErrNoServices is the error returned when your input file does not contain any "XyzService" interfaces.

View Source
var ErrTypeNotContext = fmt.Errorf("not the type 'context.Context'")

ErrTypeNotContext is the error returned when the first param of an operation is not a context.Context.

View Source
var ErrTypeNotError = fmt.Errorf("not the type 'error'")

ErrTypeNotError is the error returned when the second return value of an operation is not an error.

View Source
var ErrTypeNotStructPointer = fmt.Errorf("not a pointer to a struct type")

ErrTypeNotStructPointer is the error returned when the request/response value is not a struct pointer.

View Source
var ErrTypeNotTwoParams = fmt.Errorf("must have two params")

ErrTypeNotTwoParams is the error for when your function signature doesn't accept two parameters.

View Source
var ErrTypeNotTwoReturns = fmt.Errorf("must have two return values")

ErrTypeNotTwoReturns is the error for when your function signature doesn't return two values.

Functions

func ApplyFunctionDocumentation

func ApplyFunctionDocumentation(ctx *Context, function *ServiceFunctionDeclaration)

ApplyFunctionDocumentation takes the documentation comment block above your interface function declaration and applies them to the function snapshot, parsing all Doc Options in the process.

func FindGoDotMod

func FindGoDotMod(dirName string) (string, error)

FindGoDotMod starts in the current directory provided and recursively checks parent directories until it encounters a "go.mod" file. When it does, this will return a path to the file. You'll receive an error if we can't find a "go.mod" file or the input is not a valid directory.

func ParseDocumentation

func ParseDocumentation(ctx *Context) (Documentation, Tags, error)

ParseDocumentation runs go/doc parsing on your input file to extract all of your documentation, comments, and struct field tags. It returns 2 specialized lookup maps; one for doc comments and one for the struct field tags. The keys to these maps are based on the names of the thing whose docs/tags you want; either "SERVICE", "SERVICE.FUNCTION", "MODEL", or "MODEL.FIELD".

func ParsePackageInfo

func ParsePackageInfo(ctx *Context) (input *PackageDeclaration, output *PackageDeclaration, err error)

ParsePackageInfo overlays your project's "go.mod" file and your input file/path to figure out the fully qualified package info for the input service. We'll then apply our conventions to construct info about the output package where we'll put all of our output artifacts.

Types

type Context

type Context struct {

	// FileSet is the collection of related files we're going to give to the Go AST parser.
	FileSet *token.FileSet
	// File is the entire syntax tree from when we parsed your input file.
	File *ast.File
	// Path is the relative path to the service definition file we're parsing.
	Path string
	// AbsolutePath is the absolute path to the service definition file we're parsing.
	AbsolutePath string
	// Timestamp is when we performed the parsing.
	Timestamp time.Time

	// Module contains info from "go.mod" about the entire module where the service/package is defined.
	Module *ModuleDeclaration
	// InputPackage contains information about the package where the service definition resides.
	InputPackage *PackageDeclaration
	// OutputPackage contains information about the package where the generated code will go.
	OutputPackage *PackageDeclaration

	// Service contains the parsed info about the service declared in our input file.
	Service *ServiceDeclaration
	// Types captures snapshots of every model type, primitive type, and recursive field type for
	// any field referenced by any of your request/response models. It contains all of the referenced types
	// for every single field and their fields and their fields, and so on.
	Types TypeRegistry

	// Documentation stores the GoDoc comments for the services/functions/models/fields in the parsed code.
	Documentation Documentation
	// Tags stores the struct field tags annotated on fields in your input file.
	Tags Tags
	// RawTypes contains the tree of all raw, parsed type information as we get it from the AST.
	RawTypes *packages.Package
}

Context wrangles all of the captured data about your input service declaration file. It tracks the module/package information, the service(s) that were defined, the request/response structs that were defined in the file, etc. It's the output of Parse() and is the input value when we evaluate Go templates to generate other source files based on this service definition info.

func ParseFile

func ParseFile(inputPath string) (*Context, error)

ParseFile parses a source code file containing a service interface declaration as well as the structs for the request/response inputs and outputs. It will aggregate all of the services/ops/models described in the source code in a much more simple/direct Context.

The resulting Context contains all of the information from the source code that we need to generate clients/gateways for the service(s). It will also be used as the input value when evaluating any of our artifact templates.

func (Context) Scope

func (ctx Context) Scope() *types.Scope

Scope returns the root of the parsed type tree for the source file we parsed.

func (Context) TimestampString

func (ctx Context) TimestampString() string

TimestampString returns the timestamp formatted in a standard fashion that we include in artifact headers.

type Documentation

type Documentation map[string]string

Documentation is a lookup cache for all GoDoc comments on your services, functions, models, and fields.

func (Documentation) ForField

ForField find the GoDoc comments for the attribute of a request/response struct

func (Documentation) ForFunction

ForFunction finds the GoDoc comments for the given service function.

func (Documentation) ForService

ForService finds the GoDoc comments for the given service interface.

func (Documentation) ForType

ForType finds the GoDoc comments for the request/response struct.

func (Documentation) Set

func (docs Documentation) Set(segmentsAndDoc ...string)

Set adds an entry to the lookup. This is admittedly a bastardization of variadic functions - the last value you pass in is the GoDoc comments. The first to second-to-last values are segments in the lookup key. For instance if you are caching the comment for the function Bar on the FooService, you would call `Set("FooService", "Bar", "Bar does some baz magic and gives you back goo.")`. The resulting entry will look like "FooService.Bar"->"Bar does some...".

type DocumentationLines

type DocumentationLines []string

DocumentationLines represents all of the 'go doc' lines above a type/function/field with all of the leading slashes removed.

func (DocumentationLines) Empty

func (docs DocumentationLines) Empty() bool

Empty returns true when there are no lines of documentation/comments for the service/field/function/etc.

func (DocumentationLines) NotEmpty

func (docs DocumentationLines) NotEmpty() bool

NotEmpty returns true when there is at least 1 line of documentation/comments.

func (DocumentationLines) String

func (docs DocumentationLines) String() string

String returns all of the documentation lines as one string, separated by newlines.

func (DocumentationLines) Trim

Trim removes blank doc lines from the front/back of your list of comments.

type FieldBindingOptions

type FieldBindingOptions struct {
	// Omit will be true if you provided the `json:"-"` tag saying that this is not part of JSON marshaling.
	Omit bool
	// Name is the remapped JSON attribute for the associated field (e.g. `json:"user_id"` -> user_id).
	Name string
}

FieldBindingOptions provides hints to the generation tools about how the runtime binder will map request parameters to an attribute of the request struct.

func ParseBindingOptions

func ParseBindingOptions(ctx *Context, field *FieldDeclaration, fieldVar *types.Var) *FieldBindingOptions

ParseBindingOptions looks at the `json` tags of the given struct field and returns this field's binding configuration. It indicates whether the field should be left out of JSON marshaling and what field name to use when going to/from JSON format. If the field has no `json` tag, you will get a set of binding options representing the default values (i.e. include the field and use its exact name).

func (FieldBindingOptions) NotOmit

func (opts FieldBindingOptions) NotOmit() bool

NotOmit is a convenience for templates that returns true when we should expose this field to client and documentation templates/tooling.

type FieldDeclaration

type FieldDeclaration struct {
	// Name the name of the field/attribute.
	Name string
	// ParentType is a back-pointer to the type that this field is a member of.
	ParentType *TypeDeclaration
	// Type contains the data type information for this field.
	Type *TypeDeclaration
	// Pointer indicates if this field is a pointer type (true) or value type (false).
	Pointer bool
	// Documentation are all of the comments documenting this field.
	Documentation DocumentationLines
	// Binding describes the custom binding instructions used when unmarshaling request data onto this field.
	Binding *FieldBindingOptions
}

FieldDeclaration describes a single field in a request/response model.

func ApplyFieldDocumentation

func ApplyFieldDocumentation(ctx *Context, field *FieldDeclaration) *FieldDeclaration

ApplyFieldDocumentation takes the documentation comment block above your struct field declaration and applies them to the model snapshot, parsing all Doc Options in the process.

type FieldDeclarations

type FieldDeclarations []*FieldDeclaration

FieldDeclarations collects the fields/attributes on a service model.

func (FieldDeclarations) ByBindingName

func (fields FieldDeclarations) ByBindingName(name string) *FieldDeclaration

ByBindingName looks for a field whose (possibly) re-mapped name matches the given value. This comparison is CASE INSENSITIVE, so "id" will find the field "ID".

func (FieldDeclarations) ByName

func (fields FieldDeclarations) ByName(name string) *FieldDeclaration

ByName looks up the declaration for the field that matches the given name. This name comparison is CASE INSENSITIVE, so "id" will find the field "ID".

func (FieldDeclarations) Empty

func (fields FieldDeclarations) Empty() bool

Empty returns true if there are zero fields in this set.

func (FieldDeclarations) NotEmpty

func (fields FieldDeclarations) NotEmpty() bool

NotEmpty returns true if there is at least one field in this set

func (FieldDeclarations) TransportFields

func (fields FieldDeclarations) TransportFields() FieldDeclarations

TransportFields returns the subset of child fields for this field that are NOT omitted (i.e. should be included in JSON/transport).

type GatewayFunctionOptions

type GatewayFunctionOptions struct {
	// Function is a back-pointer to the service function these options correspond to.
	Function *ServiceFunctionDeclaration
	// Method indicates if the RPC gateway should use a GET, POST, etc when exposing this operation via HTTP.
	Method string
	// Path defines the URL pattern to provide to the gateway's router/mux to access this operation.
	Path string
	// Status indicates what success status code the gateway should use when responding via HTTP (e.g. 200, 202, etc)
	Status int
}

GatewayFunctionOptions contains all of the configurable HTTP-related options for a single function within your service (e.g. method, path, etc).

func (GatewayFunctionOptions) PathParameters

func (opts GatewayFunctionOptions) PathParameters() GatewayParameters

PathParameters looks at all of the ":xxx" path parameters in HTTPPath and returns the fields on the request struct that will be bound by those values at runtime. For instance, if the path was "/user/:userID/address/:addressID", this will return a 2-element slice containing the request's UserID and AddressID fields.

func (GatewayFunctionOptions) QueryParameters

func (opts GatewayFunctionOptions) QueryParameters() GatewayParameters

QueryParameters describes all of the request struct attributes that can be bound by specifying them in the query string of the URL when making a request. For instance, if your request struct had an attribute "Limit uint64", then this includes a GatewayParameter that describes the caller's ability to include "&Limit=123" in the query string.

func (GatewayFunctionOptions) SupportsBody

func (opts GatewayFunctionOptions) SupportsBody() bool

SupportsBody returns true when the method is either POST, PUT, or PATCH; the HTTP methods where we expect you to feed request data via the request body rather than query string.

type GatewayParameter

type GatewayParameter struct {
	// Name is the identifier of the path param (e.g "id" in "/user/:id") or query string value that
	// will be bound to the Field.
	Name string
	// Field indicates which model attribute will be populated when this parameter goes
	// through the request binder.
	Field *FieldDeclaration
}

GatewayParameter defines how a path/query parameter will be bound to a field in your request struct.

type GatewayParameters

type GatewayParameters []*GatewayParameter

GatewayParameters is an overlay of a service function's path and request type/field info. It helps you indicate how a given field will be bound when handling incoming requests (e.g. path params vs query params).

func (GatewayParameters) ByName

func (params GatewayParameters) ByName(name string) *GatewayParameter

ByName locates the parameter with the given name. This is a case-insensitive search.

func (GatewayParameters) Empty

func (params GatewayParameters) Empty() bool

Empty returns true when there are zero parameters defined in this set.

func (GatewayParameters) NotEmpty

func (params GatewayParameters) NotEmpty() bool

NotEmpty returns true when there is at least one parameter mapping defined.

type GatewayServiceOptions

type GatewayServiceOptions struct {
	// Service is a back-pointer to the service these options correspond to.
	Service *ServiceDeclaration
	// PathPrefix is the optional version/domain prefix for all endpoints in the API (e.g. "v2/").
	PathPrefix string
}

GatewayServiceOptions contains all of the configurable HTTP-related options for a top-level service.

type ModuleDeclaration

type ModuleDeclaration struct {
	// Name is the fully qualified module name (e.g. "github.com/someuser/modulename")
	Name string
	// Directory is the absolute path to the root directory of the module (where go.mod resides).
	Directory string
}

ModuleDeclaration contains information about the Go module that the service belongs to. This is information scraped from project's "go.mod" file.

func ParseModuleInfo

func ParseModuleInfo(ctx *Context) (*ModuleDeclaration, error)

ParseModuleInfo cherry picks a tiny bit of info from your "go.mod" file that we use in processing your services.

func (ModuleDeclaration) GoMod

func (module ModuleDeclaration) GoMod() string

GoMod returns the absolute path to the "go.mod" file for this module on the system running frodo.

type PackageDeclaration

type PackageDeclaration struct {
	// Name is just the raw package name (no path info)
	Name string
	// Import is the fully qualified package name (e.g. "github.com/someuser/modulename/foo/bar/baz")
	Import string
	// Directory is the absolute path to the package.
	Directory string
}

PackageDeclaration defines the subpackage that the service resides in.

type ServiceDeclaration

type ServiceDeclaration struct {
	// Name is the name of the service/interface.
	Name string
	// Version is the (hopefully) semantic version of your API (e.g. 1.2.0). This is NOT the prefix
	// to all routes in the API for the service. It's just an identifier available to code gen tools.
	Version string
	// Gateway contains the configuration HTTP-related options for this service.
	Gateway *GatewayServiceOptions
	// Functions are all of the functions explicitly defined on this service.
	Functions ServiceFunctionDeclarations
	// Documentation are all of the comments documenting this service.
	Documentation DocumentationLines
}

ServiceDeclaration wrangles all of the information we could grab about the service from the interface that defined it.

func ApplyServiceDocumentation

func ApplyServiceDocumentation(ctx *Context, service *ServiceDeclaration) *ServiceDeclaration

ApplyServiceDocumentation takes the documentation comment block above your interface type declaration and applies them to the service snapshot, parsing all Doc Options in the process.

func ParseService

func ParseService(ctx *Context) (*ServiceDeclaration, error)

ParseService looks for 'type XxxService interface' declarations and extracts all service/operation info from it that we need to generate our artifacts. This operation will fail if you have multiple service interfaces in this file.

func (ServiceDeclaration) FunctionByName

func (service ServiceDeclaration) FunctionByName(name string) *ServiceFunctionDeclaration

FunctionByName fetches the service operation with the given function name. This returns nil when there are no functions in this interface/service by that name.

type ServiceFunctionDeclaration

type ServiceFunctionDeclaration struct {
	// Name is the name of the function defined in the service interface (the function name to call this operation).
	Name string
	// Request contains the details about the model/type/struct for this operation's input/request value.
	Request *TypeDeclaration
	// Response contains the details about the model/type/struct for this operation's output/response value.
	Response *TypeDeclaration
	// Gateway wrangles all of the HTTP-related options for this function (method, path, etc).
	Gateway *GatewayFunctionOptions
	// Documentation are all of the comments documenting this operation.
	Documentation DocumentationLines
	// Service represents the interface/service that this function belongs to.
	Service *ServiceDeclaration
}

ServiceFunctionDeclaration defines a single operation/function within a service (one of the interface functions).

func ParseServiceFunction

func ParseServiceFunction(ctx *Context, service *ServiceDeclaration, funcType *types.Func) (*ServiceFunctionDeclaration, error)

ParseServiceFunction captures the information for a single function on a service. This includes all of the doc options that configure the gateway stuff.

func ParseServiceFunctions

func ParseServiceFunctions(ctx *Context, service *ServiceDeclaration, interfaceType *types.Interface) ([]*ServiceFunctionDeclaration, error)

ParseServiceFunctions creates function declarations for all methods on the service interface.

func (ServiceFunctionDeclaration) String

String returns the function signature for this operation for debugging purposes.

type ServiceFunctionDeclarations

type ServiceFunctionDeclarations []*ServiceFunctionDeclaration

ServiceFunctionDeclarations defines a collection of related service functions/operations.

type Tags

type Tags map[string]string

Tags is a lookup for finding `json:"xxx"` tags defined on your request/response structs.

func (Tags) ForField

func (tags Tags) ForField(f *FieldDeclaration) reflect.StructTag

ForField looks up the tag annotations for the given model field. If the field does not have any tags you'll get back the zero-value StructTag that always gives you empty for any value lookups.

func (Tags) Set

func (tags Tags) Set(model string, field string, tag *ast.BasicLit)

Set captures the tag information for the given model attribute.

type TypeDeclaration

type TypeDeclaration struct {
	// Name is the "package.Type" formatted name of the type as it is used in our source file. For types that
	// are defined in the declaration file, the name might only be "FooRequest" (no package).
	Name string
	// Type is the raw Go type information that we used to build this snapshot.
	Type types.Type
	// Kind classifies our types. Our definition aligns fairly closely with the reflect package's definition, so
	// we use it to describe whether a type, at its lowest level, describes a struct, int, slice, etc.
	Kind reflect.Kind
	// Elem is used by slice and map-like declarations to describe the type info for the underlying value type.
	Elem *TypeDeclaration
	// Key is used by map-like declarations to describe the type info for the lookup key.
	Key *TypeDeclaration
	// Basic indicates if this type is one of our base types (primitives, etc).
	Basic bool
	// Fields (for struct types) contains all of the attribute/type info for all members of the struct.
	Fields FieldDeclarations
	// Documentation are all of the comments documenting this operation.
	Documentation DocumentationLines
	// Implements contains some quick checks for whether or not this type implements the various
	// single function interfaces used to handle raw data responses.
	Implements struct {
		// ContentReader is true when it implements that interface.
		ContentReader bool
		// ContentWriter is true when it implements that interface.
		ContentWriter bool
		// ContentTypeReader is true when it implements that interface.
		ContentTypeReader bool
		// ContentTypeWriter is true when it implements that interface.
		ContentTypeWriter bool
		// ContentFileNameReader is true when it implements that interface.
		ContentFileNameReader bool
		// ContentFileNameWriter is true when it implements that interface.
		ContentFileNameWriter bool
	}
}

TypeDeclaration is a snapshot of the type information for any type referenced, directly or indirectly, in your service declaration file.

func ApplyTypeDocumentation

func ApplyTypeDocumentation(ctx *Context, t *TypeDeclaration) *TypeDeclaration

ApplyTypeDocumentation takes the documentation comment block above your struct/alias type declaration and applies them to the model snapshot, parsing all Doc Options in the process.

func (TypeDeclaration) MapLike

func (t TypeDeclaration) MapLike() bool

MapLike returns true for map types. This will also be true for any alias to a map type.

func (TypeDeclaration) NonOmittedFields

func (t TypeDeclaration) NonOmittedFields() FieldDeclarations

NonOmittedFields returns just the subset of fields that should be included in this model's transport/binding.

func (TypeDeclaration) ObjectLike

func (t TypeDeclaration) ObjectLike() bool

ObjectLike returns true for types that represent some sort complex object (i.e. struct/interface).

func (TypeDeclaration) PrimitiveLike

func (t TypeDeclaration) PrimitiveLike() bool

PrimitiveLike returns true for types that represent some sort of basic/primitive type like numbers/bools/strings.

func (TypeDeclaration) SliceLike

func (t TypeDeclaration) SliceLike() bool

SliceLike returns true for array or slice types. This will also be true for any alias to an array/slice type.

func (TypeDeclaration) String

func (t TypeDeclaration) String() string

String returns the name of the type. That is all.

type TypeRegistry

type TypeRegistry map[string]*TypeDeclaration

TypeRegistry is a quick lookup of all types we encountered when processing your declaration file.

func NewTypeRegistry

func NewTypeRegistry() TypeRegistry

NewTypeRegistry creates a new type registry that is pre-filled with all of the base types already registered.

func ParseTypes

func ParseTypes(ctx *Context) (*packages.Package, TypeRegistry, error)

ParseTypes runs the syntax tree through the "go/types" processor so that we get detailed type information on all of the structs/types we defined, their fields, and the parameters/outputs of our service functions.

func (TypeRegistry) Lookup

func (reg TypeRegistry) Lookup(t types.Type) (*TypeDeclaration, bool)

Lookup finds our parsed snapshot for the raw type. It returns the snapshot an an "ok" boolean to indicate whether we found it or not.

func (TypeRegistry) LookupByName

func (reg TypeRegistry) LookupByName(name string) (*TypeDeclaration, bool)

LookupByName finds our parsed snapshot for the type name. It returns the snapshot an an "ok" boolean to indicate whether we found it or not.

func (TypeRegistry) NonBasicTypes

func (reg TypeRegistry) NonBasicTypes() []*TypeDeclaration

NonBasicTypes returns a slice containing only types not declared as "Basic". This way you only iterate complex types that you defined or imported.

func (TypeRegistry) Register

func (reg TypeRegistry) Register(entry *TypeDeclaration) *TypeDeclaration

Register adds the given type declaration to the registry.

func (TypeRegistry) WithoutInvalid

func (reg TypeRegistry) WithoutInvalid() TypeRegistry

WithoutInvalid removes all entries for types whose 'Kind' is 'Invalid'. This ensures that your context doesn't contain any entries for fields/types that we don't support.

Jump to

Keyboard shortcuts

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