Skip to content

jotadrilo/go-factory

Repository files navigation

go-factory

A factory code autogenator for Go

Installation

go install github.com/jotadrilo/go-factory

Linux

mkdir -p go-factory && \
curl -sSL https://github.com/jotadrilo/go-factory/releases/download/1.0.3/go-factory_1.0.3_linux_x86_64.tar.gz | tar xzf - -C go-factory && \
sudo cp go-factory/go-factory /usr/local/bin/ && \
sudo chmod +x /usr/local/bin/go-factory

MacOS

Homebrew users:

brew tap jotadrilo/tap
brew install jotadrilo/tap/go-factory

Alternative:

mkdir -p go-factory && \
curl -sSL https://github.com/jotadrilo/go-factory/releases/download/1.0.3/go-factory_1.0.3_darwin_arm64.tar.gz | tar xzf - -C go-factory && \
sudo cp go-factory/go-factory /usr/local/bin/ && \
sudo chmod +x /usr/local/bin/go-factory

Configuration

You can configure go-factory by adding annotations to your Go files, or placing a .gofactory.yaml configuration file in your project root directory.

See the examples section.

Annotations

You can annotate your structs with //go:generate go-factory -n Name

Configuration File

The program will auto-detect any .gofactory.yaml file in your project root directory (a directory from your current directory to the filesystem root directory with a go.mod file inside) or in your current directory.

You can specify one with the --config or -c flag.

The configuration file must include a configuration section per package.

External packages are not supported.

packages:
  - name: 'github.com/jotadrilo/go-factory/examples'
  - name: 'github.com/jotadrilo/go-factory/examples/inner'
    factory_file_tpl: '{{ .ProjectDir }}/{{ .PackageDirRel }}/{{ .Filename }}_factory.go'
    include:
      - A
      - B
    exclude:
      - C
      - D

Parameters

Parameter Description
packages Array of packages configuration
packages[n].name Name of the package to generate factories for
packages[n].factory_file_tpl Go template to configure the factory file output location and name. Default: '{{ .ProjectDir }}/{{ .PackageDirRel }}/{{ .Filename }}_factory.go'
packages[n].include Array of struct names to include in the generation. Default: []
packages[n].exclude Array of struct names to exclude from the generation. Default: []

If both include and exclude parameters are blank, it will generate factories for all structs in the package.

Factory File Template

Let's use the example examples/inner/foo.go in this project, assuming that I have cloned the project in /home/user/go-factory:

This template can be configured with the following parameters:

Parameter Description Example
ProjectDir Project directory /home/user/go-factory
PackageName Name of the package inner
PackageDirRel Relative path to the directory of the package examples/inner
TypeName Name of the struct type For Foo struct: Foo
Filename Name of the file declaring the struct, without its extension For Foo struct: foo

Examples

package examples

//go:generate go-factory -n A
type A struct {
	Bool   bool
	String string
}

//go:generate go-factory -n B
type B struct {
	Name string
}

//go:generate go-factory -n C
type C struct {
	A  *A
	B  *B
	As []*A
	Bs []*B
}

Generates:

// Code generated by go-factory 1.0.2; DO NOT EDIT.

package examples

// FactoryC is a helper factory to ease creating data of type C
type FactoryC struct {
	Factory C
}

func NewFactoryC() *FactoryC {
	return &FactoryC{}
}

func (f *FactoryC) WithA(value *A) *FactoryC {
	f.Factory.A = value
	return f
}

func (f *FactoryC) WithB(value *B) *FactoryC {
	f.Factory.B = value
	return f
}

func (f *FactoryC) WithAs(values ...*A) *FactoryC {
	f.Factory.As = values
	return f
}

func (f *FactoryC) AddAs(values ...*A) *FactoryC {
	f.Factory.As = append(f.Factory.As, values...)
	return f
}

func (f *FactoryC) WithBs(values ...*B) *FactoryC {
	f.Factory.Bs = values
	return f
}

func (f *FactoryC) AddBs(values ...*B) *FactoryC {
	f.Factory.Bs = append(f.Factory.Bs, values...)
	return f
}

func (f *FactoryC) Build() *C {
	return deepCopyC(&f.Factory)
}

func deepCopyC(src *C) *C {
	if src == nil {
		return nil
	}
	copyInstance := *src
	if src.A != nil {
		copyInstance.A = new(A)
		*copyInstance.A = *src.A
	}
	if src.B != nil {
		copyInstance.B = new(B)
		*copyInstance.B = *src.B
	}
	if src.As != nil {
		copyInstance.As = make([]*A, len(src.As))
		copy(copyInstance.As, src.As)
	}
	if src.Bs != nil {
		copyInstance.Bs = make([]*B, len(src.Bs))
		copy(copyInstance.Bs, src.Bs)
	}
	return &copyInstance
}