webrpc-gen
uses Go text/template language, along with the webrpc schema AST (abtract-syntax-tree) to generate source code of the target's type system, client library and server handlers.
The Go templates are used in many popular projects including Hugo and Helm. Hugo has a nice introduction to Go templates.
- Developing a new generator
- Interoperability tests
- Template structure
- Template variables
- Template functions
webrpc-gen
can be invoked against templates located in a local directory:
webrpc-gen -schema=api.ridl -target=./local/directory
All webrpc generators are expected to implement reference TestApi schema and run client/server interoperability tests against the official webrpc-test binaries.
For more info, see typescript or golang tests.
webrpc-gen
expects at least one *.go.tmpl
file with the entrypoint template called "main"
.
{{- define "main" -}}
{{/* Your generator code */}}
{{- end -}}
{{- if ne .WebrpcVersion "v1" -}}
{{- stderrPrintf "%s generator error: unsupported webrpc protocol version %s\n" .WebrpcTarget .WebrpcVersion -}}
{{- exit 1 -}}
{{- end -}}
Require specific webrpc-gen
version to ensure the API of the template functions.
{{- if not (minVersion .WebrpcGenVersion "v0.7.0") -}}
{{- stderrPrintf "%s generator error: unsupported webrpc-gen version %s, please update\n" .WebrpcTarget .WebrpcGenVersion -}}
{{- exit 1 -}}
{{- end -}}
webrpc-gen -schema=proto.ridl -target=golang -h
{{- if exists .Opts "help" -}}
{{- template "help" $opts -}}
{{- exit 0 -}}
{{- end -}}
{{- $opts := dict -}}
{{- set $opts "pkg" (default .Opts.pkg "proto") -}}
{{- set $opts "client" (ternary (in .Opts.client "" "true") true false) -}}
{{- set $opts "server" (ternary (in .Opts.server "" "true") true false) -}}
{{- /* Print help on unsupported option. */ -}}
{{- range $k, $v := .Opts }}
{{- if not (exists $opts $k) -}}
{{- stderrPrintf "-%v=%q is not supported target option\n\nUsage:\n" $k $v -}}
{{- template "help" $opts -}}
{{- exit 1 -}}
{{- end -}}
{{- end -}}
{{- /* Type mapping. */ -}}
{{- $typeMap := dict }}
{{- set $typeMap "null" "null" -}}
{{- set $typeMap "any" "object" -}}
{{- set $typeMap "byte" "string" -}}
{{- set $typeMap "bool" "boolean" -}}
{{- set $typeMap "uint" "number" -}}
{{- set $typeMap "uint8" "number" -}}
{{- set $typeMap "uint16" "number" -}}
{{- set $typeMap "uint32" "number" -}}
{{- set $typeMap "uint64" "number" -}}
{{- set $typeMap "int" "number" -}}
{{- set $typeMap "int8" "number" -}}
{{- set $typeMap "int16" "number" -}}
{{- set $typeMap "int32" "number" -}}
{{- set $typeMap "int64" "number" -}}
{{- set $typeMap "float32" "number" -}}
{{- set $typeMap "float64" "number" -}}
{{- set $typeMap "string" "string" -}}
{{- set $typeMap "timestamp" "string" -}}
{{- set $typeMap "map" "object" -}}
{{- set $typeMap "[]" "array" -}}
Timestamps must be serialized in JSON to ECMA Script ISO 8601 format: YYYY-MM-DDTHH:mm:ss.sssZ
Call {{ get $typeMap .Type }}
to print your type.
Import a sub-template.
{{ template "sub-template" }}
Use dict
function to pass multiple variables into the sub-template:
{{ template "sub-template" dict "Type" .Type "TypeMap" $typeMap }}
Base webrpc types can be nested (ie. map<string,map<string,User>>
), so you will need to render them recursively.
{{- define "type" -}}
{{- $typeMap := .TypeMap -}}
{{- if isMapType .Type -}}
map[{{mapKeyType .Type}}]{{template "type" dict "Type" (mapValueType .Type) "TypeMap" $typeMap}}
{{- else if isArrayType .Type -}}
[]{{template "type" dict "Type" (arrayItemType .Type) "TypeMap" $typeMap}}
{{- else if isCoreType .Type -}}
{{ get $typeMap .Type }}
{{- else -}}
*{{.Type}}
{{- end -}}
{{- end -}}
Variable | Description | Example value |
---|---|---|
{{.WebrpcVersion}} |
webrpc protocol version | "v1" |
{{.WebrpcGenVersion}} |
webrpc-gen CLI version | "v0.7.0" |
{{.WebrpcGenCmd}} |
webrpc-gen command | "webrpc-gen ..." |
{{.WebrpcTarget}} |
webrpc-gen target | "github.com/webrpc/[email protected]" |
You can let users pass custom variables into your template by adding custom -options
to webrpc-gen
CLI.
webrpc-gen -option | Template variable |
---|---|
-name=HelloService |
{{.Opts.name}} |
-description="some value" |
{{.Opts.description}} |
-enableFeature |
{{.Opts.someFeature}} |
Example:
webrpc-gen -schema=proto.ridl -target=./custom-template -name=Hello -description="some value" -enableFeature
will pass {{.Opts.name}}
, {{.Opts.description}}
and {{.Opts.enableFeature}}
variables into your template.
Variable | Description | Example value |
---|---|---|
{{.SchemaName}} |
schema name | "example schema" |
{{.SchemaVersion}} |
schema version | "v0.0.1" |
{{.SchemaHash}} |
sha1 schema hash |
"483889fb084764e3a256" |
{{.WebrpcErrors}} |
webrpc errors | array of built-in errors |
{{.WebrpcErrors[0].Code}} |
unique error code | -4 (0 or negative number) |
{{.WebrpcErrors[0].Name}} |
unique error name | "WebrpcBadRequest" |
{{.WebrpcErrors[0].Message}} |
error description | "bad request" |
{{.WebrpcErrors[0].HTTPStatus}} |
HTTP response status code | 400 (number 400 -599 ) |
{{.Errors}} |
schema errors | array of schema errors |
{{.Errors[0].Code}} |
unique error code | 1001 (positive number) |
{{.Errors[0].Name}} |
unique error name | "RateLimited" |
{{.Errors[0].Message}} |
error description | "rate limited, slow down" |
{{.Errors[0].HTTPStatus}} |
HTTP response status code | 429 (number 100 -599 ) |
{{.Types}} |
types | array of types |
{{.Types[0].Name}} |
type name | "User" |
{{.Types[0].Type}} |
type | "struct" |
{{.Types[0].Fields}} |
type fields | array of fields |
{{.Types[0].Fields[0].Name}} |
field name | "ID" |
{{.Types[0].Fields[0].Type}} |
field type | "int" |
{{.Types[0].Fields[0].Optional}} |
field optional? | false |
{{.Types[0].Fields[0].Meta}} |
field metadata | array of {"key": "value"} |
{{.Services}} |
schema services | array of services |
{{.Services[0].Name}} |
service name | "ExampleService" |
{{.Services[0].Methods}} |
service methods | array of methods |
{{.Services[0].Methods[0].Inputs}} |
method inputs | array of method inputs |
{{.Services[0].Methods[0].Outputs}} |
method outputs | array of method outputs |
{{.Services[0].Methods[0].Inputs[0].Name}} |
method input name | "header" |
{{.Services[0].Methods[0].Inputs[0].Type}} |
method input type | "map<string,string>" |
{{.Services[0].Methods[0].Outputs[0].Name}} |
method output name | "user" |
{{.Services[0].Methods[0].Outputs[0].Type}} |
method output type | "User" |
See the example schema JSON file.
For example, you can iterate over the schema methods and print their names:
{{- range $_, $msg := .Services -}}
{{- range $_, $method := .Methods -}}
method {{.Name}}()
{{- end -}}
{{- end -}}
Function | Description |
---|---|
and EXPR |
Returns the boolean AND of its arguments by returning the first empty argument or the last argument. That is, "and x y" behaves as "if x then y else x." Evaluation proceeds through the arguments left to right and returns when the result is determined. |
call FUNC ARGS... |
Returns the result of calling the first argument, which must be a function, with the remaining arguments as parameters. Thus "call .X.Y 1 2" is, in Go notation, dot.X.Y(1, 2) where Y is a func-valued field, map entry, or the like. The first argument must be the result of an evaluation that yields a value of function type (as distinct from a predefined function such as print). The function must return either one or two result values, the second of which is of type error. If the arguments don't match the function or the returned error value is non-nil, execution stops. |
html STRING |
Returns the escaped HTML equivalent of the textual representation of its arguments. This function is unavailable in html/template, with a few exceptions. |
index ARRAY 1 |
Returns the result of indexing its first argument by the following arguments. Thus "index x 1 2 3" is, in Go syntax, x[1][2][3]. Each indexed item must be a map, slice, or array. |
slice ARRAY 1 2 |
slice returns the result of slicing its first argument by the remaining arguments. Thus "slice x 1 2" is, in Go syntax, x[1:2], while "slice x" is x[:], "slice x 1" is x[1:], and "slice x 1 2 3" is x[1:2:3]. The first argument must be a string, slice, or array. |
js STRING |
Returns the escaped JavaScript equivalent of the textual representation of its arguments. |
len ARRAY|MAP |
Returns the integer length of its argument. |
not EXPR |
Returns the boolean negation of its single argument. |
or EXPR |
Returns the boolean OR of its arguments by returning the first non-empty argument or the last argument, that is, "or x y" behaves as "if x then x else y". Evaluation proceeds through the arguments left to right and returns when the result is determined. |
print "format %v" ARGS... |
Print and format, see Go's fmt.Sprint() |
printf "format %v" ARGS... |
Print and format, see Go's fmt.Sprintf() |
println "format %v" ARGS... |
Print and format, see Go's fmt.Sprintln() |
urlquery STRING |
Returns the escaped value of the textual representation of its arguments in a form suitable for embedding in a URL query. This function is unavailable in html/template, with a few exceptions. |
See https://pkg.go.dev/text/template#hdr-Functions
You have access to all template functions in sprig v3 except for those overloaded below.
See https://masterminds.github.io/sprig/
You have access to all template functions in sprig v3 except for those overloaded below.
Template flow | Description | webrpc-gen |
---|---|---|
minVersion {{.WebrpcVersion}} v1.4 |
Returns boolean if the given major/minor semver is at least v1.4 |
v0.7.0 |
stderrPrint ARGS... |
print to webrpc-gen CLI stderr |
v0.13.0 |
stderrPrintf "format %v" ARGS... |
printf to webrpc-gen CLI stderr |
v0.7.0 |
exit INT |
Terminate template execution, useful for fatal errors | v0.7.0 |
dump VAR |
Dump variable | v0.13.0 |
hasField OBJ FIELD |
Check if object has a given field | v0.13.0 |
Schema type helpers | Description | webrpc-gen |
---|---|---|
isBasicType .Type |
Returns true if .Type is core type |
v0.7.0 (deprecated) |
isCoreType .Type |
Returns true if .Type is core type |
v0.9.0 |
isStructType .Type |
Returns true if .Type is struct |
v0.7.0 |
isEnumType .Type |
Returns true if .Type is enum |
v0.7.0 |
isMapType .Type |
Returns true if .Type is map (map<T1,T2> ) |
v0.7.0 |
isListType .Type |
Returns true if .Type is list ([]T ) |
v0.7.0 |
mapKeyType .MapType |
Returns map's key type (ie. T1 from map<T1,T2> ) |
v0.7.0 |
mapValueType .MapType |
Returns map's value type (ie. T2 from map<T1,T2> ) |
v0.7.0 |
listElemType .ListType |
Returns list's element type (ie. T from []T ) |
v0.7.0 |
Dictionary (map[string]any ) |
Description | webrpc-gen |
---|---|---|
dict [KEY VALUE]... |
Create a new dictionary (map[string]any ) |
v0.7.0 |
get $dict KEY |
Get value for the given KEY in dictionary | v0.7.0 |
set $dict KEY VALUE |
Set value for the given KEY in dictionary | v0.7.0 |
exists $dict KEY |
Returns true if the KEY exists in the given dictionary |
v0.7.0 |
String utils | Description | webrpc-gen |
---|---|---|
join ARRAY SEPARATOR |
Concatenate array into a string with separator between elements (see strings.Join()) | v0.7.0 |
split SEPARATOR STRING |
Split string by a separator into string array []string |
v0.7.0 |
hasPrefix STRING PREFIX |
Returns true if the given string starts with PREFIX |
v0.8.0 |
hasSuffix STRING SUFFIX |
Returns true if the given string ends with SUFFIX |
v0.8.0 |
trimPrefix STRING PREFIX |
Trim prefix from a given string | v0.8.0 |
trimSuffix STRING SUFFIX |
Trim suffix from a given string | v0.8.0 |
toLower STRING |
Converts input to "lower case" |
v0.7.0 |
toUpper STRING |
Converts input to "UPPER CASE" |
v0.7.0 |
firstLetterToLower STRING |
Converts first letter to lower case | v0.7.0 |
firstLetterToUpper STRING |
Converts first letter to UPPER CASE | v0.7.0 |
camelCase STRING |
Converts input to "camelCase" |
v0.7.0 |
pascalCase STRING |
Converts input to "PascalCase" |
v0.7.0 |
snakeCase STRING |
Converts input to "snake_case" |
v0.7.0 |
kebabCase STRING |
Converts input to "kebab-case" |
v0.7.0 |
Generic utils | Description | webrpc-gen |
---|---|---|
lastIndex ARRAY |
Return the index of the last element of the array | v0.18.0 |
array [ELEMENTS]... |
Create a new string array | v0.11.2 (string support v0.8.0) |
append ARRAY [ELEMENTS]... |
Append elements to existing string array | v0.11.2 (string support v0.8.0) |
first ARRAY |
Return first element from the given array | v0.11.2 (string support v0.7.0) |
last ARRAY |
Return last element from the given array | v0.11.2 (string support v0.7.0) |
sort ARRAY |
Return sorted copy of the given array (ascending order) | v0.8.0 |
coalesce VALUES... |
Returns first non-empty value | v0.7.0 |
default VALUE DEFAULT |
Returns DEFAULT value if given VALUE is empty |
v0.7.0 |
in FIRST VALUES... |
Returns true if any of the given VALUES match the first value |
v0.7.0 |
ternary BOOL FIRST SECOND |
Ternary if-else. Returns first value if true , second value if false |
v0.7.0 |