1 // Protocol Buffers for Go with Gadgets 2 // 3 // Copyright (c) 2013, The GoGo Authors. All rights reserved. 4 // http://github.com/gogo/protobuf 5 // 6 // Redistribution and use in source and binary forms, with or without 7 // modification, are permitted provided that the following conditions are 8 // met: 9 // 10 // * Redistributions of source code must retain the above copyright 11 // notice, this list of conditions and the following disclaimer. 12 // * Redistributions in binary form must reproduce the above 13 // copyright notice, this list of conditions and the following disclaimer 14 // in the documentation and/or other materials provided with the 15 // distribution. 16 // 17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 29 /* 30 The face plugin generates a function will be generated which can convert a structure which satisfies an interface (face) to the specified structure. 31 This interface contains getters for each of the fields in the struct. 32 The specified struct is also generated with the getters. 33 This means that getters should be turned off so as not to conflict with face getters. 34 This allows it to satisfy its own face. 35 36 It is enabled by the following extensions: 37 38 - face 39 - face_all 40 41 Turn off getters by using the following extensions: 42 43 - getters 44 - getters_all 45 46 The face plugin also generates a test given it is enabled using one of the following extensions: 47 48 - testgen 49 - testgen_all 50 51 Let us look at: 52 53 github.com/gogo/protobuf/test/example/example.proto 54 55 Btw all the output can be seen at: 56 57 github.com/gogo/protobuf/test/example/* 58 59 The following message: 60 61 message A { 62 option (gogoproto.face) = true; 63 option (gogoproto.goproto_getters) = false; 64 optional string Description = 1 [(gogoproto.nullable) = false]; 65 optional int64 Number = 2 [(gogoproto.nullable) = false]; 66 optional bytes Id = 3 [(gogoproto.customtype) = "github.com/gogo/protobuf/test/custom.Uuid", (gogoproto.nullable) = false]; 67 } 68 69 given to the face plugin, will generate the following code: 70 71 type AFace interface { 72 Proto() github_com_gogo_protobuf_proto.Message 73 GetDescription() string 74 GetNumber() int64 75 GetId() github_com_gogo_protobuf_test_custom.Uuid 76 } 77 78 func (this *A) Proto() github_com_gogo_protobuf_proto.Message { 79 return this 80 } 81 82 func (this *A) TestProto() github_com_gogo_protobuf_proto.Message { 83 return NewAFromFace(this) 84 } 85 86 func (this *A) GetDescription() string { 87 return this.Description 88 } 89 90 func (this *A) GetNumber() int64 { 91 return this.Number 92 } 93 94 func (this *A) GetId() github_com_gogo_protobuf_test_custom.Uuid { 95 return this.Id 96 } 97 98 func NewAFromFace(that AFace) *A { 99 this := &A{} 100 this.Description = that.GetDescription() 101 this.Number = that.GetNumber() 102 this.Id = that.GetId() 103 return this 104 } 105 106 and the following test code: 107 108 func TestAFace(t *testing7.T) { 109 popr := math_rand7.New(math_rand7.NewSource(time7.Now().UnixNano())) 110 p := NewPopulatedA(popr, true) 111 msg := p.TestProto() 112 if !p.Equal(msg) { 113 t.Fatalf("%#v !Face Equal %#v", msg, p) 114 } 115 } 116 117 The struct A, representing the message, will also be generated just like always. 118 As you can see A satisfies its own Face, AFace. 119 120 Creating another struct which satisfies AFace is very easy. 121 Simply create all these methods specified in AFace. 122 Implementing The Proto method is done with the helper function NewAFromFace: 123 124 func (this *MyStruct) Proto() proto.Message { 125 return NewAFromFace(this) 126 } 127 128 just the like TestProto method which is used to test the NewAFromFace function. 129 130 */ 131 package face 132 133 import ( 134 "github.com/gogo/protobuf/gogoproto" 135 "github.com/gogo/protobuf/protoc-gen-gogo/generator" 136 ) 137 138 type plugin struct { 139 *generator.Generator 140 generator.PluginImports 141 } 142 143 func NewPlugin() *plugin { 144 return &plugin{} 145 } 146 147 func (p *plugin) Name() string { 148 return "face" 149 } 150 151 func (p *plugin) Init(g *generator.Generator) { 152 p.Generator = g 153 } 154 155 func (p *plugin) Generate(file *generator.FileDescriptor) { 156 p.PluginImports = generator.NewPluginImports(p.Generator) 157 protoPkg := p.NewImport("github.com/gogo/protobuf/proto") 158 if !gogoproto.ImportsGoGoProto(file.FileDescriptorProto) { 159 protoPkg = p.NewImport("github.com/golang/protobuf/proto") 160 } 161 for _, message := range file.Messages() { 162 if !gogoproto.IsFace(file.FileDescriptorProto, message.DescriptorProto) { 163 continue 164 } 165 if message.DescriptorProto.GetOptions().GetMapEntry() { 166 continue 167 } 168 if message.DescriptorProto.HasExtension() { 169 panic("face does not support message with extensions") 170 } 171 if gogoproto.HasGoGetters(file.FileDescriptorProto, message.DescriptorProto) { 172 panic("face requires getters to be disabled please use gogoproto.getters or gogoproto.getters_all and set it to false") 173 } 174 ccTypeName := generator.CamelCaseSlice(message.TypeName()) 175 p.P(`type `, ccTypeName, `Face interface{`) 176 p.In() 177 p.P(`Proto() `, protoPkg.Use(), `.Message`) 178 for _, field := range message.Field { 179 fieldname := p.GetFieldName(message, field) 180 goTyp, _ := p.GoType(message, field) 181 if p.IsMap(field) { 182 m := p.GoMapType(nil, field) 183 goTyp = m.GoType 184 } 185 p.P(`Get`, fieldname, `() `, goTyp) 186 } 187 p.Out() 188 p.P(`}`) 189 p.P(``) 190 p.P(`func (this *`, ccTypeName, `) Proto() `, protoPkg.Use(), `.Message {`) 191 p.In() 192 p.P(`return this`) 193 p.Out() 194 p.P(`}`) 195 p.P(``) 196 p.P(`func (this *`, ccTypeName, `) TestProto() `, protoPkg.Use(), `.Message {`) 197 p.In() 198 p.P(`return New`, ccTypeName, `FromFace(this)`) 199 p.Out() 200 p.P(`}`) 201 p.P(``) 202 for _, field := range message.Field { 203 fieldname := p.GetFieldName(message, field) 204 goTyp, _ := p.GoType(message, field) 205 if p.IsMap(field) { 206 m := p.GoMapType(nil, field) 207 goTyp = m.GoType 208 } 209 p.P(`func (this *`, ccTypeName, `) Get`, fieldname, `() `, goTyp, `{`) 210 p.In() 211 p.P(` return this.`, fieldname) 212 p.Out() 213 p.P(`}`) 214 p.P(``) 215 } 216 p.P(``) 217 p.P(`func New`, ccTypeName, `FromFace(that `, ccTypeName, `Face) *`, ccTypeName, ` {`) 218 p.In() 219 p.P(`this := &`, ccTypeName, `{}`) 220 for _, field := range message.Field { 221 fieldname := p.GetFieldName(message, field) 222 p.P(`this.`, fieldname, ` = that.Get`, fieldname, `()`) 223 } 224 p.P(`return this`) 225 p.Out() 226 p.P(`}`) 227 p.P(``) 228 } 229 } 230 231 func init() { 232 generator.RegisterPlugin(NewPlugin()) 233 } 234