19 Commits

Author SHA1 Message Date
jhayotte
5a8293c573 feat: enhance the function of typing field with their package 2018-08-08 16:38:58 +02:00
Manfred Touron
6abbdf4cfb Merge pull request #96 from moul/helpers/gfanton/add-store-helper
Add Concat/Join/Store helpers
2018-04-07 19:42:21 +02:00
Guilhem Fanton
adb58bd940 Add concat & Join helper methods 2018-04-06 17:18:38 +02:00
Julien Hayotte
283713bd93 Merge pull request #95 from Tommy-42/master
Add arithmetics helpers - fix #94
2018-03-21 17:29:47 +01:00
Tommy PAGEARD
19fb3c8da9 Add arithmetics helpers - fix #94 2018-03-21 15:03:26 +01:00
Manfred Touron
77a820ea6f Merge pull request #93 from sumup/feature/stringMethodOptionsExtension
Add stringMethodOptionsExtension
2018-02-27 11:02:53 +01:00
Jan Weitz
6765ff04eb Add stringMethodOptionsExtension
This helper function can be used on service methods
to read options, which extend method options by a string.

e.g.

It is possible to read "Some string" for a custom

`my_method_option.bar` message option extension.

See:

https://developers.google.com/protocol-buffers/docs/proto#customoptions

on how to define a custom extension and referencing is by a fieldID.

```
service MyService {

  rpc MyMethod(RequestType) returns(ResponseType) {
    option (my_method_option.bar) = "Some string";
  }
}
```

If `my_method_option.bar` was defined for `fieldID: 50000`
one can reference it using protoc-gen-template like:

```
{{- stringMethodOptionsExtension 50000 .method }}
```
2018-02-23 16:55:45 +01:00
Alexandre Beslic
bb3105a862 Merge pull request #91 from tkerambloch/dev/tkerambloch/getHttpPathInAdditionalBindings
Helper: add helper like httpPath for get path in additional bindings options
2018-02-13 17:34:22 +01:00
Thomas KERAMBLOCH
0c29a9922f add helper like httpPath for get path in additional bindings options 2018-02-13 16:00:04 +01:00
Alexandre Beslic
aff21c1e7c Merge pull request #90 from tkerambloch/dev/tkerambloch/helperGetIndexArray
fix type int32 in index helper
2018-02-12 17:03:31 +01:00
Thomas KERAMBLOCH
15ac366c8d fix type int32 in index helper 2018-02-12 16:48:36 +01:00
Alexandre Beslic
5a9555bcf5 Merge pull request #89 from tkerambloch/dev/tkerambloch/helperGetIndexArray
Add Helper 'index': get elem of array with the index
2018-02-10 18:43:45 +01:00
Thomas KERAMBLOCH
3f1d5001d9 Add Helper 'index': get elem of array with the index 2018-02-10 18:00:39 +01:00
Manfred Touron
be5f14041f Merge pull request #83 from pmoroney/new-helpers
add new helpers
2018-01-12 23:25:50 +01:00
Pat Moroney
100279cd16 add comment about tree traversal 2018-01-12 11:36:29 -07:00
Pat Moroney
4f138c4f92 remove duplicated line 2018-01-12 11:23:17 -07:00
Pat Moroney
78cb5588b1 make nilString constant 2018-01-12 11:12:15 -07:00
Pat Moroney
0b600dd93f rename fieldId to fieldID 2018-01-12 10:56:25 -07:00
Pat Moroney
decb64ccd8 add new helpers 2018-01-12 10:38:10 -07:00
15 changed files with 700 additions and 53 deletions

View File

@@ -20,6 +20,7 @@ test: install
cd examples/go-generate && make cd examples/go-generate && make
cd examples/single-package-mode && make cd examples/single-package-mode && make
cd examples/helpers && make cd examples/helpers && make
cd examples/arithmetics && make
# cd examples/go-kit && make # cd examples/go-kit && make
.PHONY: docker.build .PHONY: docker.build

View File

