# Get Started

# Usage

# Install

go install github.com/go-zing/gozz@latest

# CLI

Gozz CLI is built with cobra (opens new window), command syntax as follows:

gozz [--GLOBAL-FLAGS] [COMMAND] [--COMMAND-FLAGS] [ARGS]

# Environment

Gozz would lookup .so plugins in user's home directory ~/.gozz/plugins/ when starts up, exception raised during these default plugins loading would be ignored.

Use env GOZZ_PLUGINS_DIR to specify the default plugins install directory.

Project Environment

Ensure your project is in go modules (opens new window) mode.

# Command

Gozz supports commands as follows:

# gozz list

This command would list all plugins registered in core and ready to use, and their arguments and description would print into your console. You can check whether plugins is correctly loads by core.

Usage:

gozz list

# gozz run

Usage:

gozz run [--plugin/-p] [filename] 

This command would start analysis to provided filename, and collect annotations context entities to specified ordered plugins for different jobs. It would be the most used command.

# Argument filename

If filename is a file. Parser would parse this file only.

If filename is a directory. Parser would parse walk around and parse all files, including sub-dir.

# Flag --plugin / -p "name:options..."

gozz run should specify with one or more plugins

# with single
gozz run --plugin "foo" ./
# in short hand
gozz run -p "foo" ./
# with multi
gozz run -p "foo" -p "bar" ./

When run with multi plugins, they would execute in specified order. Even if parser may do analysis before every plugin execute, Gozz provide a file-meta based version cache for analysis results, So that it may got better performance when running with multi plugins than one.

# Append Plugin Options

you could append default options after plugin name and seperated with :.

# append default option [ key:value key2:value2 ] for plugin foo.
gozz run -p "foo:key=value:key2=value2" ./ 

If annotation +zz:foo:key=value3 got default option key=value:key2=value2, treat as follows:

+zz:foo:key=value3(key exist,ignore):key2=value2(override from default)

# gozz install

Usage:

gozz install [--output/-o] [--filepath/-f] [repository] 

This command would try complies repository into .so plugin, then saves in user's plugins path ~/.gozz/plugins ( or environment GOZZ_PLUGINS_DIR).

# Argument repository

if repository starts with network protocol, such as ssh:// git:// http:// https://, it try downloads these url with git, otherwise use url as local filepath.

# Flag --output / -o

If it was specified, build results would output as provided filename, and do not install into plugins dir.

# Flag --filepath / -f

If it was specified, build plugins with this relative filepath from repository root.

Example:

gozz install https://github.com/go-zing/gozz-plugins -f ./ormdrivers/mysql -o orm-mysql.so

Gozz would download this remote repo and build it with command:

go build --buildmode=plugin -o orm-mysql.so ./ormdrivers/mysql

# Precondition for install plugins

  • Version must be match between gozz built from and environment.
  • Plugin Repo gozz-core must be adapted with gozz CLI.
  • Others factors effect compatibility of go/plugin. checkout: Understand Go Plugin (opens new window).

# Global Flags

# -x / --extension [filename]

Example:

# execute list command 
gozz -x plugin.so list
# execute run command
gozz -x plugin.so run -p "plugin" ./

Use this flag to load external .so plugin with filepath. Exception raised during these plugins load would print error and exit process.

# Annotation Syntax

Annotations are comments that stick with object, and match syntax as follows:

// +zz:[PLUGIN][:ARGS][:OPTIONS]
type T interface{}

# PLUGIN - plugin name

Plugin has unique name when registered. This name would also uses for matching annotation. Case-insensitive.

Example: plugin Foo match annotations with prefix +zz:foo.

# ARGS - Exact Arguments

Plugins would specify exact arguments count. These arguments should append after annotation prefix and seperated with :. If an annotation match plugin prefix but does not have enough arguments. It would be ignored.

Example:

plugin foo accepts two exact arguments, +zz:foo:arg1:arg2 would be accepted.

but rejects +zz:foo:arg1 and +zz:foo.

# Why Ignore

If an argument could have default value, it should not be a exact argument.

Checkout Design Concept.

# OPTIONS - Optional Arguments

Values after exact arguments would be parsed as Key-Value pairs option, and passed to plugin.

Example:

Plugin foo Has 2 exact arguments. When parsing annotation as follows:

+zz:foo:arg1:arg2:arg3=value:arg4

Options {"arg3":"value","arg4":""} would present.

# Repeatable Optional Arguments

Sometimes optional arguments accept slice value, they were often seperated with ,. Example:

// +zz:foo:set=a,b,c,d
type T interface{}

When these options were seperated in different key-value pairs. Parser would merge them. Example:

