Patterns

Enum stringer

Use the embedded template when you need the conventional implementation:

package status

//mgo:gen std.stringer trimprefix=Status
type Status int

const (
    StatusPending Status = iota
    StatusRunning
    StatusDone
)

Running metago . generates String() string. For unknown values it returns Status(value).

A custom template gives you complete control:

{{ define "short-status" }}
func (v {{ name . }}) String() string {
    switch v {
    {{- range .Values }}
    case {{ .Name }}:
        return {{ quote (trimPrefix .Name "Status") }}
    {{- end }}
    default:
        return "unknown"
    }
}
{{ end }}
//mgo:gen short-status
type Status int

Interface mock

package users

//mgo:gen std.mock
type Store interface {
    Find(id string) (User, error)
    Save(user User) error
}

Metago generates a MockStore with one function field per method:

func TestService(t *testing.T) {
    store := MockStore{
        FindFunc: func(id string) (User, error) {
            return User{ID: id}, nil
        },
        SaveFunc: func(user User) error {
            return nil
        },
    }

    service := NewService(store)
    // ...
}

Use named, non-variadic interface parameters. Embedded interface methods are not expanded by the standard mock template.

Reflection-free JSON

Generate the runtime once:

// Package jsonruntime contains generated JSON support.
//
//mgo:gen std.serde.jsonruntime
package jsonruntime

Set the runtime import for every codec in metago.toml:

[templates."std.serde".args]
runtime = "example.com/project/internal/jsonruntime"
strict = "true"
maxinput = "1048576"

Derive codecs on models:

package users

//mgo:gen std.serde
type User struct {
    ID      int64    `json:"id"`
    Name    string   `json:"name"`
    Tags    []string `json:"tags,omitempty"`
    Address Address  `json:"address"`
}

//mgo:gen std.serde
type Address struct {
    City string `json:"city"`
}

The generated types implement json.Marshaler and json.Unmarshaler. Unsupported field shapes fall back to encoding/json for that field.

Validation properties

Properties attach generator-specific metadata to a declaration:

//mgo:gen validate
type Signup struct {
    Email string `json:"email"` //mgo:validate required format=email
    Name  string `json:"name"`  //mgo:validate required max=80
    Age   int    `json:"age"`   //mgo:validate min=18
}
{{ define "validate" }}
func (v {{ name . }}) Validate() error {
    {{- range .Fields }}
    {{- if propHas . "validate" "required" }}
    if v.{{ .Name }} == {{ zero . }} {
        return fmt.Errorf({{ quote (printf "%s is required" (tagName . "json")) }})
    }
    {{- end }}
    {{- end }}
    return nil
}
{{ imports "fmt" }}
{{ end }}

The imports call can appear after the generated code; it records the import and emits no text.

Package registry

One aggregate invocation can inspect marker directives across a package:

//mgo:gen route-table
package api

//mgo:gen route GET /users
func ListUsers() {}

//mgo:gen route POST /users
func CreateUser() {}
{{ define "route" }}{{ end }}

{{ define "route-table" }}
type Route struct {
    Method string
    Path   string
}

var Routes = []Route{
{{- range .Package.Metas }}
{{- if eq .Template "route" }}
    {Method: {{ quote (index .Argv 0) }}, Path: {{ quote (index .Argv 1) }}},
{{- end }}
{{- end }}
}
{{ end }}

route produces no code, but each //mgo:gen route ... directive still appears in .Package.Metas. The route-table template reads those directives and generates one route table for the package. Entries in .Package.Metas are ordered by file and line.

Inline generated code

Inline output is useful when generated declarations are easier to understand beside their source:

//mgo:inline constructor
type Client struct {
    BaseURL string
    Token   string
}
{{ define "constructor" }}
func New{{ name . }}(
{{- range .Fields }}
    {{ unexported .Name }} {{ typeof . }},
{{- end }}
) *{{ name . }} {
    return &{{ name . }}{
    {{- range .Fields }}
        {{ .Name }}: {{ unexported .Name }},
    {{- end }}
    }
}
{{ end }}

Metago inserts the constructor after Client and closes the managed region with //mgo:end. Later runs replace only that region.

Test-only generation

Directives in test files stay in test builds:

package service_test

// Clock provides time in tests.
//mgo:gen std.mock
type Clock interface {
    Now() time.Time
}

For an external service_test package, Metago writes meta_service_test.go. Internal test directives write meta_test.go. Production meta.go remains unaffected.

For exact supported targets, helper signatures, and standard-template options, use the reference.