This application provides a simple way of executing standard Go templates from the command line. The obvious use-case is for source code generation, amongst many others.
You can install it with:
go get github.com/rickb777/runtemplate
It is intended to be used directly from the command-line and also with go generate
.
It supports light-weight dependency checking, i.e. less work is done when the generated output file already exists and is up to date.
A selection of built-in templates is included with runtemplate
. These provide type-safe collection types etc.
Flexible option parsing is provided. Example
runtemplate -tpl filename.tpl -output outfile.go -deps foo.go,bar.go Type=MyStruct Option1:Value1 Option2:Value2
-
-tpl <name>
- (required) the name of the input template.
-
-output <name>
-o <name>
- the name of the output file. If
-tpl
is not specifed,-output
is required, otherwise it is optional. The name-
causes writing to standard out instead of a file. Standard out is also used if this flag is unspecified and there are nokey=value
types.
- the name of the output file. If
-
-deps <name>,<name>,...
- adds more dependencies to be checked in addition to the template itself and the 'type' file (if any).
-
-f
- force output generation; if this is not set the output file is only produced when it is older than the dependencies
-
-v
- verbose info messages
-
key:value ...
- (optional) supply a (list of) simple key/value pairs that are passed in to the template.
true
andfalse
are converted to booleans, allowing conditional blocks within your templates.
- (optional) supply a (list of) simple key/value pairs that are passed in to the template.
-
key=value ...
- (optional) supply a (list of) key/value pairs that are passed in to the template. These are often Go types; extra synthetic values are also added, making it really easy to generate source code. This is described further below.
true
andfalse
are converted to booleans, allowing conditional blocks within your templates.
- (optional) supply a (list of) key/value pairs that are passed in to the template. These are often Go types; extra synthetic values are also added, making it really easy to generate source code. This is described further below.
The option parser will also infer the template and output file names, so it is also permitted to use either
runtemplate -output outfile.go -tpl filename.tpl Type=MyStruct Option1:Value1 Option2:true
runtemplate outfile.go filename.tpl Type=MyStruct Option1:Value1 Option2:true
i.e. to omit the explicit flags -tpl
and -output
provided the files are named.
Furthermore, the output file may be completely omitted:
runtemplate filename.tpl Type=MyStruct Option1=Value1 Option2=true Option3:foo
in which case a name will be computed from all the values of the key=value pairs (excluding true/false) in the order they are specified, plus the name of the template, conjoined with underscores, plus the extension '.go'. All the key:value settings are excluded.
For the example above, it will be mystruct_value1_filename.go
because Option2 and Option3 are ignored for the reasons above.
Easy. Just put the go generate
comment in your code like this:
//go:generate runtemplate -tpl filename.tpl -output outfile.go Option1=Value1 Option2:Value2
When you run go generate
, it will find these marked comments and execute their commands. This will runtemplate
against the specified template, passing in whatever options have have been specified on the command line as a map.
In the template file, you can access the key=value or key:value pairs simply by their keys. For instance:
{{ .Option1 }}
Boolean true/false key-values are available for {{if .Flag}} ... {{end}}
conditional use. Undefined values default to false.
The key:value syntax (using colon) defines simple values. These can be repeated to supply a slice of values, which is useful for the template range
operator.
The values true
and false
are converted to booleans.
Foo:Bar | Foo:Ban Foo:Bar Foo:Baz | |
---|---|---|
.Foo |
Bar |
{Ban , Bar , Baz } |
.HasFoo |
true |
true |
The key=value
syntax (using equals) does more and is intended for identifiers in the programming language of the generated code (usually Go).
Unlike most of runtemplate behaviour, the type definition can become quite specific to Go (in other contexts, what follows may not be wholly relevant).
It is possible to give aliases for types. Specifically, the string following Type=
contains one, two or three slash-separated parts. The simple case is like Type=string
, i.e. one part; this becomes the alias String
when composing identifiers. When required, the zero value for the type is computed: specifically, for built-in types the zero value is well defined; for other types, the zero value is obtained using the new
built-in.
For types such as interface{}
, this doesn't work because it cannot form any part of an identifier due to the braces. So there is a common use-case; for example it is valid to use Type=interface{}/Any/nil
; here, the three parts represent the type itself (interface{}
), the alias (Any
), and the expression used for the zero value (nil
).
If only two parts are present, e.g. Type=foo/Bar
, the type and alias are defined but the absent zero value follows the default rule described above.
The values are supplemented by additional entries in the template's context. For example, given Type=SomeValue
or Type=*SomeValue
, these are:
.Type
- the type name (prefixed by '*' if supplied).Type.String
- same as above (because this is simply astring
, it may be used in '==' comparisons).Type.Name
- the type name (without any '*' prefix).Type.U
- the type alias having its first character converted to uppercase - useful for exported identifiers; dots are removed..Type.L
- the type alias having its first character converted to lowercase - useful for internal identifiers; dots are removed..Type.IsPtr
- boolean indicating whether '*' is supplide.Type.Star
- a '*' if the type is a pointer type, otherwise blank.Type.Amp
- a '&' if the type is a pointer type, otherwise blank.Type.Zero
-nil
if the type is a pointer type, otherwise the zero value for the type.Type.Ident
- the type alias as aRichString
that has several extra methods (ToUpper
,ToLower
etc).HasType
- set totrue
to allow conditional expressions (it defaults to false if undefined)
This table shows two examples of context symbols defined for Type=big.Int
and Type=*big.Int
.
Type=big.Int |
Type=*big.Int |
|
---|---|---|
.Type |
big.Int |
*big.Int |
.Type.Name |
big.Int |
big.Int |
.Type.U |
BigInt |
BigInt |
.Type.L |
bigInt |
bigInt |
.Type.IsPtr |
false | true |
.Type.Star |
blank | * |
.Type.Amp |
blank | & |
.Type.Zero |
*(new(big.Int)) |
nil |
.HasType |
true |
true |
Be aware that your shell might expand *
so you may need suitable quote marks, such as 'Type=*Foo'
. This is not needed when using go:generate
comment lines.
If you need to generate code for several generated types and they need to co-exist within the same package, you can easily define a prefix to differentiate their names.
For every <X>Type
template value that you specify (for some <X>
), there is a corresponding special value <X>Prefix
that is always predefined with a blank default value. But you can set it to something else, and if you do the generated types can use this to prefix their names. This only happens for keys that end in Type
.
In short, the key's suffix Type
is replaced with Prefix
.
As well as <X>Prefix
, there will be <X>UPrefix
and <X>LPrefix
as above.
Additional settings are also made available:
.OutFile
- the name of the output file.TemplateFile
- the template name as specified.TemplatePath
- the location and name of the actual template file used.Package
- the name of the directory of the output file (often the current directory).AppVersion
- the version of Runtemplate that is being used.GOARCH
,.GOOS
,.GOPATH
,GOROOT
- the value of Go environment variables.
Some filters are also included that may be helpful.
- title - Converts the input to Title Case.
- upper - Converts the input to UPPER CASE.
- lower - Converts the input to lower case.
- firstUpper - Converts the first character of input to upper case.
- firstLower - Converts the first character of input to lower case.
- condFirstUpper - Given a string and a boolean, converts the first character of input to upper case when the boolean is true.
- splitDotFirst - Given an input that has a '.' separator, returns the part before the first '.'.
- splitDotLast - Given an input that has a '.' separator, returns the part after the last '.'.
The last two are useful for getting only the package name or only the type name if passed an input of package.Type
.
Templates are located by following TEMPLATEPATH
, an optional environment variable. If it is defined, it is used like PATH
, i.e. a colon-separate list of directories to be searched.
If TEMPLATEPATH
is absent, its default is TEMPLATEPATH=.
, i.e. templates are relative to the current directory.
The builtin templates are also available and are searched if no other match is found. For example, template "types/stringy.tpl" will resolve to the built-in template of that name unless the TEMPLATEPATH contains another file with the same path.
A selection of built-in templates is included with runtemplate
. These provide type-safe collection types. Their API style has been loosely influenced by other similar Go types and the excellent Scala collection classes.