123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296 |
- // 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 stringer plugin generates a String method for each message.
- It is enabled by the following extensions:
- - stringer
- - stringer_all
- The stringer 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:
- option (gogoproto.goproto_stringer_all) = false;
- option (gogoproto.stringer_all) = true;
- message A {
- 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 stringer stringer, will generate the following code:
- func (this *A) String() string {
- if this == nil {
- return "nil"
- }
- s := strings.Join([]string{`&A{`,
- `Description:` + fmt.Sprintf("%v", this.Description) + `,`,
- `Number:` + fmt.Sprintf("%v", this.Number) + `,`,
- `Id:` + fmt.Sprintf("%v", this.Id) + `,`,
- `XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`,
- `}`,
- }, "")
- return s
- }
- and the following test code:
- func TestAStringer(t *testing4.T) {
- popr := math_rand4.New(math_rand4.NewSource(time4.Now().UnixNano()))
- p := NewPopulatedA(popr, false)
- s1 := p.String()
- s2 := fmt1.Sprintf("%v", p)
- if s1 != s2 {
- t.Fatalf("String want %v got %v", s1, s2)
- }
- }
- Typically fmt.Printf("%v") will stop to print when it reaches a pointer and
- not print their values, while the generated String method will always print all values, recursively.
- */
- package stringer
- import (
- "github.com/gogo/protobuf/gogoproto"
- "github.com/gogo/protobuf/protoc-gen-gogo/generator"
- "strings"
- )
- type stringer struct {
- *generator.Generator
- generator.PluginImports
- atleastOne bool
- localName string
- }
- func NewStringer() *stringer {
- return &stringer{}
- }
- func (p *stringer) Name() string {
- return "stringer"
- }
- func (p *stringer) Init(g *generator.Generator) {
- p.Generator = g
- }
- func (p *stringer) Generate(file *generator.FileDescriptor) {
- proto3 := gogoproto.IsProto3(file.FileDescriptorProto)
- p.PluginImports = generator.NewPluginImports(p.Generator)
- p.atleastOne = false
- p.localName = generator.FileName(file)
- fmtPkg := p.NewImport("fmt")
- stringsPkg := p.NewImport("strings")
- reflectPkg := p.NewImport("reflect")
- sortKeysPkg := p.NewImport("github.com/gogo/protobuf/sortkeys")
- protoPkg := p.NewImport("github.com/gogo/protobuf/proto")
- for _, message := range file.Messages() {
- if !gogoproto.IsStringer(file.FileDescriptorProto, message.DescriptorProto) {
- continue
- }
- if gogoproto.EnabledGoStringer(file.FileDescriptorProto, message.DescriptorProto) {
- panic("old string method needs to be disabled, please use gogoproto.goproto_stringer or gogoproto.goproto_stringer_all and set it to false")
- }
- if message.DescriptorProto.GetOptions().GetMapEntry() {
- continue
- }
- p.atleastOne = true
- ccTypeName := generator.CamelCaseSlice(message.TypeName())
- p.P(`func (this *`, ccTypeName, `) String() string {`)
- p.In()
- p.P(`if this == nil {`)
- p.In()
- p.P(`return "nil"`)
- p.Out()
- p.P(`}`)
- for _, field := range message.Field {
- if !p.IsMap(field) {
- continue
- }
- fieldname := p.GetFieldName(message, field)
- m := p.GoMapType(nil, field)
- mapgoTyp, keyField, keyAliasField := m.GoType, m.KeyField, m.KeyAliasField
- keysName := `keysFor` + fieldname
- keygoTyp, _ := p.GoType(nil, keyField)
- keygoTyp = strings.Replace(keygoTyp, "*", "", 1)
- keygoAliasTyp, _ := p.GoType(nil, keyAliasField)
- keygoAliasTyp = strings.Replace(keygoAliasTyp, "*", "", 1)
- keyCapTyp := generator.CamelCase(keygoTyp)
- p.P(keysName, ` := make([]`, keygoTyp, `, 0, len(this.`, fieldname, `))`)
- p.P(`for k, _ := range this.`, fieldname, ` {`)
- p.In()
- if keygoAliasTyp == keygoTyp {
- p.P(keysName, ` = append(`, keysName, `, k)`)
- } else {
- p.P(keysName, ` = append(`, keysName, `, `, keygoTyp, `(k))`)
- }
- p.Out()
- p.P(`}`)
- p.P(sortKeysPkg.Use(), `.`, keyCapTyp, `s(`, keysName, `)`)
- mapName := `mapStringFor` + fieldname
- p.P(mapName, ` := "`, mapgoTyp, `{"`)
- p.P(`for _, k := range `, keysName, ` {`)
- p.In()
- if keygoAliasTyp == keygoTyp {
- p.P(mapName, ` += fmt.Sprintf("%v: %v,", k, this.`, fieldname, `[k])`)
- } else {
- p.P(mapName, ` += fmt.Sprintf("%v: %v,", k, this.`, fieldname, `[`, keygoAliasTyp, `(k)])`)
- }
- p.Out()
- p.P(`}`)
- p.P(mapName, ` += "}"`)
- }
- p.P("s := ", stringsPkg.Use(), ".Join([]string{`&", ccTypeName, "{`,")
- oneofs := make(map[string]struct{})
- for _, field := range message.Field {
- nullable := gogoproto.IsNullable(field)
- repeated := field.IsRepeated()
- fieldname := p.GetFieldName(message, field)
- oneof := field.OneofIndex != nil
- if oneof {
- if _, ok := oneofs[fieldname]; ok {
- continue
- } else {
- oneofs[fieldname] = struct{}{}
- }
- p.P("`", fieldname, ":`", ` + `, fmtPkg.Use(), `.Sprintf("%v", this.`, fieldname, ") + `,", "`,")
- } else if p.IsMap(field) {
- mapName := `mapStringFor` + fieldname
- p.P("`", fieldname, ":`", ` + `, mapName, " + `,", "`,")
- } else if (field.IsMessage() && !gogoproto.IsCustomType(field)) || p.IsGroup(field) {
- desc := p.ObjectNamed(field.GetTypeName())
- msgname := p.TypeName(desc)
- msgnames := strings.Split(msgname, ".")
- typeName := msgnames[len(msgnames)-1]
- if nullable {
- p.P("`", fieldname, ":`", ` + `, stringsPkg.Use(), `.Replace(`, fmtPkg.Use(), `.Sprintf("%v", this.`, fieldname, `), "`, typeName, `","`, msgname, `"`, ", 1) + `,", "`,")
- } else if repeated {
- p.P("`", fieldname, ":`", ` + `, stringsPkg.Use(), `.Replace(`, stringsPkg.Use(), `.Replace(`, fmtPkg.Use(), `.Sprintf("%v", this.`, fieldname, `), "`, typeName, `","`, msgname, `"`, ", 1),`&`,``,1) + `,", "`,")
- } else {
- p.P("`", fieldname, ":`", ` + `, stringsPkg.Use(), `.Replace(`, stringsPkg.Use(), `.Replace(this.`, fieldname, `.String(), "`, typeName, `","`, msgname, `"`, ", 1),`&`,``,1) + `,", "`,")
- }
- } else {
- if nullable && !repeated && !proto3 {
- p.P("`", fieldname, ":`", ` + valueToString`, p.localName, `(this.`, fieldname, ") + `,", "`,")
- } else {
- p.P("`", fieldname, ":`", ` + `, fmtPkg.Use(), `.Sprintf("%v", this.`, fieldname, ") + `,", "`,")
- }
- }
- }
- if message.DescriptorProto.HasExtension() {
- if gogoproto.HasExtensionsMap(file.FileDescriptorProto, message.DescriptorProto) {
- p.P("`XXX_InternalExtensions:` + ", protoPkg.Use(), ".StringFromInternalExtension(this) + `,`,")
- } else {
- p.P("`XXX_extensions:` + ", protoPkg.Use(), ".StringFromExtensionsBytes(this.XXX_extensions) + `,`,")
- }
- }
- if gogoproto.HasUnrecognized(file.FileDescriptorProto, message.DescriptorProto) {
- p.P("`XXX_unrecognized:` + ", fmtPkg.Use(), `.Sprintf("%v", this.XXX_unrecognized) + `, "`,`,")
- }
- p.P("`}`,")
- p.P(`}`, `,""`, ")")
- p.P(`return s`)
- p.Out()
- p.P(`}`)
- //Generate String methods for oneof fields
- for _, field := range message.Field {
- oneof := field.OneofIndex != nil
- if !oneof {
- continue
- }
- ccTypeName := p.OneOfTypeName(message, field)
- p.P(`func (this *`, ccTypeName, `) String() string {`)
- p.In()
- p.P(`if this == nil {`)
- p.In()
- p.P(`return "nil"`)
- p.Out()
- p.P(`}`)
- p.P("s := ", stringsPkg.Use(), ".Join([]string{`&", ccTypeName, "{`,")
- fieldname := p.GetOneOfFieldName(message, field)
- if field.IsMessage() || p.IsGroup(field) {
- desc := p.ObjectNamed(field.GetTypeName())
- msgname := p.TypeName(desc)
- msgnames := strings.Split(msgname, ".")
- typeName := msgnames[len(msgnames)-1]
- p.P("`", fieldname, ":`", ` + `, stringsPkg.Use(), `.Replace(`, fmtPkg.Use(), `.Sprintf("%v", this.`, fieldname, `), "`, typeName, `","`, msgname, `"`, ", 1) + `,", "`,")
- } else {
- p.P("`", fieldname, ":`", ` + `, fmtPkg.Use(), `.Sprintf("%v", this.`, fieldname, ") + `,", "`,")
- }
- p.P("`}`,")
- p.P(`}`, `,""`, ")")
- p.P(`return s`)
- p.Out()
- p.P(`}`)
- }
- }
- if !p.atleastOne {
- return
- }
- p.P(`func valueToString`, p.localName, `(v interface{}) string {`)
- p.In()
- p.P(`rv := `, reflectPkg.Use(), `.ValueOf(v)`)
- p.P(`if rv.IsNil() {`)
- p.In()
- p.P(`return "nil"`)
- p.Out()
- p.P(`}`)
- p.P(`pv := `, reflectPkg.Use(), `.Indirect(rv).Interface()`)
- p.P(`return `, fmtPkg.Use(), `.Sprintf("*%v", pv)`)
- p.Out()
- p.P(`}`)
- }
- func init() {
- generator.RegisterPlugin(NewStringer())
- }
|