Typewriter is a framework for building code generation tools to produce Go code.
You can see how a simple code-generation code flow works by using built-in Producer
generator.
Let's say we have two versions of a database scheme for User
and Belonging
objects. In order to keep the systems working, we have variants of both objects
in our app like the following:
type UserV1 struct {
Name string
Surname string
Identifier int
Belongings []BelongingV1
}
type BelongingV1 struct {
Automobiles []string
}
type UserV2 struct {
Name string
Id int
Belongings []BelongingV2
UserGroup string
}
type BelongingV2 struct {
Cars []string
}
You can find these structs in
examples/producer/db
.
In order for our app to work with these two different schemes in database, we employ structs that are aggregated versions of two versions:
type UserAll struct {
Name string
Surname string
Id int
Identifier int
UserGroup string
Belongings []BelongingAll
}
type BelongingAll struct {
Automobiles []string
Cars []string
}
You can find these structs in
examples/producer/app
.
Now we need functions that takes UserAll
and produce UserV1
and UserV2
so
that we can choose whichever is needed depending on the use case. These functions
are pure iteration and assignment operations, but they are hard to test for all
cases and error prone when written by a human. So, we will generate them using
a built-in function generator, Producer
. Let's mark UserAll
by adding the
following comment so that typewriter can know which types to convert it to:
// +typewriter:types:aggregated=github.com/muvaf/typewriter/examples/producer/db.UserV1
// +typewriter:types:aggregated=github.com/muvaf/typewriter/examples/producer/db.UserV2
Typewriter uses standard package loading mechanisms that Go build tooling uses in the folder it's run. The package here can be either a local URL or a remote one.
Let's run the following function:
go run cmd/main.go --package-path examples/producer/app --target-package-path examples/producer
You'll see the result in examples/producer/producers.go
:
// Code generated by typewriter. DO NOT EDIT.
package producer
import (
app "github.com/muvaf/typewriter/examples/producer/app"
db "github.com/muvaf/typewriter/examples/producer/db"
)
// GenerateUserV1 returns a new db.UserV1 with the information from
// given app.UserAll.
func GenerateUserV1(a app.UserAll) db.UserV1 {
b := db.UserV1{}
b.Name = a.Name
b.Surname = a.Surname
b.Identifier = a.Identifier
if len(a.Belongings) != 0 {
b.Belongings = make([]db.BelongingV1, len(a.Belongings))
for v0 := range a.Belongings {
if len(a.Belongings[v0].Automobiles) != 0 {
b.Belongings[v0].Automobiles = make([]string, len(a.Belongings[v0].Automobiles))
for v1 := range a.Belongings[v0].Automobiles {
b.Belongings[v0].Automobiles[v1] = a.Belongings[v0].Automobiles[v1]
}
}
}
}
return b
}
// GenerateUserV2 returns a new db.UserV2 with the information from
// given app.UserAll.
func GenerateUserV2(a app.UserAll) db.UserV2 {
b := db.UserV2{}
b.Name = a.Name
b.Id = a.Id
b.UserGroup = a.UserGroup
if len(a.Belongings) != 0 {
b.Belongings = make([]db.BelongingV2, len(a.Belongings))
for v0 := range a.Belongings {
if len(a.Belongings[v0].Cars) != 0 {
b.Belongings[v0].Cars = make([]string, len(a.Belongings[v0].Cars))
for v1 := range a.Belongings[v0].Cars {
b.Belongings[v0].Cars[v1] = a.Belongings[v0].Cars[v1]
}
}
}
}
return b
}
Now we can use these functions wherever we need them.
You can take a look at cmd/main.go
to see how cmd.File
is used to generate a
file. It accepts a list of NewGeneratorFn
s and cmd.NewProducer
is a built-in
generator that we just tested. Go ahead and take a look at its implementation
and use it as reference for your own generator implementations.
Producer
is a function generator that generates functions that can produce an
object whose type is B
from an input object whose type is A
by recursively
traversing all fields and structs, matching the fields and writing assignment
statements.
You'll see that it's a very lean function generator implementation as it uses the built-in assignment templates. You can provide your own templates to be used for basic kind iterations and be creative about what you're doing for each matched field pair!
Section to be filled.