Copygen
Copygen is a command-line code generator that generates type-to-type and field-to-field struct code without adding any reflection or dependencies to your project. Manual-copy code generated by Copygen is 391x faster than jinzhu/copier, and adds no allocation to your program. Copygen is the most customizable type-copy generator to-date and features a rich yet simple setup inspired by goverter.
Topic | Categories |
---|---|
Usage | Types, Setup, Command Line, Output |
Customization | Custom Types, Templates |
Matcher | Automatch, Depth |
Optimization | Shallow Copy vs. Deep Copy, When to Use |
Usage
Each example has a README.
Example | Description |
---|---|
main | The default example. |
manual | Uses the manual map feature. |
automatch | Uses the automatch feature with depth. |
deepcopy (Roadmap Feature) | Uses the deepcopy option. |
error | Uses templates to return an error (temporarily unsupported). |
This example uses three type-structs to generate the ModelsToDomain()
function.
Types
./domain/domain.go
// Package domain contains business logic models.
package domain
// Account represents a user account.
type Account struct {
ID int
UserID string
Name string
Other string // The other field is not used.
}
./models/model.go
// Package models contains data storage models (i.e database).
package models
// Account represents the data model for account.
type Account struct {
ID int
Name string
Password string
Email string
}
// A User represents the data model for a user.
type User struct {
UserID int
Name int
UserData string
}
Setup
Setting up Copygen is a 2-step process involving a YML
and GO
file.
setup.yml
# Define where the code will be generated.
generated:
setup: ./setup.go
output: ../copygen.go
# Define the optional custom templates used to generate the file.
template: ./generate.go
# Define custom options (which are passed to generator options) for customization.
custom:
option: The possibilities are endless.
The main example ignores the template fields.
setup.go
Create an interface in the specified setup file with a type Copygen interface
. In each function, specify the types you want to copy from as parameters, and the type you want to copy to as return values.
/* Specify the name of the generated file's package. */
package copygen
/* Copygen defines the functions that will be generated. */
type Copygen interface {
// custom see table below for options
ModelsToDomain(models.Account, models.User) *domain.Account
}
Copygen uses no allocation with pointers which means fields are assigned to objects passed as parameters.
options
You can specify options for your functions using comments. Do NOT put empty lines between comments that pertain to one function. Options are evaluated in order of declaration.
Option | Use | Description | Example |
---|---|---|---|
map from to |
Manual Field Mapping | Copygen uses its automatcher by default. Override this using map to set fields that will be mapped to and from eachother. Regex is supported for from-fields. |
map .* package.Type.Field map models.Account.ID domain.Account.ID |
depth field level |
Use a specific field depth. | Copygen uses the full-field depth by default. Override this using depth with regex and a depth-level integer. |
depth .* 2 depth models.Account.* 1 |
deepcopy field |
Deepcopy from-fields. | Copygen shallow copies fields by default. Override this using deepcopy with regex. For more info, view Shallow Copy vs. Deep Copy. |
deepcopy package.Type.Field deepcopy .* (all fields) |
custom option |
Specify custom options. | You may want to use custom templates. custom options are passed to a function's options. Returns map[string][]string (trim-spaced). |
ignore true swap false |
Convert
In certain cases, you may want to specify a how a specific type or field is copied with a function. This can be done by defining a function with a convert
option.
/* Define the function and field this converter is applied to using regex. */
// convert .* models.User.UserID
// Itoa converts an integer to an ascii value.
func Itoa(i int) string {
return c.Itoa(i)
}
Command Line
Install the command line utility. Copygen is an executable and not a dependency, so use go install
.
go install github.com/switchupcb/[email protected]
Install a specific version by specifying a tag version.
go install github.com/switchupcb/[email protected]
Run the executable with given options.
# Specify the .yml configuration file.
copygen -yml path/to/yml
The path to the YML file is specified in reference to the current working directory.
Output
This example outputs a copygen.go
file with the specified imports and functions.
// Code generated by github.com/switchupcb/copygen
// DO NOT EDIT.
// Package copygen contains the setup information for copygen generated code.
package copygen
import (
c "strconv"
"github.com/switchupcb/copygen/examples/main/domain"
"github.com/switchupcb/copygen/examples/main/models"
)
// Itoa converts an integer to an ascii value.
func Itoa(i int) string {
return c.Itoa(i)
}
// ModelsToDomain copies a Account, User to a Account.
func ModelsToDomain(tA *domain.Account, fA models.Account, fU models.User) {
// Account fields
tA.Name = fA.Name
tA.UserID = Itoa(fU.UserID)
tA.ID = fA.ID
}
Customization
Copygen's method of input and output allows you to generate code not limited to copying fields.
Custom Types
Custom types external to your application can be created for use in the setup.go
file. When a file is generated, all types (structs, interfaces, funcs) are copied EXCEPT the type Copygen interface
.
type DataTransferObject struct {
// ...
}
type Model interface {
// ...
}
func New() {
// ...
}
Templates
Templates can be created using Go to customize the generated code algorithm. The copygen
generator uses the package templates Generate(*models.Generator)
to generate code. As a result, this funtion is required for your templates to work. View models.Generator for context on the parameters passed to each function. Generator options are parsed from the YML configuration file. Function options refer to custom
options. Any other option represents a field option.
Templates are interpreted by yaegi which has limitations on module imports (Pull Request Pending): As a result, templates are temporarily unsupported. The error example modifies the .yml to use custom functions which return error
. This is done by modifying the .yml and creating custom template files.
Matcher
Copygen provides two ways to configure fields: Manually and using the Automatcher. Matching is specified in a .go
file (which functions as a schema in relation to other generators). Tags are complicated to use with other generators which is why they aren't used.
Automatch
When fields aren't specified using options, Copygen will attempt to automatch type-fields by name. Automatch supports field-depth (where types are located within fields) and recursive types (where the same type is in another type). Automatch loads types from Go modules (in GOPATH). Ensure your modules are up to date by using go get -u
.
Depth
The automatcher uses a field-based depth system. A field with a depth-level of 0 will only match itself. Increasing the depth-level allows its sub-fields to be matched. This system allows you to specify the depth-level for whole types and specific fields.
// depth-level in relation to the first-level fields.
type Account
// 0
ID int
Name string
Email string
Basic domain.T // int
User domain.DomainUser
// 1
UserID string
Name string
UserData map[string]interface{}
// 0
Log log.Logger
// 1
mu sync.Mutex
// 2
state int32
sema uint32
// 1
prefix string
flag int
out io.Writer
// 2
Write func(p []byte) (n int, err error)
buf []byte
Optimization
Shallow Copy vs. Deep Copy
The library generates a shallow copy by default. An easy way to deep-copy fields with the same return type is by using new()
as/in a converter function or by using a custom template.
When to Use Copygen
Copygen's customizability gives it many potential usecases. However, Copygen's main purpose is save you time by generating boilerplate code to map objects together.
Why would I do that?
In order to keep a program adaptable (to new features), a program may contain two types of models. The first type of model is the domain model which is used throughout your application to model its business logic. For example, the domain models of Copygen focus on field relations and manipulation. In contrast, the ideal way to store your data (such as in a database) may not match your domain model. In order to amend this problem, you create a data model. The data models of Copygen are located in its configuration loader. In many cases, you will need a way to map these models together to exchange information from your data-model to your domain (and vice-versa). It's tedious to repeateadly do this in the application (through assignment or function definitions). Copygen solves this problem.
Contributing
You can contribute to this repository by viewing the Project Structure, Code Specifications, and Roadmap.