copygen

command module
v0.4.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 26, 2025 License: AGPL-3.0 Imports: 2 Imported by: 0

README


Copygen Logo


GoDoc License Mentioned in Awesome Go

Copygen saves you from losing time writing repetitive code with a type-based code generator.

What is Copygen?

Copygen is a command-line and programmatic code generator that generates custom type-based code, including type-to-type and field-to-field 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 supports every Go type including basic, array, slice, map, chan, interface, and func types.

Table of Contents

Topic Categories
Usage Types, Setup, Command Line, Output
Customization Custom Objects, Templates
Matcher Automatch, Manual, Depth
Usecases When to Use, Custom Generation
License What can I do?, License Exception

How do you use Copygen?

Each example has a README.

Example Description
main The default example.
basic Matches a basic type to a field.
automatch Uses the automatch matcher with depth.
map Uses the manual map matcher.
tag Uses the manual tag matcher.
cast Uses the cast modifier.
deepcopy (Roadmap) Uses the deepcopy option.
error Uses .go templates to return an error.
tmpl Uses .tmpl templates.
program Uses Copygen programmatically.

*multi tests every type.

This example uses three type-structs to generate the ModelsToDomain() function using a Command Line Interface.

Step 1. Define Go Types

Go types are defined in a file.

./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     string
	UserData string
}

The models.Account and models.User fields will be copied to domain.Account fields.

Step 2. Configure the setup files.

You set up Copygen with 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 (.go, .tmpl supported).
  # template: ./generate.go

# Define custom options (which are passed to generator options) for customization.
custom:
  option: The possibilities are endless.
setup.go

Define a type Copygen interface in the specified setup file.

In each function, specify the types you want to copy from as parameters, and the types you want to copy to as return values.

This interface is inspired by goverter.

/* Specify the name of the generated file's package. */
package copygen

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

Copygen uses no allocation with pointers because Go is pass-by-value. So, using a pointer results in the object's fields being assigned directly as opposed to a copy of the object's fields.

options

Use comments to specify options for Copygen functions: Do NOT add empty lines between comments that pertain to one function.

Options are evaluated in order of declaration.

Option Use Description Example
automatch field Use the automatcher selectively or with map and tag options. Using map or tag disables the default automatcher.
Enable it again using automatch with regex.
automatch package.Type.Field
automatch models.User.*
map from to Map fields manually. map fields to and from each other.
Regex is supported for from-fields.
map .* package.Type.Field
map models.Account.ID domain.Account.ID
tag field key Map fields manually using struct tags. Use tag with regex and a tag key. tag package.Type.Field key
tag .* api (all fields)
depth field level Use a specific field depth. Copygen uses 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 function options. Use custom options with templates.
Returns map[string][]string (trim-spaced).
ignore true
swap false

View a reference on Regex.

A matching option (e.g. map, automatch, tag) determines whether the field will be matched to another field, but a modifying option (e.g. convert, cast) is only applied when a field is matched.

Convert

Use the convert function field option to control how a type or field is copied within a function when the field is matched.

