a "type-safe Go converter" generator
goverter is a tool for creating type-safe converters. All you have to do is create an interface and execute goverter. The project is meant as alternative to jinzhu/copier that doesn't use reflection.
- Automatic conversion of builtin types
(
house
example), this includes:- slices, maps, named types, primitive types, pointers
- structs with same fields
- Extend parts of the conversion with your own implementation: Docs
- Optional return of an error: Docs
- Awesome error messages: mismatch type test
- No reflection in the generated code
-
Create a go modules project if you haven't done so already
$ go mod init module-name
-
Add
goverter
as dependency to your project$ go get github.com/jmattheis/goverter
-
Create your converter interface and mark it with a comment containing
goverter:converter
input.go
package example // goverter:converter type Converter interface { Convert(source []Input) []Output } type Input struct { Name string Age int } type Output struct { Name string Age int }
-
Run
goverter
:$ go run github.com/jmattheis/goverter/cmd/goverter module-name-in-full # example $ go run github.com/jmattheis/goverter/cmd/goverter github.com/jmattheis/goverter/example/simple
-
goverter created a file at
./generated/generated.go
, it may look like this:package generated import simple "github.com/jmattheis/goverter/example/simple" type ConverterImpl struct{} func (c *ConverterImpl) Convert(source []simple.Input) []simple.Output { simpleOutputList := make([]simple.Output, len(source)) for i := 0; i < len(source); i++ { simpleOutputList[i] = c.simpleInputToSimpleOutput(source[i]) } return simpleOutputList } func (c *ConverterImpl) simpleInputToSimpleOutput(source simple.Input) simple.Output { var simpleOutput simple.Output simpleOutput.Name = source.Name simpleOutput.Age = source.Age return simpleOutput }
With goverter:name
you can set the name of the generated converter struct.
input.go
// goverter:converter
// goverter:name RenamedConverter
type BadlyNamed interface {
// .. methods
}
output.go
type RenamedConverter struct {}
func (c *RenamedConverter) ...
With goverter:extend
you can instruct goverter to use an implementation of
your own. You can pass multiple function names to goverter:extend
, or define
the tag multiple times.
See house
example sql.NullString
input.go
// goverter:converter
// goverter:extend IntToString
type Converter interface {
Convert(Input) Output
}
type Input struct {Value int}
type Output struct {Value string}
// You must atleast define a source and target type. Meaning one parameter and one return.
// You can use any type you want, like struct, maps and so on.
func IntToString(i int) string {
return fmt.Sprint(i)
}
If you need access to the generated converter, you can define it as first parameter.
func IntToString(c Converter, i int) string {
// c.DoSomething()..
return fmt.Sprint(i)
}
Sometimes, custom conversion may fail, in this case goverter allows you to
define a second return parameter which must be type error
.
// goverter:converter
// goverter:extend IntToString
type Converter interface {
Convert(Input) (Output, error)
}
type Input struct {Value int}
type Output struct {Value string}
func IntToString(i int) (string, error) {
if i == 0 {
return "", errors.New("zero is not allowed")
}
return fmt.Sprint(i)
}
Note: If you do this, methods on the interface that'll use this custom implementation, must also return error as second return.
With goverter:map
you can map fields on structs that have the same type.
goverter:map
takes 2 parameters.
- source field path (fields are separated by
.
) - target field name
// goverter:converter
type Converter interface {
// goverter:map Name FullName
// goverter:map Nested.Age Age
Convert(source Input) Output
}
type Input struct {
Name string
Nested NestedInput
}
type NestedInput struct {
Age int
}
type Output struct {
FullName string
Age int
}
With goverter:mapIdentity
you can instruct goverter to use the source struct
as source for the conversion to the target property.
goverter:mapIdentity
takes multiple field names separated by space
.
// goverter:converter
type Converter interface {
// goverter:mapIdentity Address
ConvertPerson(source Person) APIPerson
}
type Person struct {
Name string
Street string
City string
}
type APIPerson struct {
Name string
Address APIAddress
}
type APIAddress struct {
Street string
City string
}
In the example goverter will fill Address
by creating a converter from
Person
to APIAddress
. Example generated code:
func (c *ConverterImpl) ConvertPerson(source execution.Person) execution.APIPerson {
var structsAPIPerson execution.APIPerson
structsAPIPerson.Name = source.Name
structsAPIPerson.Address = c.structsPersonToStructsAPIAddress(source)
return structsAPIPerson
}
func (c *ConverterImpl) structsPersonToStructsAPIAddress(source execution.Person) execution.APIAddress {
var structsAPIAddress execution.APIAddress
structsAPIAddress.Street = source.Street
structsAPIAddress.City = source.City
return structsAPIAddress
}
With goverter:ignore
you can ignore fields on the target struct
goverter:ignore
takes multiple field names separated by space
.
// goverter:converter
type Converter interface {
// goverter:ignore Age
Convert(source Input) Output
}
type Input struct {
Name string
}
type Output struct {
Name string
Age int
}
goverter use SemVer for versioning the cli.
This project is licensed under the MIT License - see the LICENSE file for details
Logo by MariaLetta