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.
201 lines
5.6 KiB
201 lines
5.6 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 description (experimental) plugin generates a Description method for each message. |
|
The Description method returns a populated google_protobuf.FileDescriptorSet struct. |
|
This contains the description of the files used to generate this message. |
|
|
|
It is enabled by the following extensions: |
|
|
|
- description |
|
- description_all |
|
|
|
The description 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 B { |
|
option (gogoproto.description) = true; |
|
optional A A = 1 [(gogoproto.nullable) = false, (gogoproto.embed) = true]; |
|
repeated bytes G = 2 [(gogoproto.customtype) = "github.com/gogo/protobuf/test/custom.Uint128", (gogoproto.nullable) = false]; |
|
} |
|
|
|
given to the description plugin, will generate the following code: |
|
|
|
func (this *B) Description() (desc *google_protobuf.FileDescriptorSet) { |
|
return ExampleDescription() |
|
} |
|
|
|
and the following test code: |
|
|
|
func TestDescription(t *testing9.T) { |
|
ExampleDescription() |
|
} |
|
|
|
The hope is to use this struct in some way instead of reflect. |
|
This package is subject to change, since a use has not been figured out yet. |
|
|
|
*/ |
|
package description |
|
|
|
import ( |
|
"bytes" |
|
"compress/gzip" |
|
"fmt" |
|
"github.com/gogo/protobuf/gogoproto" |
|
"github.com/gogo/protobuf/proto" |
|
descriptor "github.com/gogo/protobuf/protoc-gen-gogo/descriptor" |
|
"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 "description" |
|
} |
|
|
|
func (p *plugin) Init(g *generator.Generator) { |
|
p.Generator = g |
|
} |
|
|
|
func (p *plugin) Generate(file *generator.FileDescriptor) { |
|
used := false |
|
localName := generator.FileName(file) |
|
|
|
p.PluginImports = generator.NewPluginImports(p.Generator) |
|
descriptorPkg := p.NewImport("github.com/gogo/protobuf/protoc-gen-gogo/descriptor") |
|
protoPkg := p.NewImport("github.com/gogo/protobuf/proto") |
|
gzipPkg := p.NewImport("compress/gzip") |
|
bytesPkg := p.NewImport("bytes") |
|
ioutilPkg := p.NewImport("io/ioutil") |
|
|
|
for _, message := range file.Messages() { |
|
if !gogoproto.HasDescription(file.FileDescriptorProto, message.DescriptorProto) { |
|
continue |
|
} |
|
if message.DescriptorProto.GetOptions().GetMapEntry() { |
|
continue |
|
} |
|
used = true |
|
ccTypeName := generator.CamelCaseSlice(message.TypeName()) |
|
p.P(`func (this *`, ccTypeName, `) Description() (desc *`, descriptorPkg.Use(), `.FileDescriptorSet) {`) |
|
p.In() |
|
p.P(`return `, localName, `Description()`) |
|
p.Out() |
|
p.P(`}`) |
|
} |
|
|
|
if used { |
|
|
|
p.P(`func `, localName, `Description() (desc *`, descriptorPkg.Use(), `.FileDescriptorSet) {`) |
|
p.In() |
|
//Don't generate SourceCodeInfo, since it will create too much code. |
|
|
|
ss := make([]*descriptor.SourceCodeInfo, 0) |
|
for _, f := range p.Generator.AllFiles().GetFile() { |
|
ss = append(ss, f.SourceCodeInfo) |
|
f.SourceCodeInfo = nil |
|
} |
|
b, err := proto.Marshal(p.Generator.AllFiles()) |
|
if err != nil { |
|
panic(err) |
|
} |
|
for i, f := range p.Generator.AllFiles().GetFile() { |
|
f.SourceCodeInfo = ss[i] |
|
} |
|
p.P(`d := &`, descriptorPkg.Use(), `.FileDescriptorSet{}`) |
|
var buf bytes.Buffer |
|
w, _ := gzip.NewWriterLevel(&buf, gzip.BestCompression) |
|
w.Write(b) |
|
w.Close() |
|
b = buf.Bytes() |
|
p.P("var gzipped = []byte{") |
|
p.In() |
|
p.P("// ", len(b), " bytes of a gzipped FileDescriptorSet") |
|
for len(b) > 0 { |
|
n := 16 |
|
if n > len(b) { |
|
n = len(b) |
|
} |
|
|
|
s := "" |
|
for _, c := range b[:n] { |
|
s += fmt.Sprintf("0x%02x,", c) |
|
} |
|
p.P(s) |
|
|
|
b = b[n:] |
|
} |
|
p.Out() |
|
p.P("}") |
|
p.P(`r := `, bytesPkg.Use(), `.NewReader(gzipped)`) |
|
p.P(`gzipr, err := `, gzipPkg.Use(), `.NewReader(r)`) |
|
p.P(`if err != nil {`) |
|
p.In() |
|
p.P(`panic(err)`) |
|
p.Out() |
|
p.P(`}`) |
|
p.P(`ungzipped, err := `, ioutilPkg.Use(), `.ReadAll(gzipr)`) |
|
p.P(`if err != nil {`) |
|
p.In() |
|
p.P(`panic(err)`) |
|
p.Out() |
|
p.P(`}`) |
|
p.P(`if err := `, protoPkg.Use(), `.Unmarshal(ungzipped, d); err != nil {`) |
|
p.In() |
|
p.P(`panic(err)`) |
|
p.Out() |
|
p.P(`}`) |
|
p.P(`return d`) |
|
p.Out() |
|
p.P(`}`) |
|
} |
|
} |
|
|
|
func init() { |
|
generator.RegisterPlugin(NewPlugin()) |
|
}
|
|
|