Copygen

GoDoc
Go Report Card
MIT License

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 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 with alloc.
automatch Uses the automatch feature with depth (doesn’t require fields).
deepcopy (Roadmap Feature) Uses the deepcopy option.
error Uses templates to return an error (temporarily unsupported).

NOTE: The following guide is set for v0.2 (view v0.1)

This example uses three type-structs to generate the ModelsToDomain() function.

Types

./domain/domain.go

// The domain package 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

// The models package 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 {
	ID       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
  package: copygen

# Define the optional custom templates used to generate the file.
# Templates are currently unsupported.
templates:
  header: ./templates/header.go
  function: ./templates/function.go

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.

/* Copygen defines the functions that will be generated. */
type Copygen interface {
  // custom: see table below for options
  ModelsToDomain(models.Account, models.User) domain.Account
}

You can specify options for your functions using comments.

Option Use Description Example
alloc Return a new type object(s) Copygen uses no allocation by default which means that
fields are assigned to objects passed as parameters.
Use alloc to return a new copy of your to-types.
alloc
map from to Manual Field Mapping Copygen uses its automatcher default. Override this using map
which uses regex to identify fields that will be mapped to and from eachother.
map .* package.Type.Field
map models.Account.ID domain.Account.ID
depth field level Use a specific field depth. When fields have fields, Copygen uses the full-field depth by default.
Override this using depth which uses a regex string and depth-level integer.
depth .* 2
depth models.Account.* 1
deepcopy field Deepcopy from-fields. Copygen shallow copies fields by default. Use deepcopy to override this.
For more information, view Shallow Copy vs. Deep Copy.
deepcopy package.Type.Field
deepcopy .* (deepcopies all fields)
custom: option
custom: option: option
Specify custom options. You may want to use custom options in your templates.
Options of your choice are passed to the generator object using custom.
Custom options are parsed as trim-spaced strings.
Each : adds an additional key to a map.
custom: ignore
custom: swap: false

View a reference on Regex.

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 fields this converter is applied to using regex. If unspecified, converters are applied to all valid fields. */
// convert: models.User.ID
// comment: Itoa converts an integer to an ascii value.
func Itoa(i int) string {
  return strconv.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

import (
	"github.com/switchupcb/copygen/examples/main/converter"
	"github.com/switchupcb/copygen/examples/main/domain"
	"github.com/switchupcb/copygen/examples/main/models"
)

// ModelsToDomain copies a User, Account to a Account.
func ModelsToDomain(tA *domain.Account, fU models.User, fA models.Account) {
	// Account fields
	tA.UserID = c.Itoa(fU.ID)
	tA.ID = fA.ID
	tA.Name = fA.Name

}

Customization

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.

Templates

Templates can be created using Go to customize the generated code algorithm. The copygen generator uses the package tenplates Header(*models.Generator) to generate header code and Function(*models.Function) to generate code for each function. As a result, these (package templates with functions) are required for your templates to work. View models.Generator and models.Function for context on the parameters passed to each function. Templates are interpreted by yaegi which has limitations on module imports (that are being fixed): As a result, templates are temporarily unsupported.

Matcher

Copygen provides two ways to configure fields: Manually and 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 <insert/module/import/path>.

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 loader(s). 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.

GitHub

https://github.com/switchupcb/copygen