mappergen
Static code generator that emits fast, type-safe mapping functions between Go structs. Think field-matching like typeconv, but compile-time generated functions instead of runtime reflection.
Status
- Early prototype: supports direct fields, pointer parity (no auto addr/deref), slices, maps, and basic numeric conversions (optional widening).
Install
go install github.com/vaughanb/mappergen/cmd/mappergen@latest
Quickstart
Given types:
type UserDTO struct {
ID string
Name string
Tags []string
}
type User struct {
ID string
Name string
Tags []string
}
Generate:
mappergen \
-from github.com/<you>/repo/pkg.UserDTO \
-to github.com/<you>/repo/pkg.User \
-out internal/mapper/usermappers.go \
-pkg pkg
Outputs usermappers.go with both directions by default:
// Code generated by mappergen; DO NOT EDIT.
package pkg
func MapUserDTOToUser(src UserDTO) User {
var dst User
dst.ID = src.ID
dst.Name = src.Name
if src.Tags != nil {
dst.Tags = make([]string, len(src.Tags))
for i := range src.Tags { dst.Tags[i] = src.Tags[i] }
}
return dst
}
func MapUserToUserDTO(src User) UserDTO {
var dst UserDTO
dst.ID = src.ID
dst.Name = src.Name
if src.Tags != nil {
dst.Tags = make([]string, len(src.Tags))
for i := range src.Tags { dst.Tags[i] = src.Tags[i] }
}
return dst
}
CLI
mappergen \
-from <module/path.Type> \
-to <module/path.Type> \
-out <path/to/file.go> \
-pkg <packageName> \
-allow-widening # optional numeric widening
-plan # print mapping plan and exit (no file written)
-stdout # write generated code to stdout
-check # verify on-disk file is up-to-date (non-zero on diff)
-strict # fail if any destination fields are skipped
-config ./.mapgen.yaml # scan-mode: generate many mappings grouped per package (one <pkg>mappers.go)
Notes:
- Function names default to `Map<Src>To<Dst>` using PascalCase.
- When using `-config`, both directions are generated per mapping and a single `<pkg>mappers.go` is written per destination package.
You can invoke functions for specific fields using destination tags:
type Src struct { Name string }
type Dst struct { Name string `map:",transform=strings.ToUpper"` }
func ToUpper(s string) string { return strings.ToUpper(s) }
Generates:
dst.Name = strings.ToUpper(src.Name)
Notes:
- Transformer can be local or package-qualified. Ensure imports are resolvable from the destination package.
- Supported signatures:
(In) Out and (In) (Out, error) (use transform_err=Func).
Destination fields can carry a map tag to control mapping:
map:"SrcName[,option=value]"
- Source override:
map:"OtherName" maps from source field OtherName.
- Transform:
map:",transform=Func" applies Func(src.Field) while keeping the same source name.
- Combine:
map:"OtherName,transform=Func" overrides the name and applies the transform.
Notes:
- Tags go on the destination field.
- Transform functions must be visible to the generated file (same package or imported) and accept the source field’s type.
- Pointer parity is enforced for non-transformed fields; if types differ only by pointer, the field is skipped.
Numeric widening (-allow-widening)
- When OFF (default): numeric fields must be the same type (or assignable), otherwise the field is skipped.
- When ON: safe widening casts are allowed (e.g.,
int32 → int64, float32 → float64). Narrowing is never allowed.
- Useful when DTOs use smaller numeric widths than your domain types, avoiding custom transformers while preventing truncation risks.
Goals (current subset)
- Deterministic, formatted output.
- Field name match (case-sensitive), assignable or convertible types.
- Pointer parity required (no implicit addr/deref).
- Slices and maps supported (element-wise copy; assignable or convertible elements).
Developing
go generate ./examples/...
go test ./...
CI
GitHub Actions runs go vet, go build, and go test on pushes and PRs to main.
License
MIT