@@ -73,34 +73,59 @@ See [examples](./examples).
This project uses [Masterminds/sprig](https://github.com/Masterminds/sprig) library and additional functions to extend the builtin [text/template](https://golang.org/pkg/text/template) helpers. This project uses [Masterminds/sprig](https://github.com/Masterminds/sprig) library and additional functions to extend the builtin [text/template](https://golang.org/pkg/text/template) helpers.
Non-exhaustive list of new helpers:s Non-exhaustive list of new helpers:
* **all the functions from [sprig](https://github.com/Masterminds/sprig)** * **all the functions from [sprig](https://github.com/Masterminds/sprig)**
* `string`
* `json` * `json`
* `prettyjson` * `prettyjson`
* `splitArray`
* `first` * `first`
* `last` * `last`
* `splitArray`
* `upperFirst` * `upperFirst`
* `lowerFirst` * `lowerFirst`
* `camelCase` * `camelCase`
* `lowerCamelCase` * `lowerCamelCase`
* `kebabCase` * `kebabCase`
* `contains`
* `trimstr`
* `index`
* `snakeCase` * `snakeCase`
* `getProtoFile` * `getProtoFile`
* `getMessageType` * `getMessageType`
* `getEnumValue` * `getEnumValue`
* `isFieldMessage` * `isFieldMessage`
* `isFieldMessageTimeStamp`
* `isFieldRepeated` * `isFieldRepeated`
* `haskellType`
* `goType` * `goType`
* `goZeroValue`
* `goTypeWithPackage` * `goTypeWithPackage`
* `jsType` * `jsType`
* `jsSuffixReserved` * `jsSuffixReserved`
* `namespacedFlowType` * `namespacedFlowType`
* `httpVerb` * `httpVerb`
* `httpPath` * `httpPath`
* `httpPathsAdditionalBindings`
* `httpBody`
* `shortType` * `shortType`
* `urlHasVarsFromMessage` * `urlHasVarsFromMessage`
* `lowerGoNormalize`
* `goNormalize`
* `leadingComment`
* `trailingComment`
* `leadingDetachedComments`
* `stringFieldExtension`
* `stringMethodOptionsExtension`
* `boolFieldExtension`
* `isFieldMap`
* `fieldMapKeyType`
* `fieldMapValueType`
* `replaceDict`
* `add`
* `subtract`
* `multiply`
* `divide`
See the project helpers for the complete list. See the project helpers for the complete list.

View File

@@ -52,6 +52,7 @@ func NewGenericServiceTemplateBasedEncoder(templateDir string, service *descript
if debug { if debug {
log.Printf("new encoder: file=%q service=%q template-dir=%q", file.GetName(), service.GetName(), templateDir) log.Printf("new encoder: file=%q service=%q template-dir=%q", file.GetName(), service.GetName(), templateDir)
} }
pgghelpers.InitPathMap(file)
return return
} }
@@ -68,6 +69,7 @@ func NewGenericTemplateBasedEncoder(templateDir string, file *descriptor.FileDes
if debug { if debug {
log.Printf("new encoder: file=%q template-dir=%q", file.GetName(), templateDir) log.Printf("new encoder: file=%q template-dir=%q", file.GetName(), templateDir)
} }
pgghelpers.InitPathMap(file)
return return
} }

View File

@@ -0,0 +1,13 @@
.PHONY: build
build:
mkdir -p output
protoc -I. --gotemplate_out=template_dir=templates,debug=true,all=true:output proto/*.proto
.PHONY: re
re: clean build
.PHONY: clean
clean:
rm -rf output

View File

@@ -0,0 +1,5 @@
add(1,2) = 3
subtract(1,2) = -1
multiply(1,2) = 2
divide(2,1) = 2

View File

@@ -0,0 +1,2 @@
syntax = "proto3";
package Arithmetics;

View File

@@ -0,0 +1,6 @@
{{with $a := 1}}{{with $b := 2}}
add(1,2) = {{add $a $b}}
subtract(1,2) = {{subtract $a $b}}
multiply(1,2) = {{multiply $a $b}}
divide(2,1) = {{divide $b $a}}
{{end}}{{end}}

View File

@@ -25,7 +25,7 @@
{{abbrev 5 "hello world"}}: he... {{abbrev 5 "hello world"}}: he...
{{abbrevboth 5 10 "1234 5678 9123"}}: ...5678... {{abbrevboth 5 10 "1234 5678 9123"}}: ...5678...
{{initials "First Try"}}: FT {{initials "First Try"}}: FT
{{randNumeric 3}}: 146 {{randNumeric 3}}: 528
{{- /*{{wrap 80 $someText}}*/}}: {{- /*{{wrap 80 $someText}}*/}}:
{{wrapWith 5 "\t" "Hello World"}}: Hello World {{wrapWith 5 "\t" "Hello World"}}: Hello World
{{contains "cat" "catch"}}: true {{contains "cat" "catch"}}: true
@@ -45,6 +45,9 @@
{{regexReplaceAllLiteral "a(x*)b" "-ab-axxb-" "${1}"}}: -${1}-${1}- {{regexReplaceAllLiteral "a(x*)b" "-ab-axxb-" "${1}"}}: -${1}-${1}-
{{regexSplit "z+" "pizza" -1}}: [pi a] {{regexSplit "z+" "pizza" -1}}: [pi a]
# Get one specific method on array method using index
{{ index .Service.Method 1 }}: name:"Iii" input_type:".dummy.Dummy2" output_type:".dummy.Dummy1" options:<>
# Sprig: advanced # Sprig: advanced
{{if contains "cat" "catch"}}yes{{else}}no{{end}}: yes {{if contains "cat" "catch"}}yes{{else}}no{{end}}: yes
{{1 | plural "one anchovy" "many anchovies"}}: one anchovy {{1 | plural "one anchovy" "many anchovies"}}: one anchovy

View File

@@ -46,6 +46,9 @@
{{`{{regexReplaceAllLiteral "a(x*)b" "-ab-axxb-" "${1}"}}`}}: {{regexReplaceAllLiteral "a(x*)b" "-ab-axxb-" "${1}"}} {{`{{regexReplaceAllLiteral "a(x*)b" "-ab-axxb-" "${1}"}}`}}: {{regexReplaceAllLiteral "a(x*)b" "-ab-axxb-" "${1}"}}
{{`{{regexSplit "z+" "pizza" -1}}`}}: {{regexSplit "z+" "pizza" -1}} {{`{{regexSplit "z+" "pizza" -1}}`}}: {{regexSplit "z+" "pizza" -1}}
# Get one specific method on array method using index
{{`{{ index .Service.Method 1 }}`}}: {{ index .Service.Method 1 }}
# Sprig: advanced # Sprig: advanced
{{`{{if contains "cat" "catch"}}yes{{else}}no{{end}}`}}: {{if contains "cat" "catch"}}yes{{else}}no{{end}} {{`{{if contains "cat" "catch"}}yes{{else}}no{{end}}`}}: {{if contains "cat" "catch"}}yes{{else}}no{{end}}
{{`{{1 | plural "one anchovy" "many anchovies"}}`}}: {{1 | plural "one anchovy" "many anchovies"}} {{`{{1 | plural "one anchovy" "many anchovies"}}`}}: {{1 | plural "one anchovy" "many anchovies"}}

