# 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
# 4. If filename is relative path. Related to annotated file.
// /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
# 5. If filepath is absolute. Related to annotated file module
root.
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.