abgen is a command-line tool that automatically generates Go code for converting between two different struct types. It is highly configurable and designed to reduce boilerplate code, especially when converting between data models (DO), API models (VO), and protobuf-generated types (PB).
- Type-based Conversion: Generate converters by annotating a struct.
- Package-based Conversion: Generate converters for all matching types between two entire packages.
- Configurable conversion direction (
to,from,both). - Selective ignoring of types or fields.
abgen is controlled via //go:abgen:... directives in your Go source files.
To run the generator, execute the following command from your project's root directory:
go run ./tools/abgen/cmd/abgen <path-to-directory-with-directives>The generated code will be placed in a .gen.go file inside the target directory.
This is the most powerful feature. It allows you to generate converters for all matching types between two external packages.
-
Add Directives: In the package where you want the generated code to live, create a
.gofile (e.g.,provider.go) and add file-level directives at the top.File:
internal/service/provider.go// The following directives instruct abgen to generate converters for all matching types // between the data model package and the API package. //go:abgen:convert:package:source="github.com/your/project/internal/data/po" //go:abgen:convert:package:target="github.com/your/project/api/v1/system" //go:abgen:convert:package:direction=to //go:abgen:convert:package:ignore="InternalType,TimestampMixin" package service // ... rest of your code
-
Run
abgen:go run ./tools/abgen/cmd/abgen ./internal/service
-
Result: A
service.gen.gofile will be created ininternal/service, containing converter functions for all types that exist in both thepoandsystempackages (except for the ignored ones).
For one-off conversions or to override package-level behavior, you can add a directive directly to a struct or type alias.
These directives must use the //go:abgen: prefix and be placed immediately above the type declaration.
-
Add Directive:
package dto // Example for a simple struct: //go:abgen:convert:target="UserPB" //go:abgen:convert:ignore="Password" // Ignore specific fields type User struct { /* ... fields ... */ } // Example for a type alias within a type () block: type ( // The 'User' alias, used for conversion to 'UserPB'. // Directives must be placed immediately above the alias declaration. //go:abgen:convert:target="UserPB" //go:abgen:convert:direction="both" //go:abgen:convert:ignore="password,salt" User = ent.User // The 'UserPB' alias, used for conversion back to 'User'. //go:abgen:convert:target="User" //go:abgen:convert:direction="both" UserPB = typespb.User )
-
Run
abgen:go run ./tools/abgen/cmd/abgen ./internal/dto
-
Result: A
dto.gen.gofile will be created containing theUserToUserPBandUserPBToUserfunctions.
//go:abgen:convert:package:source="<import-path>": (Required) Sets the full import path for the source package.//go:abgen:convert:package:target="<import-path>": (Required) Sets the full import path for the target package.//go:abgen:convert:package:direction="<to|from|both>": (Optional) Sets the conversion direction. Defaults toboth.//go:abgen:convert:package:ignore="<TypeName1,TypeName2>": (Optional) A comma-separated list of types to ignore during generation.
//go:abgen:convert:target="<TypeName>": (Required) The name of the target type to convert to/from.//go:abgen:convert:direction="<to|from|both>": (Optional) The conversion direction for this specific type.//go:abgen:convert:ignore="<FieldName1,FieldName2>": (Optional) A comma-separated list of fields to ignore during conversion.