Skip to content

sublee/convgen

Repository files navigation

Warning

Convgen is in early development. If you have any feedback, please open an issue.

Convgen Logo

Convgen

Refactor-safe type conversion codegen for Go

CI Go Reference

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
}

Features

  • 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
    

Motivation

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.

Quick Start

  1. Install Convgen:

    go install github.com/sublee/convgen
  2. Add a build constraint to files containing Convgen directives:

    //go:build convgen
  3. Declare your conversions:

    var EncodeUser = convgen.Struct[User, api.User](nil)
  4. Run the generator:

    convgen ./...
  5. 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
    }

Lint

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.

License

MIT License — see LICENSE for details.

Author

Heungsub Lee, with assistance from ChatGPT

About

Refactor-safe type conversion codegen for Go

Topics

Resources

License

Stars

Watchers

Forks