View File

@@ -1,6 +1,5 @@
// Code generated by protoc-gen-go. // Code generated by protoc-gen-go. DO NOT EDIT.
// source: proto/article.proto // source: proto/article.proto
// DO NOT EDIT!
/* /*
Package article is a generated protocol buffer package. Package article is a generated protocol buffer package.
@@ -49,6 +48,8 @@ func (m *GetArticleRequest) GetGetarticle() *common.GetArticle {
type GetArticleResponse struct { type GetArticleResponse struct {
Article *Article `protobuf:"bytes,1,opt,name=article" json:"article,omitempty"` Article *Article `protobuf:"bytes,1,opt,name=article" json:"article,omitempty"`
// The generated output should write []*GetArticleResponse_Storage.Storage for this field.
Storages []*GetArticleResponse_Storage `protobuf:"bytes,2,rep,name=storages" json:"storages,omitempty"`
} }
func (m *GetArticleResponse) Reset() { *m = GetArticleResponse{} } func (m *GetArticleResponse) Reset() { *m = GetArticleResponse{} }
@@ -63,6 +64,29 @@ func (m *GetArticleResponse) GetArticle() *Article {
return nil return nil
} }
func (m *GetArticleResponse) GetStorages() []*GetArticleResponse_Storage {
if m != nil {
return m.Storages
}
return nil
}
type GetArticleResponse_Storage struct {
Code string `protobuf:"bytes,1,opt,name=code" json:"code,omitempty"`
}
func (m *GetArticleResponse_Storage) Reset() { *m = GetArticleResponse_Storage{} }
func (m *GetArticleResponse_Storage) String() string { return proto.CompactTextString(m) }
func (*GetArticleResponse_Storage) ProtoMessage() {}
func (*GetArticleResponse_Storage) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1, 0} }
func (m *GetArticleResponse_Storage) GetCode() string {
if m != nil {
return m.Code
}
return ""
}
type Article struct { type Article struct {
Id string `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"` Id string `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"`
Name string `protobuf:"bytes,2,opt,name=name" json:"name,omitempty"` Name string `protobuf:"bytes,2,opt,name=name" json:"name,omitempty"`
@@ -88,26 +112,30 @@ func (m *Article) GetName() string {
} }
func init() { func init() {
proto.RegisterType((*GetArticleRequest)(nil), "article.GetArticleRequest") proto.RegisterType((*GetArticleRequest)(nil), "company.GetArticleRequest")
proto.RegisterType((*GetArticleResponse)(nil), "article.GetArticleResponse") proto.RegisterType((*GetArticleResponse)(nil), "company.GetArticleResponse")
proto.RegisterType((*Article)(nil), "article.Article") proto.RegisterType((*GetArticleResponse_Storage)(nil), "company.GetArticleResponse.Storage")
proto.RegisterType((*Article)(nil), "company.Article")
} }
func init() { proto.RegisterFile("proto/article.proto", fileDescriptor0) } func init() { proto.RegisterFile("proto/article.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{ var fileDescriptor0 = []byte{
// 208 bytes of a gzipped FileDescriptorProto // 256 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x2e, 0x28, 0xca, 0x2f, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x91, 0x41, 0x4b, 0xc4, 0x30,
0xc9, 0xd7, 0x4f, 0x2c, 0x2a, 0xc9, 0x4c, 0xce, 0x49, 0xd5, 0x03, 0xf3, 0x84, 0xd8, 0xa1, 0x5c, 0x10, 0x85, 0x6d, 0x15, 0xab, 0xb3, 0x20, 0x3a, 0x82, 0x94, 0x8a, 0xb0, 0xd4, 0xcb, 0x22, 0x18,
0x29, 0x21, 0x88, 0x6c, 0x72, 0x7e, 0x6e, 0x6e, 0x7e, 0x1e, 0x44, 0x52, 0xc9, 0x9d, 0x4b, 0xd0, 0xa1, 0x1e, 0x3d, 0x88, 0x5e, 0x7a, 0xaf, 0x78, 0xf1, 0x56, 0xd3, 0x61, 0x29, 0x6c, 0x3a, 0xb5,
0x3d, 0xb5, 0xc4, 0x11, 0xa2, 0x22, 0x28, 0xb5, 0xb0, 0x34, 0xb5, 0xb8, 0x44, 0xc8, 0x88, 0x8b, 0x89, 0x82, 0xff, 0xc6, 0x9f, 0x2a, 0x9b, 0x4c, 0xd7, 0x8a, 0xb2, 0xa7, 0x4e, 0xf3, 0xbe, 0xf7,
0x2b, 0x3d, 0xb5, 0x04, 0xaa, 0x4d, 0x82, 0x51, 0x81, 0x51, 0x83, 0xdb, 0x48, 0x48, 0x0f, 0xaa, 0xf2, 0xc8, 0xc0, 0x69, 0x3f, 0xb0, 0xe3, 0x9b, 0x7a, 0x70, 0xad, 0x5e, 0x91, 0xf2, 0x7f, 0x98,
0x0f, 0x49, 0x39, 0x92, 0x2a, 0x25, 0x07, 0x2e, 0x21, 0x64, 0x83, 0x8a, 0x0b, 0xf2, 0xf3, 0x8a, 0x68, 0x36, 0x7d, 0xdd, 0x7d, 0x66, 0x18, 0x54, 0xcd, 0xc6, 0x70, 0x17, 0xc4, 0xbc, 0x84, 0x93,
0x53, 0x85, 0xb4, 0xb8, 0xd8, 0x51, 0x8d, 0x11, 0xd0, 0x83, 0x39, 0x0e, 0xa6, 0x14, 0xa6, 0x40, 0x92, 0xdc, 0x43, 0x30, 0x54, 0xf4, 0xf6, 0x4e, 0xd6, 0x61, 0x01, 0xb0, 0x24, 0x27, 0x29, 0x69,
0x49, 0x97, 0x8b, 0x1d, 0x2a, 0x26, 0xc4, 0xc7, 0xc5, 0x94, 0x99, 0x02, 0xd6, 0xc1, 0x19, 0xc4, 0x34, 0x8f, 0x16, 0xb3, 0x02, 0x95, 0xf8, 0x26, 0xf8, 0x84, 0xca, 0xbf, 0x22, 0xc0, 0x69, 0x92,
0x94, 0x99, 0x22, 0x24, 0xc4, 0xc5, 0x92, 0x97, 0x98, 0x9b, 0x2a, 0xc1, 0x04, 0x16, 0x01, 0xb3, 0xed, 0xb9, 0xb3, 0x84, 0x57, 0x90, 0xfc, 0xce, 0x39, 0x56, 0x52, 0x47, 0x8d, 0xe8, 0x08, 0xe0,
0x8d, 0x42, 0xb9, 0xb8, 0xa0, 0x3a, 0x8b, 0xcb, 0x92, 0x85, 0xdc, 0xb9, 0xb8, 0x10, 0xd6, 0x0b, 0x3d, 0x1c, 0x58, 0xc7, 0x43, 0xbd, 0x24, 0x9b, 0xc6, 0xf3, 0xdd, 0xc5, 0xac, 0xb8, 0xdc, 0xc0,
0x49, 0xc1, 0x6d, 0xc1, 0xf0, 0x9c, 0x94, 0x34, 0x56, 0x39, 0x88, 0x7b, 0x95, 0x18, 0x9c, 0x24, 0x7f, 0xa3, 0xd5, 0x53, 0x60, 0xab, 0x8d, 0x29, 0xbb, 0x80, 0x44, 0x0e, 0x11, 0x61, 0x4f, 0x73,
0xa2, 0xc4, 0x72, 0xf3, 0x53, 0x52, 0x73, 0x8a, 0x61, 0xa1, 0x68, 0x0d, 0xa5, 0x93, 0xd8, 0xc0, 0x13, 0x2e, 0x3d, 0xac, 0xfc, 0x9c, 0x5f, 0x43, 0x22, 0x19, 0x78, 0x04, 0x71, 0xdb, 0x88, 0x18,
0x21, 0x66, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0x16, 0x18, 0x87, 0xc4, 0x65, 0x01, 0x00, 0x00, 0xb7, 0xcd, 0x1a, 0xef, 0x6a, 0x43, 0x69, 0x1c, 0xf0, 0xf5, 0x5c, 0x3c, 0x03, 0x48, 0x33, 0xfb,
0xa1, 0xb1, 0x04, 0xf8, 0xe9, 0x80, 0xd9, 0xbf, 0xc5, 0xfc, 0xeb, 0x65, 0xe7, 0x5b, 0x4a, 0xe7,
0x3b, 0x8f, 0xe9, 0xcb, 0x99, 0xe1, 0x86, 0x56, 0x76, 0x5c, 0xd3, 0x9d, 0x7c, 0x5f, 0xf7, 0xfd,
0x4a, 0x6e, 0xbf, 0x03, 0x00, 0x00, 0xff, 0xff, 0x93, 0xb5, 0x4a, 0x95, 0xc6, 0x01, 0x00, 0x00,
} }

View File

@@ -1,6 +1,5 @@
// Code generated by protoc-gen-go. // Code generated by protoc-gen-go. DO NOT EDIT.
// source: proto/common.proto // source: proto/common.proto
// DO NOT EDIT!
/* /*
Package common is a generated protocol buffer package. Package common is a generated protocol buffer package.

View File

@@ -1,5 +1,5 @@
// Code generated by protoc-gen-gotemplate // Code generated by protoc-gen-gotemplate
package article package company
import ( import (
"github.com/moul/protoc-gen-gotemplate/examples/import/output/models/article" "github.com/moul/protoc-gen-gotemplate/examples/import/output/models/article"
@@ -7,5 +7,34 @@ import (
) )
type Repository interface { type Repository interface {
GetArticle(getarticle *common.GetArticle ) (*article.Article, error) GetArticle(getarticle *common.GetArticle ) (*company.Article, []*company.Storage, error)
} }
// ------------------------- Public SDK -----------------------------
// GetArticle : proto: missing extension proto: missing extension
func (sdk *Sdk) GetArticle(ctx context.Context,
getarticle *GetArticle.GetArticle, token, requestID string)(article *Article.Article, storages []*GetArticleResponse_Storage.Storage, err error) {
out := &pb.GetArticleResponse{}
_ = out
return out.Article, out.Storages, nil
}

View File

@@ -1,13 +1,25 @@
syntax = "proto3"; syntax = "proto3";
package article; package company;
option go_package = "models/article;article"; option go_package = "models/article;article";
import "proto/common.proto"; import "proto/common.proto";
message GetArticleRequest { common.GetArticle getarticle = 1;} message GetArticleRequest {
message GetArticleResponse { Article article = 1;} common.GetArticle getarticle = 1;
}
message GetArticleResponse {
Article article = 1;
message Storage {
string code = 1;
}
// The generated output should write []*GetArticleResponse_Storage.Storage for this field.
repeated Storage storages = 2;
}
message Article{ message Article{
string id = 1; string id = 1;

View File

@@ -1,4 +1,5 @@
// Code generated by protoc-gen-gotemplate // Code generated by protoc-gen-gotemplate
{{- $file := .File}}
package {{.File.Package}} package {{.File.Package}}
import ( import (
@@ -8,4 +9,33 @@ import (
type Repository interface { type Repository interface {
{{range $m := .Service.Method}}{{with $t := $m.InputType | getMessageType $.File}} {{$m.Name}}({{range $f := $t.Field}}{{$f.Name|lowerCamelCase}} {{$f| goTypeWithPackage }} {{end}}) ({{with $out := $m.OutputType | getMessageType $.File}}{{range $f := $out.Field}}{{$f | goTypeWithPackage}}, {{end}}{{end}} error){{end}}{{end}} {{range $m := .Service.Method}}{{with $t := $m.InputType | getMessageType $.File}} {{$m.Name}}({{range $f := $t.Field}}{{$f.Name|lowerCamelCase}} {{$f| goTypeWithPackage }} {{end}}) ({{with $out := $m.OutputType | getMessageType $.File}}{{range $f := $out.Field}}{{$f | goTypeWithPackage}}, {{end}}{{end}} error){{end}}{{end}}
} }
// ------------------------- Public SDK -----------------------------
{{$pkg := "pb"}}
{{range $m := .Service.Method}}
{{with $t := $m.InputType | getMessageType $.File}}
{{if and (not $m.ServerStreaming) (not $m.ClientStreaming)}}
{{/* ----------------------------- nominal case ---------------------------- */}}
// {{$m.Name}} : {{$m | httpVerb}} {{$m | httpPath}}
func (sdk *Sdk) {{$m.Name}}(ctx context.Context, {{if $t.OneofDecl}} req *{{$pkg}}.{{$m.Name}}Request,{{else}}{{range $f := $t.Field}}
{{$f.Name|lowerCamelCase}} {{$f| goTypeWithPackageV2 $.File}},{{end}}{{end}} token, requestID string)({{with $out := $m.OutputType | getMessageType $.File}}{{range $f := $out.Field}}{{$f.Name|lowerCamelCase}} {{$f | goTypeWithPackageV2 $.File}}, {{end}}{{end}}err error) {
out := &{{$pkg}}.{{$m.Name}}Response{}
_ = out
{{with $out := $m.OutputType | getMessageType $.File}}
return {{range $f := $out.Field}}out.{{$f.Name|camelCase}}, {{end}}nil
{{end}} {{/* with */}}
}
{{end}} {{/* streaming ifs */}}
{{end}}{{end}} {{/* range with */}}