// +zz:foo:set=a:set=:set=b:set=c,d
type T interface{}

is equal to

// +zz:foo:set=a,,b,c,d
type T interface{}

# Boolean Option

Sometimes, plugin would check whether a key exists in options. These were called Boolean Option.

If a Boolean Option key's value is not empty. In additionally, Parser would also check this value whether is implicit negative, like 0 / false / null.

Example:

  • +zz:bar:arg3=value:arg4 => {"arg3":"value","arg4":""}
  • +zz:bar:arg3=value:arg4=true => {"arg3":"value","arg4":"true"}
  • +zz:bar:arg3=value:arg4=false => {"arg3":"value"}
  • +zz:bar:arg3=value:arg4=0 => {"arg3":"value"}

# Argument Escape

Some arguments may contain :, use \ to escape, this solution works for command line external arguments and annotations arguments.

Example:

+zz:plugin:addr=localhost:8080 -> +zz:plugin:addr=localhost\:8080

Escape principle

Parser replace \: with \u003A before annotation separating, and replace \u003A with : as last.

# Declaration Object

Annotation could be added on Decl blocks, and also Spec Object.

You can check out Principle to know about what is Decl and Spec.

package t

// +zz:foo
type (
	T0 interface{}
	T1 interface{}

	// +zz:bar
	T2 interface{}
)

// +zz:foo
type T3 interface{}

Annotations added on Decl, would be copied for all Spec objects in Decl blocks as follows:

package t

type (
	// +zz:foo
	T0 interface{}
	// +zz:foo
	T1 interface{}

	// +zz:foo
	// +zz:bar
	T2 interface{}
)

// +zz:foo
type T3 interface{}

# Multiline Annotation

# Annotation Detect

Lines starts with +zz: (ignore whitespace) would be detected as annotations.

/*
+zz:foo  <- annotation
  +zz:foo  <- annotation
 x +zoo:foo <- not
//   +zoo:foo  <- not
*/
type T1 interface{}

// +zz:foo  <- annotation
//   +zoo:  <- annotation
//   +zoo-  <- not
// x +zoo:foo <- not
type T2 interface{}

# Repeat Annotations

Object could have multi annotations added, and these annotations may contains repeat plugins. It would not be considered as any exception.

// +zz:foo
// +zz:bar
// +zz:bar:filename=./bar
type T interface{}

How to handle repeat annotations is based on plugin implement and usage.

# Conventions

# about filepath and filename

plugins may have arguments for filename as follows:

// +zz:api:./
// +zz:impl:./impl.go
// +zz:wire:inject=/
type API interface{}

These filepath should follow these rules:

Example based on directory tree below:

/go/src/project/
├── go.mod
└── types
    └── api.go

# 1. If ends with .go, use it to generate go file.

// /go/src/project/types/api.go

// +zz:api:./api.go
type API interface{}

This example would get filename api.go.

# 2. If not ends with .go, plugin would provide a default filename

Most of the plugin default filename formatted as zzgen.${plugin}.go.

// /go/src/project/types/api.go

// +zz:api:./
type API interface{}

This example would get filename zzgen.api.go

# 3. Most of the filename arguments support templating

You could use meta info from annotated object like Name Package to do templating.

String functions provided (opens new window) in core like snake / camel ...

// /go/src/project/types/api.go

// +zz:api:./{{ lower .Name }}.go
type Foo interface{}

This example would get filename foo.go

// /go/src/project/types/api.go

// +zz:api:./apix.go
type API interface{}

This example would get full filepath /go/src/project/types/apix.go


// /go/src/project/types/api.go

// +zz:api:./apix
type API interface{}

This example would get filename zzgen.api.go because it does not have .go suffix.

And got full filepath /go/src/project/types/apix/zzgen.api.go

module root is detected by command go env GOMOD

// /go/src/project/types/api.go

// +zz:api:/apix.go
type API interface{}

This example would get filepath /go/src/project/apix.go.


// /go/src/project/types/api.go

// +zz:api:/apix
type API interface{}

This example would get filepath /go/src/project/apix/zzgen.api.go


If directory types contains a sub module:

/go/src/project/
├── go.mod
└── types
    ├── api.go
    └── go.mod
// /go/src/project/types/api.go

// +zz:api:/
type API interface{}

This example would get filepath /go/src/project/types/zzgen.api.go

# Custom Template

For most of the code generation, we would check whether exist file named ${filename}.impl in directory.

If it exists, it would be used as generate template. Else, a builtin template with this name would be generated.

TIP

To reset template, please delete existed .tmpl file and regenerate builtin template.

Last Updated: 10/27/2023, 12:58:51 PM