You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
233 lines
6.8 KiB
233 lines
6.8 KiB
// Protocol Buffers for Go with Gadgets |
|
// |
|
// Copyright (c) 2013, The GoGo Authors. All rights reserved. |
|
// http://github.com/gogo/protobuf |
|
// |
|
// Redistribution and use in source and binary forms, with or without |
|
// modification, are permitted provided that the following conditions are |
|
// met: |
|
// |
|
// * Redistributions of source code must retain the above copyright |
|
// notice, this list of conditions and the following disclaimer. |
|
// * Redistributions in binary form must reproduce the above |
|
// copyright notice, this list of conditions and the following disclaimer |
|
// in the documentation and/or other materials provided with the |
|
// distribution. |
|
// |
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
|
|
/* |
|
The face plugin generates a function will be generated which can convert a structure which satisfies an interface (face) to the specified structure. |
|
This interface contains getters for each of the fields in the struct. |
|
The specified struct is also generated with the getters. |
|
This means that getters should be turned off so as not to conflict with face getters. |
|
This allows it to satisfy its own face. |
|
|
|
It is enabled by the following extensions: |
|
|
|
- face |
|
- face_all |
|
|
|
Turn off getters by using the following extensions: |
|
|
|
- getters |
|
- getters_all |
|
|
|
The face plugin also generates a test given it is enabled using one of the following extensions: |
|
|
|
- testgen |
|
- testgen_all |
|
|
|
Let us look at: |
|
|
|
github.com/gogo/protobuf/test/example/example.proto |
|
|
|
Btw all the output can be seen at: |
|
|
|
github.com/gogo/protobuf/test/example/* |
|
|
|
The following message: |
|
|
|
message A { |
|
option (gogoproto.face) = true; |
|
option (gogoproto.goproto_getters) = false; |
|
optional string Description = 1 [(gogoproto.nullable) = false]; |
|
optional int64 Number = 2 [(gogoproto.nullable) = false]; |
|
optional bytes Id = 3 [(gogoproto.customtype) = "github.com/gogo/protobuf/test/custom.Uuid", (gogoproto.nullable) = false]; |
|
} |
|
|
|
given to the face plugin, will generate the following code: |
|
|
|
type AFace interface { |
|
Proto() github_com_gogo_protobuf_proto.Message |
|
GetDescription() string |
|
GetNumber() int64 |
|
GetId() github_com_gogo_protobuf_test_custom.Uuid |
|
} |
|
|
|
func (this *A) Proto() github_com_gogo_protobuf_proto.Message { |
|
return this |
|
} |
|
|
|
func (this *A) TestProto() github_com_gogo_protobuf_proto.Message { |
|
return NewAFromFace(this) |
|
} |
|
|
|
func (this *A) GetDescription() string { |
|
return this.Description |
|
} |
|
|
|
func (this *A) GetNumber() int64 { |
|
return this.Number |
|
} |
|
|
|
func (this *A) GetId() github_com_gogo_protobuf_test_custom.Uuid { |
|
return this.Id |
|
} |
|
|
|
func NewAFromFace(that AFace) *A { |
|
this := &A{} |
|
this.Description = that.GetDescription() |
|
this.Number = that.GetNumber() |
|
this.Id = that.GetId() |
|
return this |
|
} |
|
|
|
and the following test code: |
|
|
|
func TestAFace(t *testing7.T) { |
|
popr := math_rand7.New(math_rand7.NewSource(time7.Now().UnixNano())) |
|
p := NewPopulatedA(popr, true) |
|
msg := p.TestProto() |
|
if !p.Equal(msg) { |
|
t.Fatalf("%#v !Face Equal %#v", msg, p) |
|
} |
|
} |
|
|
|
The struct A, representing the message, will also be generated just like always. |
|
As you can see A satisfies its own Face, AFace. |
|
|
|
Creating another struct which satisfies AFace is very easy. |
|
Simply create all these methods specified in AFace. |
|
Implementing The Proto method is done with the helper function NewAFromFace: |
|
|
|
func (this *MyStruct) Proto() proto.Message { |
|
return NewAFromFace(this) |
|
} |
|
|
|
just the like TestProto method which is used to test the NewAFromFace function. |
|
|
|
*/ |
|
package face |
|
|
|
import ( |
|
"github.com/gogo/protobuf/gogoproto" |
|
"github.com/gogo/protobuf/protoc-gen-gogo/generator" |
|
) |
|
|
|
type plugin struct { |
|
*generator.Generator |
|
generator.PluginImports |
|
} |
|
|
|
func NewPlugin() *plugin { |
|
return &plugin{} |
|
} |
|
|
|
func (p *plugin) Name() string { |
|
return "face" |
|
} |
|
|
|
func (p *plugin) Init(g *generator.Generator) { |
|
p.Generator = g |
|
} |
|
|
|
func (p *plugin) Generate(file *generator.FileDescriptor) { |
|
p.PluginImports = generator.NewPluginImports(p.Generator) |
|
protoPkg := p.NewImport("github.com/gogo/protobuf/proto") |
|
if !gogoproto.ImportsGoGoProto(file.FileDescriptorProto) { |
|
protoPkg = p.NewImport("github.com/golang/protobuf/proto") |
|
} |
|
for _, message := range file.Messages() { |
|
if !gogoproto.IsFace(file.FileDescriptorProto, message.DescriptorProto) { |
|
continue |
|
} |
|
if message.DescriptorProto.GetOptions().GetMapEntry() { |
|
continue |
|
} |
|
if message.DescriptorProto.HasExtension() { |
|
panic("face does not support message with extensions") |
|
} |
|
if gogoproto.HasGoGetters(file.FileDescriptorProto, message.DescriptorProto) { |
|
panic("face requires getters to be disabled please use gogoproto.getters or gogoproto.getters_all and set it to false") |
|
} |
|
ccTypeName := generator.CamelCaseSlice(message.TypeName()) |
|
p.P(`type `, ccTypeName, `Face interface{`) |
|
p.In() |
|
p.P(`Proto() `, protoPkg.Use(), `.Message`) |
|
for _, field := range message.Field { |
|
fieldname := p.GetFieldName(message, field) |
|
goTyp, _ := p.GoType(message, field) |
|
if p.IsMap(field) { |
|
m := p.GoMapType(nil, field) |
|
goTyp = m.GoType |
|
} |
|
p.P(`Get`, fieldname, `() `, goTyp) |
|
} |
|
p.Out() |
|
p.P(`}`) |
|
p.P(``) |
|
p.P(`func (this *`, ccTypeName, `) Proto() `, protoPkg.Use(), `.Message {`) |
|
p.In() |
|
p.P(`return this`) |
|
p.Out() |
|
p.P(`}`) |
|
p.P(``) |
|
p.P(`func (this *`, ccTypeName, `) TestProto() `, protoPkg.Use(), `.Message {`) |
|
p.In() |
|
p.P(`return New`, ccTypeName, `FromFace(this)`) |
|
p.Out() |
|
p.P(`}`) |
|
p.P(``) |
|
for _, field := range message.Field { |
|
fieldname := p.GetFieldName(message, field) |
|
goTyp, _ := p.GoType(message, field) |
|
if p.IsMap(field) { |
|
m := p.GoMapType(nil, field) |
|
goTyp = m.GoType |
|
} |
|
p.P(`func (this *`, ccTypeName, `) Get`, fieldname, `() `, goTyp, `{`) |
|
p.In() |
|
p.P(` return this.`, fieldname) |
|
p.Out() |
|
p.P(`}`) |
|
p.P(``) |
|
} |
|
p.P(``) |
|
p.P(`func New`, ccTypeName, `FromFace(that `, ccTypeName, `Face) *`, ccTypeName, ` {`) |
|
p.In() |
|
p.P(`this := &`, ccTypeName, `{}`) |
|
for _, field := range message.Field { |
|
fieldname := p.GetFieldName(message, field) |
|
p.P(`this.`, fieldname, ` = that.Get`, fieldname, `()`) |
|
} |
|
p.P(`return this`) |
|
p.Out() |
|
p.P(`}`) |
|
p.P(``) |
|
} |
|
} |
|
|
|
func init() { |
|
generator.RegisterPlugin(NewPlugin()) |
|
}
|
|
|