View File

@@ -3,6 +3,7 @@ package pgghelpers
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"reflect"
"regexp" "regexp"
"strings" "strings"
"text/template" "text/template"
@@ -21,6 +22,9 @@ var (
registry *ggdescriptor.Registry // some helpers need access to registry registry *ggdescriptor.Registry // some helpers need access to registry
) )
// Utility to store some vars across multiple scope
var store = make(map[string]interface{})
func SetRegistry(reg *ggdescriptor.Registry) { func SetRegistry(reg *ggdescriptor.Registry) {
registry = reg registry = reg
} }
@@ -61,6 +65,12 @@ var ProtoHelpersFuncMap = template.FuncMap{
"last": func(a []string) string { "last": func(a []string) string {
return a[len(a)-1] return a[len(a)-1]
}, },
"concat": func(a string, b ...string) string {
return strings.Join(append([]string{a}, b...), "")
},
"join": func(sep string, a ...string) string {
return strings.Join(a, sep)
},
"upperFirst": func(s string) string { "upperFirst": func(s string) string {
return strings.ToUpper(s[:1]) + s[1:] return strings.ToUpper(s[:1]) + s[1:]
}, },
@@ -90,26 +100,290 @@ var ProtoHelpersFuncMap = template.FuncMap{
"trimstr": func(cutset, s string) string { "trimstr": func(cutset, s string) string {
return strings.Trim(s, cutset) return strings.Trim(s, cutset)
}, },
"snakeCase": xstrings.ToSnakeCase, "index": func(array interface{}, i int32) interface{} {
"getProtoFile": getProtoFile, slice := reflect.ValueOf(array)
"getMessageType": getMessageType, if slice.Kind() != reflect.Slice {
"getEnumValue": getEnumValue, panic("Error in index(): given a non-slice type")
"isFieldMessage": isFieldMessage, }
"isFieldMessageTimeStamp": isFieldMessageTimeStamp, if i < 0 || int(i) >= slice.Len() {
"isFieldRepeated": isFieldRepeated, panic("Error in index(): index out of bounds")
"haskellType": haskellType, }
"goType": goType, return slice.Index(int(i)).Interface()
"goTypeWithPackage": goTypeWithPackage, },
"jsType": jsType, "add": func(a int, b int) int {
"jsSuffixReserved": jsSuffixReservedKeyword, return a + b
"namespacedFlowType": namespacedFlowType, },
"httpVerb": httpVerb, "subtract": func(a int, b int) int {
"httpPath": httpPath, return a - b
"httpBody": httpBody, },
"shortType": shortType, "multiply": func(a int, b int) int {
"urlHasVarsFromMessage": urlHasVarsFromMessage, return a * b
"lowerGoNormalize": lowerGoNormalize, },
"goNormalize": goNormalize, "divide": func(a int, b int) int {
if b == 0 {
panic("psssst ... little help here ... you cannot divide by 0")
}
return a / b
},
"snakeCase": xstrings.ToSnakeCase,
"getProtoFile": getProtoFile,
"getMessageType": getMessageType,
"getEnumValue": getEnumValue,
"isFieldMessage": isFieldMessage,
"isFieldMessageTimeStamp": isFieldMessageTimeStamp,
"isFieldRepeated": isFieldRepeated,
"haskellType": haskellType,
"goType": goType,
"goZeroValue": goZeroValue,
"goTypeWithPackage": goTypeWithPackage,
"goTypeWithPackageV2": goTypeWithPackageV2,
"jsType": jsType,
"jsSuffixReserved": jsSuffixReservedKeyword,
"namespacedFlowType": namespacedFlowType,
"httpVerb": httpVerb,
"httpPath": httpPath,
"httpPathsAdditionalBindings": httpPathsAdditionalBindings,
"httpBody": httpBody,
"shortType": shortType,
"urlHasVarsFromMessage": urlHasVarsFromMessage,
"lowerGoNormalize": lowerGoNormalize,
"goNormalize": goNormalize,
"leadingComment": leadingComment,
"trailingComment": trailingComment,
"leadingDetachedComments": leadingDetachedComments,
"stringFieldExtension": stringFieldExtension,
"stringMethodOptionsExtension": stringMethodOptionsExtension,
"boolFieldExtension": boolFieldExtension,
"isFieldMap": isFieldMap,
"fieldMapKeyType": fieldMapKeyType,
"fieldMapValueType": fieldMapValueType,
"replaceDict": replaceDict,
"setStore": setStore,
"getStore": getStore,
}
var pathMap map[interface{}]*descriptor.SourceCodeInfo_Location
func setStore(key string, i interface{}) string {
store[key] = i
return ""
}
func getStore(s string) interface{} {
if v, ok := store[s]; ok {
return v
}
panic(fmt.Sprintf("No key named '%s' found", s))
}
func InitPathMap(file *descriptor.FileDescriptorProto) {
pathMap = make(map[interface{}]*descriptor.SourceCodeInfo_Location)
addToPathMap(file.GetSourceCodeInfo(), file, []int32{})
}
func InitPathMaps(files []*descriptor.FileDescriptorProto) {
pathMap = make(map[interface{}]*descriptor.SourceCodeInfo_Location)
for _, file := range files {
addToPathMap(file.GetSourceCodeInfo(), file, []int32{})
}
}
// addToPathMap traverses through the AST adding SourceCodeInfo_Location entries to the pathMap.
// Since the AST is a tree, the recursion finishes once it has gone through all the nodes.
func addToPathMap(info *descriptor.SourceCodeInfo, i interface{}, path []int32) {
loc := findLoc(info, path)
if loc != nil {
pathMap[i] = loc
}
switch d := i.(type) {
case *descriptor.FileDescriptorProto:
for index, descriptor := range d.MessageType {
addToPathMap(info, descriptor, newPath(path, 4, index))
}
for index, descriptor := range d.EnumType {
addToPathMap(info, descriptor, newPath(path, 5, index))
}
for index, descriptor := range d.Service {
addToPathMap(info, descriptor, newPath(path, 6, index))
}
case *descriptor.DescriptorProto:
for index, descriptor := range d.Field {
addToPathMap(info, descriptor, newPath(path, 2, index))
}
for index, descriptor := range d.NestedType {
addToPathMap(info, descriptor, newPath(path, 3, index))
}
for index, descriptor := range d.EnumType {
addToPathMap(info, descriptor, newPath(path, 4, index))
}
case *descriptor.EnumDescriptorProto:
for index, descriptor := range d.Value {
addToPathMap(info, descriptor, newPath(path, 2, index))
}
case *descriptor.ServiceDescriptorProto:
for index, descriptor := range d.Method {
addToPathMap(info, descriptor, newPath(path, 2, index))
}
}
}
func newPath(base []int32, field int32, index int) []int32 {
p := append([]int32{}, base...)
p = append(p, field, int32(index))
return p
}
func findLoc(info *descriptor.SourceCodeInfo, path []int32) *descriptor.SourceCodeInfo_Location {
for _, loc := range info.GetLocation() {
if samePath(loc.Path, path) {
return loc
}
}
return nil
}
func samePath(a, b []int32) bool {
if len(a) != len(b) {
return false
}
for i, p := range a {
if p != b[i] {
return false
}
}
return true
}
func findSourceInfoLocation(i interface{}) *descriptor.SourceCodeInfo_Location {
if pathMap == nil {
return nil
}
return pathMap[i]
}
func leadingComment(i interface{}) string {
loc := pathMap[i]
return loc.GetLeadingComments()
}
func trailingComment(i interface{}) string {
loc := pathMap[i]
return loc.GetTrailingComments()
}
func leadingDetachedComments(i interface{}) []string {
loc := pathMap[i]
return loc.GetLeadingDetachedComments()
}
// stringMethodOptionsExtension extracts method options of a string type.
// To define your own extensions see:
// https://developers.google.com/protocol-buffers/docs/proto#customoptions
// Typically the fieldID of private extensions should be in the range:
// 50000-99999
func stringMethodOptionsExtension(fieldID int32, f *descriptor.MethodDescriptorProto) string {
if f == nil {
return ""
}
if f.Options == nil {
return ""
}
var extendedType *descriptor.MethodOptions
var extensionType *string
eds := proto.RegisteredExtensions(f.Options)
if eds[fieldID] == nil {
ed := &proto.ExtensionDesc{
ExtendedType: extendedType,
ExtensionType: extensionType,
Field: fieldID,
Tag: fmt.Sprintf("bytes,%d", fieldID),
}
proto.RegisterExtension(ed)
eds = proto.RegisteredExtensions(f.Options)
}
ext, err := proto.GetExtension(f.Options, eds[fieldID])
if err != nil {
return ""
}
str, ok := ext.(*string)
if !ok {
return ""
}
return *str
}
func stringFieldExtension(fieldID int32, f *descriptor.FieldDescriptorProto) string {
if f == nil {
return ""
}
if f.Options == nil {
return ""
}
var extendedType *descriptor.FieldOptions
var extensionType *string
eds := proto.RegisteredExtensions(f.Options)
if eds[fieldID] == nil {
ed := &proto.ExtensionDesc{
ExtendedType: extendedType,
ExtensionType: extensionType,
Field: fieldID,
Tag: fmt.Sprintf("bytes,%d", fieldID),
}
proto.RegisterExtension(ed)
eds = proto.RegisteredExtensions(f.Options)
}
ext, err := proto.GetExtension(f.Options, eds[fieldID])
if err != nil {
return ""
}
str, ok := ext.(*string)
if !ok {
return ""
}
return *str
}
func boolFieldExtension(fieldID int32, f *descriptor.FieldDescriptorProto) bool {
if f == nil {
return false
}
if f.Options == nil {
return false
}
var extendedType *descriptor.FieldOptions
var extensionType *bool
eds := proto.RegisteredExtensions(f.Options)
if eds[fieldID] == nil {
ed := &proto.ExtensionDesc{
ExtendedType: extendedType,
ExtensionType: extensionType,
Field: fieldID,
Tag: fmt.Sprintf("varint,%d", fieldID),
}
proto.RegisterExtension(ed)
eds = proto.RegisteredExtensions(f.Options)
}
ext, err := proto.GetExtension(f.Options, eds[fieldID])
if err != nil {
return false
}
str, ok := ext.(*bool)
if !ok {
return false
}
return *str
} }
func init() { func init() {
@@ -180,6 +454,9 @@ func isFieldMessage(f *descriptor.FieldDescriptorProto) bool {
} }
func isFieldRepeated(f *descriptor.FieldDescriptorProto) bool { func isFieldRepeated(f *descriptor.FieldDescriptorProto) bool {
if f == nil {
return false
}
if f.Type != nil && f.Label != nil && *f.Label == descriptor.FieldDescriptorProto_LABEL_REPEATED { if f.Type != nil && f.Label != nil && *f.Label == descriptor.FieldDescriptorProto_LABEL_REPEATED {
return true return true
} }
@@ -187,9 +464,139 @@ func isFieldRepeated(f *descriptor.FieldDescriptorProto) bool {
return false return false
} }
func isFieldMap(f *descriptor.FieldDescriptorProto, m *descriptor.DescriptorProto) bool {
if f.TypeName == nil {
return false
}
shortName := shortType(*f.TypeName)
var nt *descriptor.DescriptorProto
for _, t := range m.NestedType {
if *t.Name == shortName {
nt = t
break
}
}
if nt == nil {
return false
}
for _, f := range nt.Field {
switch *f.Name {
case "key":
if *f.Number != 1 {
return false
}
case "value":
if *f.Number != 2 {
return false
}
default:
return false
}
}
return true
}
func fieldMapKeyType(f *descriptor.FieldDescriptorProto, m *descriptor.DescriptorProto) *descriptor.FieldDescriptorProto {
if f.TypeName == nil {
return nil
}
shortName := shortType(*f.TypeName)
var nt *descriptor.DescriptorProto
for _, t := range m.NestedType {
if *t.Name == shortName {
nt = t
break
}
}
if nt == nil {
return nil
}
for _, f := range nt.Field {
if *f.Name == "key" {
return f
}
}
return nil
}
func fieldMapValueType(f *descriptor.FieldDescriptorProto, m *descriptor.DescriptorProto) *descriptor.FieldDescriptorProto {
if f.TypeName == nil {
return nil
}
shortName := shortType(*f.TypeName)
var nt *descriptor.DescriptorProto
for _, t := range m.NestedType {
if *t.Name == shortName {
nt = t
break
}
}
if nt == nil {
return nil
}
for _, f := range nt.Field {
if *f.Name == "value" {
return f
}
}
return nil
}
// goTypeWithPackageV2 type the field given with its package name.
// This method is an evolution of goTypeWithPackage. It allows you to type a field with a message embedded.
//
// exemple:
// ```proto
// message GetArticleResponse {
// Article article = 1;
// message Storage {
// string code = 1;
// }
// repeated Storage storages = 2;
// }
// ```
// Then the type of `storages` is `GetArticleResponse_Storage` for the go language.
func goTypeWithPackageV2(p *descriptor.FileDescriptorProto, f *descriptor.FieldDescriptorProto) string {
pkg := ""
if *f.Type == descriptor.FieldDescriptorProto_TYPE_MESSAGE || *f.Type == descriptor.FieldDescriptorProto_TYPE_ENUM {
if isTimestampPackage(*f.TypeName) {
return "timestamp"
}
fieldPackage := strings.Split(*f.TypeName, ".")
filePackage := strings.Split(*p.Package, ".")
// check if we are working with a message embedded.
if len(fieldPackage) > 1 && len(fieldPackage)+1 > len(filePackage)+1 {
pkg = strings.Join(fieldPackage[len(filePackage)+1:len(fieldPackage)], "_")
} else {
pkg = getPackageTypeName(*f.TypeName)
}
}
res := goType(pkg, f)
return res
}
// Deprecated. Instead use goTypeWithPackageV2
func goTypeWithPackage(f *descriptor.FieldDescriptorProto) string { func goTypeWithPackage(f *descriptor.FieldDescriptorProto) string {
pkg := "" pkg := ""
if *f.Type == descriptor.FieldDescriptorProto_TYPE_MESSAGE || *f.Type == descriptor.FieldDescriptorProto_TYPE_ENUM { if *f.Type == descriptor.FieldDescriptorProto_TYPE_MESSAGE || *f.Type == descriptor.FieldDescriptorProto_TYPE_ENUM {
if isTimestampPackage(*f.TypeName) {
return "timestamp"
}
pkg = getPackageTypeName(*f.TypeName) pkg = getPackageTypeName(*f.TypeName)
} }
return goType(pkg, f) return goType(pkg, f)
@@ -319,6 +726,39 @@ func goType(pkg string, f *descriptor.FieldDescriptorProto) string {
} }
} }
func goZeroValue(f *descriptor.FieldDescriptorProto) string {
const nilString = "nil"
if *f.Label == descriptor.FieldDescriptorProto_LABEL_REPEATED {
return nilString
}
switch *f.Type {
case descriptor.FieldDescriptorProto_TYPE_DOUBLE:
return "0.0"
case descriptor.FieldDescriptorProto_TYPE_FLOAT:
return "0.0"
case descriptor.FieldDescriptorProto_TYPE_INT64:
return "0"
case descriptor.FieldDescriptorProto_TYPE_UINT64:
return "0"
case descriptor.FieldDescriptorProto_TYPE_INT32:
return "0"
case descriptor.FieldDescriptorProto_TYPE_UINT32:
return "0"
case descriptor.FieldDescriptorProto_TYPE_BOOL:
return "false"
case descriptor.FieldDescriptorProto_TYPE_STRING:
return "\"\""
case descriptor.FieldDescriptorProto_TYPE_MESSAGE:
return nilString
case descriptor.FieldDescriptorProto_TYPE_BYTES:
return "0"
case descriptor.FieldDescriptorProto_TYPE_ENUM:
return nilString
default:
return nilString
}
}
func jsType(f *descriptor.FieldDescriptorProto) string { func jsType(f *descriptor.FieldDescriptorProto) string {
template := "%s" template := "%s"
if isFieldRepeated(f) { if isFieldRepeated(f) {
@@ -357,10 +797,14 @@ func jsSuffixReservedKeyword(s string) string {
return jsReservedRe.ReplaceAllString(s, "${1}${2}_${3}") return jsReservedRe.ReplaceAllString(s, "${1}${2}_${3}")
} }
func getPackageTypeName(s string) string { func isTimestampPackage(s string) bool {
if strings.Compare(s, ".google.protobuf.Timestamp") == 0 { if strings.Compare(s, ".google.protobuf.Timestamp") == 0 {
return "timestamp" return true
} }
return false
}
func getPackageTypeName(s string) string {
if strings.Contains(s, ".") { if strings.Contains(s, ".") {
return strings.Split(s, ".")[1] return strings.Split(s, ".")[1]
} }
@@ -407,6 +851,40 @@ func httpPath(m *descriptor.MethodDescriptorProto) string {
} }
} }
func httpPathsAdditionalBindings(m *descriptor.MethodDescriptorProto) []string {
ext, err := proto.GetExtension(m.Options, options.E_Http)
if err != nil {
panic(err.Error())
}
opts, ok := ext.(*options.HttpRule)
if !ok {
panic(fmt.Sprintf("extension is %T; want an HttpRule", ext))
}
var httpPaths []string
var optsAdditionalBindings = opts.GetAdditionalBindings()
for _, optAdditionalBindings := range optsAdditionalBindings {
switch t := optAdditionalBindings.Pattern.(type) {
case *options.HttpRule_Get:
httpPaths = append(httpPaths, t.Get)
case *options.HttpRule_Post:
httpPaths = append(httpPaths, t.Post)
case *options.HttpRule_Put:
httpPaths = append(httpPaths, t.Put)
case *options.HttpRule_Delete:
httpPaths = append(httpPaths, t.Delete)
case *options.HttpRule_Patch:
httpPaths = append(httpPaths, t.Patch)
case *options.HttpRule_Custom:
httpPaths = append(httpPaths, t.Custom.Path)
default:
// nothing
}
}
return httpPaths
}
func httpVerb(m *descriptor.MethodDescriptorProto) string { func httpVerb(m *descriptor.MethodDescriptorProto) string {
ext, err := proto.GetExtension(m.Options, options.E_Http) ext, err := proto.GetExtension(m.Options, options.E_Http)
@@ -502,3 +980,14 @@ func formatID(base string, formatted string) string {
} }
return formatted return formatted
} }
func replaceDict(src string, dict map[string]interface{}) string {
for old, v := range dict {
new, ok := v.(string)
if !ok {
continue
}
src = strings.Replace(src, old, new, -1)
}
return src
}