Learned a couple of things while writing gomakeme - a code generator / boilerpate reducer for Fiber REST APIs.
The general concept of generating code with the text/templates
package is quite simple.
And that's the gist of it.
This example holds one of the most basic examples of file / code generation. We define a struct, create a variable based on the struct. Then we get the template and pass down the defined values to it.
If you want to see all of the data that is passed down to the template, you can write
{{ . }}
inside the template.
Let's say we pass down the Day
field from the MyData struct and mention it inside the template 20 times. What happens when we want to rename the field of the struct? We also need to change all of those instances of the data inside the template... or we can just assign the passed down data to a variable inside the template and change only the value of the variable.
Assigning variables inside the template could look like this.
{{ $day := .Day }}
To use the variable inside the template, we can just write
Today is {{ $day }}
To have some structure, it's easier to define all of the variables at the top of the template.
Note that when you execute the template in this case, the line in which you assign the data to the variable will be seen as an empty line in the executed template. If you don't want them to register as empty lines, you need to add - before the ending curly braces, like this
{{ $day := .Day -}}
You can also concatenate multiple strings using printf
to create new values
{{ $day_concat := printf "%s%s" "Concatenated" $day -}}
So now we can pass down data to the template and assign it to the variables. What if we want to convert the passed down data to have a specific form? Like converting Friday
to be all uppercase? For that we can use functions.
To use them inside the template, we need to import them. This is done by modifying the function that parses the template a bit.
tmpl, err := template.New("my_template.tpl").Funcs(template.FuncMap{
"functionThatConvertsInput": strings.ToUpper,
}).ParseFiles("./templates/my_template.tpl")
to use the functionThatConvertsInput
inside the template, we can just put the mapped function before the variable.
{{functionThatConvertsInput $day_concat}}
the output of the tempate would capitalize the variable.
If you execute the template and an empty file is created, then that most probably means that you used a variable inside the template that does not exist.
You can pass down nested structs and still access those values inside the template, like
{{ .SecondStruct.SomeValue }}
The name of the template in the template.New()
should be the same as the one that you mention in .ParseFiles()
You can also use conditionals and for loops inside the templates.
Name you files with the .tpl file extension and install the Smarty Template Support
extension in VSCode to get highlights
Instead of running
go run main.go
every time you want to create new output, you can add a comment that starts with go:generate
before the main
function that will trigger the script
//go:generate go run main.go
func main() {
// do some stuff
}
and just run
go generate