/* 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)
}

This example converts the models.User.UserID value using Itoa within all functions (.*) when the models.User.UserID field is matched.

Cast

Use the setup.yml matcher: cast generator option to enable automatic casting when a field is matched.

Use the setup.go cast from to modifier option to perform direct type assertion, conversion, expressions, function usage, and property usage with a matched field.

For more information, read the cast example.

Step 3. Use the Command Line

Install the command line utility: Copygen is an executable, not a dependency, so use go install.

go install github.com/switchupcb/copygen@latest

Install a specific version by specifying a branch.

go install github.com/switchupcb/copygen@main

Install a specific version by specifying a tag.

go install github.com/switchupcb/copygen@v0.4.1

Run the executable with given options.

# Specify the .yml configuration file.
copygen -yml path/to/yml

The path to the YML file must be 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 *models.Account, *models.User to a *domain.Account.
func ModelsToDomain(tA *domain.Account, fA *models.Account, fU *models.User) {
	// *domain.Account fields
	tA.ID = fA.ID
	tA.UserID = Itoa(fU.UserID)
	tA.Name = fA.Name
}

Customization

Copygen's method of input and output lets you generate code that isn't limited to copying fields.

Custom Objects

Custom types external to your application can be defined in the setup file (.go): When an output file is generated, all types (structs, interfaces, funcs) defined in the setup file (.go) are copied EXCEPT the type Copygen interface.

type DataTransferObject struct {
  // ...
}

type Model interface {
  // ...
}

func New() {
  // ...
}

Shallow Copy vs. Deep Copy

The library generates shallow copy functions by default.

Do you need to deepcopy instead? Use new() within a convert or cast function or use a customized generator template.

Templates

Copygen supports three methods of code generation: .go, .tmpl, and programmatic.

You can view the models.Generator type for context on the parameters passed to each function.

  • Generator options are parsed from the YML configuration file.
  • Function options are parsed from custom options.
  • Any other option represents a FieldOption.
.go

Use .go files to customize the code generation algorithm: The copygen generator uses the package template Generate(*models.Generator) (string, error) to generate code. So, this function is required for your .go templates to work.

The error example modifies the configuration file (.yml) to use custom .go template functions that return error. The template/generate.go file provides the default code generation algorithm for generating code.

Use of non-extracted Go Module Imports in generate.go template files are unsupported at the current time.

.tmpl

Use .tmpl (text/templates) to customize the code generation algorithm. The template example uses a .tmpl file to generate code.

programmatic

Use copygen as a third-party module in your application. For more information, read the program example.

Matcher

Copygen provides two methods of field-matching: automatch and manual.

You can disable the matcher using the matcher: skip: true option in the setup file.

Automatch

Copygen automatically matches the function's fields by field name and definition when a matching option (automatch,map,tag) isn't specified on a function.

  • Automatch matches one from-field to many to-fields
  • Automatch supports field-depth (when fields contain fields) and recursive types (when the field contains itself).
  • Automatch loads types from Go modules (in the GOPATH): Confirm your Go modules are up-to-date using go get -u <insert/module/import/path>.

Manual

Using the map or tag option disables the automatcher, which lets you manually match fields. In order to re-enable the automatcher, use the automatch option.

Options are evaluated in order of declaration, so using automatch .* after declaring map and tag options is an easy way to re-enable the automatcher for remaining fields.

Depth

The automatcher uses a field-based depth system where a field with a depth-level of 0 only matches itself. This system lets you specify the depth-level for specific types and fields. Increasing the depth-level lets the field's sub-fields at a specified depth-level be matched.

// Depth-level in relation to the first-level field (0).
type Account
  // 1
  ID      int
  Name    string
  Email   string
  Basic   domain.T // int
  User    domain.DomainUser
              // 2
              UserID   string
              Name     string
              UserData map[string]interface{}
  // 1
  Log     log.Logger
              // 2
              mu      sync.Mutex
                          // 3
                          state   int32
                          sema    uint32
              // 2
              prefix  string
              flag    int
              out     io.Writer
                          // 3
                          Write   func(p []byte) (n int, err error)
              buf     []byte

Usecase

When to Use Copygen

Copygen's default purpose is to save you time by generating code to map objects together.

Why would you do that?

You generate code to map objects together when your program contains multiple models types (e.g., domain, database), which improves feature development speed without affecting performance.

Here is an example.

Suppose a program maintains two types of models for feature development.

  • The domain model is a human-optimized definition of the business logic, and is used by a human to develop business logic. For example, an account model which is defined for use by a human.
  • The data model is a machine-optimized definition of the business logic, and is used by a machine to execute business logic efficiently. For example, a database model of an account which defines username, profile_picture, and other account related data in separate models for database efficiency.

These models are created because the ideal method of data storage is not the ideal domain model. However, you must map these models together to exchange information from your data model to your domain model and vice-versa.

Here is an example of how Copygen uses this development pattern.

  • The domain models of Copygen focus on field relations and manipulation as understood by a human.
  • The data models of Copygen are located in its configuration loader and Go type ast structs.
  • The "business logic" of Copygen is defined in the parser, matcher, and generator, which use the domain models of Copygen to generate code instead of underlying ast data structs.

Custom Generation

Copygen's customizability with templates lets you generate any code based on types (and their respective fields, tags, etc).

Example Description
Repogen Generate a Business-Logic Repository package based on a Data Access Object (DAO) model.
Wrapper Generate functions for requests using an API resource and request object model.

Check out more usecases in real-world examples using Copygen Usecases.

What is the License?

Copygen uses a AGPLv3 License.

An exception is provided for template and example files, which are licensed under the MIT License.

What can you do with this license?

Code generated by Copygen can be used without restriction (including proprietary and commercial usage) since generated code is not considered a derivative work. However, modifications to the Copygen Software Source Code or implementing Copygen in a larger work programmatically requires you to adhere to the AGPLv3 License.

These restrictions do NOT apply to template or example files, as long as those files don't generate Copygen itself.

What is a license exception?

A license exception lets you modify and use Copygen programmatically without restriction.

You can receive a license exception for Copygen by contacting SwitchUpCB using the Copygen License Exception Inquiry Form.

Contributing

You can contribute to this repository by viewing the Project Structure, Code Specifications, CI/CD, and Roadmap.

Documentation

The Go Gopher

There is no documentation for this package.

Directories

Path Synopsis
cli
config
Package config loads configuration data from an external file.
Package config loads configuration data from an external file.
generator
Package generator generates code.
Package generator generates code.
generator/interpreter
Package interpreter interprets template code at runtime.
Package interpreter interprets template code at runtime.
generator/interpreter/extract
Package extract uses the `yaegi extract` tool in order to generate the reflect.Value symbols of internal types.
Package extract uses the `yaegi extract` tool in order to generate the reflect.Value symbols of internal types.
generator/template
Package template provides a template used by copygen to generate custom code.
Package template provides a template used by copygen to generate custom code.
matcher
Package matcher matches fields.
Package matcher matches fields.
models
Package models defines the domain models that model field relations and manipulation.
Package models defines the domain models that model field relations and manipulation.
parser
Package parser parses a setup file's functions, types, and fields using an Abstract Syntax Tree.
Package parser parses a setup file's functions, types, and fields using an Abstract Syntax Tree.
parser/options
Package options parses function comments and sets them to fields.
Package options parses function comments and sets them to fields.
examples module

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL