Documentation ¶
Overview ¶
Package markers contains utilities for defining and parsing "marker comments", also occasionally called tag comments (we use the term marker to avoid confusing with struct tags). Parsed result (output) values take the form of Go values, much like the "encoding/json" package.
Definitions and Parsing ¶
Markers are defined as structured Definitions which can be used to consistently parse marker comments. A Definition contains an concrete output type for the marker, which can be a simple type (like string), a struct, or a wrapper type (useful for defining additional methods on marker types).
Markers take the general form
+path:to:marker=val +path:to:marker:arg1=val,arg2=val2 +path:to:marker
Arguments may be ints, bools, strings, and slices. Ints and bool take their standard form from Go. Strings may take any of their standard forms, or any sequence of unquoted characters up until a `,` or `;` is encountered. Lists take either of the following forms:
val;val;val {val, val, val}
Note that the first form will not properly parse nested slices, but is generally convenient and is the form used in many existing markers.
Each of those argument types maps to the corresponding go type. Pointers mark optional fields (a struct tag, below, may also be used). The empty interface will match any type.
Struct fields may optionally be annotated with the `marker` struct tag. The first argument is a name override. If it's left blank (or the tag isn't present), the camelCase version of the name will be used. The only additional argument defined is `optional`, which marks a field as optional without using a pointer.
All parsed values are unmarshalled into the output type. If any non-optional fields aren't mentioned, an error will be raised unless `Strict` is set to false.
Registries and Lookup ¶
Definitions can be added to registries to facilitate lookups. Each definition is marked as either describing a type, struct field, or package (unassociated). The same marker name may be registered multiple times, as long as each describes a different construct (type, field, or package). Definitions can then be looked up by passing unparsed markers.
Collection and Extraction ¶
Markers can be collected from a loader.Package using a Collector. The Collector will read from a given Registry, collecting comments that look like markers and parsing them if they match some definition on the registry.
Markers are considered associated with a particular field or type if they exist in the Godoc, or the closest non-godoc comment. Any other markers not inside a some other block (e.g. a struct definition, interface definition, etc) are considered package level. Markers in a "closest non-Go comment block" may also be considered package level if registered as such and no identical type-level definition exists.
Like loader.Package, Collector's methods are idempotent and will not reperform work.
Traversal ¶
EachType function iterates over each type in a Package, providing conveniently structured type and field information with marker values associated.
PackageMarkers can be used to fetch just package-level markers.
Help ¶
Help can be defined for each marker using the DefinitionHelp struct. It's mostly intended to be generated off of godocs using cmd/helpgen, which takes the first line as summary (removing the type/field name), and considers the rest as details. It looks for the
+controllertools:generateHelp[:category=<string>]
marker to start generation.
If you can't use godoc-based generation for whatever reasons (e.g. primitive-typed markers), you can use the SimpleHelp and DeprecatedHelp helper functions to generate help structs.
Help is then registered into a registry as associated with the actual definition, and can then be later retrieved from the registry.
Index ¶
- func EachType(col *Collector, pkg *loader.Package, cb TypeCallback) error
- func RegisterAll(reg *Registry, defs ...*Definition) error
- type Argument
- type ArgumentType
- type Collector
- type Definition
- type DefinitionHelp
- type DetailedHelp
- type FieldInfo
- type MarkerValues
- type RawArguments
- type Registry
- func (r *Registry) AddHelp(def *Definition, help *DefinitionHelp)
- func (r *Registry) AllDefinitions() []*Definition
- func (r *Registry) Define(name string, target TargetType, obj interface{}) error
- func (r *Registry) HelpFor(def *Definition) *DefinitionHelp
- func (r *Registry) Lookup(name string, target TargetType) *Definition
- func (r *Registry) Register(def *Definition) error
- type ScannerError
- type TargetType
- type TypeCallback
- type TypeInfo
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func EachType ¶
func EachType(col *Collector, pkg *loader.Package, cb TypeCallback) error
EachType collects all markers, then calls the given callback for each type declaration in a package. Each individual spec is considered separate, so
type ( Foo string Bar int Baz struct{} )
yields three calls to the callback.
func RegisterAll ¶
func RegisterAll(reg *Registry, defs ...*Definition) error
RegisterAll attempts to register all definitions against the given registry, stopping and returning if an error occurs.
Types ¶
type Argument ¶
type Argument struct { // Type is the type of this argument For non-scalar types (map and slice), // further information is specified in ItemType. Type ArgumentType // Optional indicates if this argument is optional. Optional bool // Pointer indicates if this argument was a pointer (this is really only // needed for deserialization, and should alway imply optional) Pointer bool // ItemType is the type of the slice item for slices, and the value type // for maps. ItemType *Argument }
Argument is the type of a marker argument.
func ArgumentFromType ¶
ArgumentFromType constructs an Argument by examining the given raw reflect.Type. It can construct arguments from the Go types corresponding to any of the types listed in ArgumentType.
func (*Argument) Parse ¶
Parse attempts to consume the argument from the given scanner (based on the given raw input as well for collecting ranges of content), and places the output value in the given reflect.Value. Errors are reported via the given scanner.
func (Argument) TypeString ¶
TypeString returns a string roughly equivalent (but not identical) to the underlying Go type that this argument would parse to. It's mainly useful for user-friendly formatting of this argument (e.g. help strings).
type ArgumentType ¶
type ArgumentType int
ArgumentType is the kind of a marker argument type. It's roughly analogous to a subset of reflect.Kind, with an extra "AnyType" to represent the empty interface.
const ( // Invalid represents a type that can't be parsed, and should never be used. InvalidType ArgumentType = iota // IntType is an int IntType // NumberType is a float64 NumberType // StringType is a string StringType // BoolType is a bool BoolType // AnyType is the empty interface, and matches the rest of the content AnyType // SliceType is any slice constructed of the ArgumentTypes SliceType // MapType is any map constructed of string keys, and ArgumentType values. // Keys are strings, and it's common to see AnyType (non-uniform) values. MapType // RawType represents content that gets passed directly to the marker // without any parsing. It should *only* be used with anonymous markers. RawType )
type Collector ¶
type Collector struct { *Registry // contains filtered or unexported fields }
Collector collects and parses marker comments defined in the registry from package source code. If no registry is provided, an empty one will be initialized on the first call to MarkersInPackage.
func (*Collector) MarkersInPackage ¶
MarkersInPackage computes the marker values by node for the given package. Results are cached by package ID, so this is safe to call repeatedly from different functions. Each file in the package is treated as a distinct node.
We consider a marker to be associated with a given AST node if either of the following are true:
- it's in the Godoc for that AST node
it's in the closest non-godoc comment group above that node, *and* that node is a type or field node, *and* [it's either registered as type-level *or* it's not registered as being package-level]
it's not in the Godoc of a node, doesn't meet the above criteria, and isn't in a struct definition (in which case it's package-level)
type Definition ¶
type Definition struct { // Output is the deserialized Go type of the marker. Output reflect.Type // Name is the marker's name. Name string // Target indicates which kind of node this marker can be associated with. Target TargetType // Fields lists out the types of each field that this marker has, by // argument name as used in the marker (if the output type isn't a struct, // it'll have a single, blank field name). This only lists exported fields, // (as per reflection rules). Fields map[string]Argument // FieldNames maps argument names (as used in the marker) to struct field name // in the output type. FieldNames map[string]string // Strict indicates that this definition should error out when parsing if // not all non-optional fields were seen. Strict bool }
Definition is a parsed definition of a marker.
func MakeAnyTypeDefinition ¶ added in v0.2.2
func MakeAnyTypeDefinition(name string, target TargetType, output interface{}) (*Definition, error)
MakeAnyTypeDefinition constructs a definition for an output struct with a field named `Value` of type `interface{}`. The argument to the marker will be parsed as AnyType and assigned to the field named `Value`.
func MakeDefinition ¶
func MakeDefinition(name string, target TargetType, output interface{}) (*Definition, error)
MakeDefinition constructs a definition from a name, type, and the output type. All such definitions are strict by default. If a struct is passed as the output type, its public fields will automatically be populated into Fields (and similar fields in Definition). Other values will have a single, empty-string-named Fields entry.
func Must ¶
func Must(def *Definition, err error) *Definition
Must panics on errors creating definitions.
func (*Definition) AnonymousField ¶
func (d *Definition) AnonymousField() bool
AnonymousField indicates that the definition has one field, (actually the original object), and thus the field doesn't get named as part of the name.
func (*Definition) Empty ¶
func (d *Definition) Empty() bool
Empty indicates that this definition has no fields.
func (*Definition) Parse ¶
func (d *Definition) Parse(rawMarker string) (interface{}, error)
Parse uses the type information in this Definition to parse the given raw marker in the form `+a:b:c=arg,d=arg` into an output object of the type specified in the definition.
type DefinitionHelp ¶
type DefinitionHelp struct { // DetailedHelp contains the overall help for the marker. DetailedHelp // Category describes what kind of marker this is. Category string // DeprecatedInFavorOf marks the marker as deprecated. // If non-nil & empty, it's assumed to just mean deprecated permanently. // If non-empty, it's assumed to be a marker name. DeprecatedInFavorOf *string // FieldHelp defines the per-field help for this marker, *in terms of the // go struct field names. Use the FieldsHelp method to map this to // marker argument names. FieldHelp map[string]DetailedHelp }
DefinitionHelp contains overall help for a marker Definition, as well as per-field help.
func DeprecatedHelp ¶
func DeprecatedHelp(inFavorOf, category, summary string) *DefinitionHelp
DeprecatedHelp returns simple help (a la SimpleHelp), except marked as deprecated in favor of the given marker (or an empty string for just deprecated).
func SimpleHelp ¶
func SimpleHelp(category, summary string) *DefinitionHelp
SimpleHelp returns help that just has marker-level summary information (e.g. for use with empty or primitive-typed markers, where Godoc-based generation isn't possible).
func (*DefinitionHelp) FieldsHelp ¶
func (d *DefinitionHelp) FieldsHelp(def *Definition) map[string]DetailedHelp
FieldsHelp maps per-field help to the actual marker argument names from the given definition.
type DetailedHelp ¶
DetailedHelp contains brief help, as well as more details. For the "full" help, join the two together.
type FieldInfo ¶
type FieldInfo struct { // Name is the name of the field (or "" for embedded fields) Name string // Doc is the Godoc of the field, pre-processed to remove markers and joine // single newlines together. Doc string // Tag struct tag associated with this field (or "" if non existed). Tag reflect.StructTag // Markers are all registered markers associated with this field. Markers MarkerValues // RawField is the raw, underlying field AST object that this field represents. RawField *ast.Field }
FieldInfo contains marker values and commonly used information for a struct field.
type MarkerValues ¶
type MarkerValues map[string][]interface{}
MarkerValues are all the values for some set of markers.
func PackageMarkers ¶
func PackageMarkers(col *Collector, pkg *loader.Package) (MarkerValues, error)
PackageMarkers collects all the package-level marker values for the given package.
func (MarkerValues) Get ¶
func (v MarkerValues) Get(name string) interface{}
Get fetches the first value that for the given marker, returning nil if no values are available.
type RawArguments ¶
type RawArguments []byte
RawArguments is a special type that can be used for a marker to receive *all* raw, underparsed argument data for a marker. You probably want to use `interface{}` to match any type instead. Use *only* for legacy markers that don't follow Definition's normal parsing logic. It should *not* be used as a field in a marker struct.
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry keeps track of registered definitions, and allows for easy lookup. It's thread-safe, and the zero-value can be safely used.
func (*Registry) AddHelp ¶
func (r *Registry) AddHelp(def *Definition, help *DefinitionHelp)
AddHelp stores the given help in the registry, marking it as associated with the given definition.
func (*Registry) AllDefinitions ¶
func (r *Registry) AllDefinitions() []*Definition
AllDefinitions returns all marker definitions known to this registry.
func (*Registry) Define ¶
func (r *Registry) Define(name string, target TargetType, obj interface{}) error
Define defines a new marker with the given name, target, and output type. It's a shortcut around
r.Register(MakeDefinition(name, target, obj))
func (*Registry) HelpFor ¶
func (r *Registry) HelpFor(def *Definition) *DefinitionHelp
HelpFor fetches the help for a given definition, if present.
func (*Registry) Lookup ¶
func (r *Registry) Lookup(name string, target TargetType) *Definition
Lookup fetches the definition corresponding to the given name and target type.
func (*Registry) Register ¶
func (r *Registry) Register(def *Definition) error
Register registers the given marker definition with this registry for later lookup.
type ScannerError ¶ added in v0.2.2
func (*ScannerError) Error ¶ added in v0.2.2
func (e *ScannerError) Error() string
type TargetType ¶
type TargetType int
TargetType describes which kind of node a given marker is associated with.
const ( // DescribesPackage indicates that a marker is associated with a package. DescribesPackage TargetType = iota // DescribesType indicates that a marker is associated with a type declaration. DescribesType // DescribesField indicates that a marker is associated with a struct field. DescribesField )
func (TargetType) String ¶
func (t TargetType) String() string
type TypeCallback ¶
type TypeCallback func(info *TypeInfo)
TypeCallback is a callback called for each type declaration in a package.
type TypeInfo ¶
type TypeInfo struct { // Name is the name of the type. Name string // Doc is the Godoc of the type, pre-processed to remove markers and joine // single newlines together. Doc string // Markers are all registered markers associated with the type. Markers MarkerValues // Fields are all the fields associated with the type, if it's a struct. // (if not, Fields will be nil). Fields []FieldInfo // RawDecl contains the raw GenDecl that the type was declared as part of. RawDecl *ast.GenDecl // RawSpec contains the raw Spec that declared this type. RawSpec *ast.TypeSpec // RawFile contains the file in which this type was declared. RawFile *ast.File }
TypeInfo contains marker values and commonly used information for a type declaration.