Warning
Convgen is in early development. If you have any feedback, please open an issue.
Refactor-safe type conversion codegen for Go
Convgen generates type-to-type conversion code for Go automatically. It's type-safe, refactorable, and designed to detect every mismatch with detailed diagnostics.
// source:
var EncodeUser = convgen.Struct[User, api.User](nil,
convgen.RenameReplace("", "", "Id", "ID"), // Replace Id with ID in output types before matching
convgen.Match(User{}.Name, api.User{}.Username), // Explicit field matching
)
// generated: (simplified)
func EncodeUser(in User) (out api.User) {
out.Id = in.ID
out.Username = in.Name
out.Email = in.Email
return
}
-
Automatic type conversions by codegen
Convgen generates conversion code at compile time, so there's no runtime reflection overhead. It supports conversions between structs, unions (interfaces with multiple implementations), and enums (const groups), automatically matching fields, implementations, and members by name.convgen.Struct[User, api.User] // func(User) api.User convgen.StructErr[User, api.User] // func(User) (api.User, error) convgen.Union[Job, api.Job] // func(Job) api.Job -- type UploadJob, type OrderJob, ... convgen.UnionErr[Job, api.Job] // func(Job) (api.Job, error) convgen.Enum[Status, api.Status] // func(Status) api.Status -- const StatusTodo, const StatusPending, ... convgen.EnumErr[Status, api.Status] // func(Status) (api.Status, error)
-
Refactor-safe configuration
All options are validated at compile time — no struct tags, strings, or comment-based directives.// Custom conversion functions must be reachable. convgen.ImportFunc(strconv.Itoa) // If User{}.Name is renamed by a refactoring tool, // this directive will be updated accordingly. convgen.Match(User{}.Name, api.User{}.Username)
-
Batched diagnostics
All matching and conversion errors in a single pass are reported together, so you can fix everything at once instead of stopping at the first error. In addition, Convgen provides Lint support for real-time feedback during development.main.go:10:10: invalid match between User and api.User ok: Name -> Username // forced at main.go:12:2 ok: ID -> ID [Id] ok: GroupID -> GroupID [GroupId] FAIL: ? -> Email // missing FAIL: EMail -> ? // missing
Convgen is inspired by both goverter and Wire. While goverter is powerful for generating type conversion code, it relies on comment-based directives that are not validated at compile time. Moreover, because it stops at the first error, refactoring becomes difficult when target types change. In contrast, Wire offers type-safe configuration and detailed diagnostics, but focuses on dependency injection.
Convgen combines the best of both worlds, bringing refactor-safe configuration and batched diagnostics to automatic type conversions by codegen.
To compare Convgen and goverter, see cmd/vs-goverter directory.
-
Install Convgen:
go install github.com/sublee/convgen
-
Add a build constraint to files containing Convgen directives:
//go:build convgen
-
Declare your conversions:
var EncodeUser = convgen.Struct[User, api.User](nil)
-
Run the generator:
convgen ./...
-
Convgen generates a
convgen_gen.go
file by copying your//go:build convgen
files and rewriting Convgen directives:func EncodeUser(in User) (out api.User) { out.Name = in.Name out.Email = in.Email return }
Convgen provides a golangci-lint plugin that validates Convgen directives during development inside your IDE.
It's not an official plugin, so you need to build it manually:
make golangci-lint-convgen
Then, add the following configuration to your .golangci.yaml
. Because Convgen
directives are only valid when the convgen
build tag is set, make sure to
include it under the run.build-tags
section:
version: "2"
run:
build-tags:
- convgen
linters:
settings:
custom:
convgen:
type: module
Now the golangci-lint-convgen
binary can validate Convgen directives in your
project. To make this process seamless in your IDE, you can configure it as the
default linter. For Visual Studio Code, add the following to your settings:
{
"go.lintTool": "golangci-lint-v2",
"go.lintFlags": ["--build-tags=convgen"],
"go.alternateTools": {
"golangci-lint-v2": "/path/to/golangci-lint-convgen",
}
}
With this setup, your Convgen directives will be validated in real time as you code.
MIT License — see LICENSE for details.
Heungsub Lee, with assistance from ChatGPT