func (d *deepCopyGen) genRepeatedWriter(m *generator.Descriptor, f *descriptor.FieldDescriptorProto, notNullablePrefix string) func() { fName := generator.CamelCase(*f.Name) if gogoproto.IsCustomName(f) { fName = gogoproto.GetCustomName(f) } typename, _ := d.gen.GoType(m, f) isMessage := f.IsMessage() repeatedFunc := func() { d.gen.P("\tif m.", fName, " != nil {") d.gen.P("\t\to.", fName, " = make(", typename, ", 0, len(m.", fName, "))") if isMessage { d.gen.P("\t\tfor _, v := range m.", fName, " {") d.gen.P("\t\t\to.", fName, " = append(o.", fName, ", ", notNullablePrefix, "v.Copy())") d.gen.P("\t\t}") } else { d.gen.P("\t\to.", fName, " = append(o.", fName, ", m.", fName, "...)") } d.gen.P("\t}") d.gen.P() } return repeatedFunc }
func (d *deepCopyGen) genOneOfWriter(m *generator.Descriptor, f *descriptor.FieldDescriptorProto, oneOfFuncs map[string][]func()) { ccTypeName := generator.CamelCaseSlice(m.TypeName()) fName := generator.CamelCase(*f.Name) if gogoproto.IsCustomName(f) { fName = gogoproto.GetCustomName(f) } odp := m.OneofDecl[int(*f.OneofIndex)] oneOfName := generator.CamelCase(odp.GetName()) oneOfNameFuncs, ok := oneOfFuncs[oneOfName] if !ok { oneOfFunc := func() { d.gen.P("\tswitch m.", oneOfName, ".(type) {") } oneOfNameFuncs = append(oneOfNameFuncs, oneOfFunc) } isMessage := f.IsMessage() oneOfFunc := func() { tName := ccTypeName + "_" + fName d.gen.P("\tcase *", tName, ":") d.gen.P("\t\ti := &", tName, " {") if isMessage { d.gen.P("\t\t\t", fName, ": m.Get", fName, "().Copy(),") } else { d.gen.P("\t\t\t", fName, ": m.Get", fName, "(),") } d.gen.P("\t\t}") d.gen.P() d.gen.P("\t\to.", oneOfName, " = i") } oneOfNameFuncs = append(oneOfNameFuncs, oneOfFunc) oneOfFuncs[oneOfName] = oneOfNameFuncs }
func (g *Generator) GetFieldName(message *Descriptor, field *descriptor.FieldDescriptorProto) string { goTyp, _ := g.GoType(message, field) fieldname := CamelCase(*field.Name) if gogoproto.IsCustomName(field) { fieldname = gogoproto.GetCustomName(field) } if gogoproto.IsEmbed(field) { fieldname = EmbedFieldName(goTyp) } if field.OneofIndex != nil { fieldname = message.OneofDecl[int(*field.OneofIndex)].GetName() fieldname = CamelCase(fieldname) } for _, f := range methodNames { if f == fieldname { return fieldname + "_" } } if !gogoproto.IsProtoSizer(message.file, message.DescriptorProto) { if fieldname == "Size" { return fieldname + "_" } } return fieldname }
func (d *deepCopyGen) genMapWriter(m *generator.Descriptor, f *descriptor.FieldDescriptorProto, notNullablePrefix string) func() { fName := generator.CamelCase(*f.Name) if gogoproto.IsCustomName(f) { fName = gogoproto.GetCustomName(f) } dv := d.gen.ObjectNamed(f.GetTypeName()) if desc, ok := dv.(*generator.Descriptor); ok && desc.GetOptions().GetMapEntry() { mt := d.gen.GoMapType(desc, f) typename := mt.GoType valueIsMessage := mt.ValueField.IsMessage() mapfunc := func() { d.gen.P("\tif m.", fName, " != nil {") d.gen.P("\t\to.", fName, " = make(", typename, ")") d.gen.P("\t\tfor k, v := range m.", fName, " {") if valueIsMessage { d.gen.P("\t\t\to.", fName, "[k] = ", notNullablePrefix, "v.Copy()") } else { d.gen.P("\t\t\to.", fName, "[k] = v") } d.gen.P("\t\t}") d.gen.P("\t}") d.gen.P() } return mapfunc } return nil }
func (d *deepCopyGen) genRepeated(m *generator.Descriptor, f *descriptor.FieldDescriptorProto) { fName := generator.CamelCase(*f.Name) if gogoproto.IsCustomName(f) { fName = gogoproto.GetCustomName(f) } typename, _ := d.GoType(m, f) d.P("if o.", fName, " != nil {") d.In() if f.IsMessage() { d.P("m.", fName, " = make(", typename, ", len(o.", fName, "))") // TODO(stevvooe): Handle custom type here? goType := d.TypeName(d.ObjectNamed(f.GetTypeName())) // elides [] or * d.P("for i := range m.", fName, " {") d.In() if !gogoproto.IsNullable(f) { d.genCopyFunc("&m."+fName+"[i]", "&o."+fName+"[i]") } else { d.P("m.", fName, "[i] = &", goType, "{}") d.genCopyFunc("m."+fName+"[i]", "o."+fName+"[i]") } d.Out() d.P("}") } else { d.P("m.", fName, " = make(", typename, ", len(o.", fName, "))") d.P("copy(m.", fName, ", ", "o.", fName, ")") } d.Out() d.P("}") d.P() }
func (p *test) Generate(imports generator.PluginImports, file *generator.FileDescriptor) bool { used := false testingPkg := imports.NewImport("testing") randPkg := imports.NewImport("math/rand") timePkg := imports.NewImport("time") for _, message := range file.Messages() { if !gogoproto.HasTestGen(file.FileDescriptorProto, message.DescriptorProto) { continue } if message.DescriptorProto.GetOptions().GetMapEntry() { continue } used = true ccTypeName := generator.CamelCaseSlice(message.TypeName()) p.P(`func Test`, ccTypeName, `Copy(t *`, testingPkg.Use(), `.T) {`) p.In() p.P(`popr := `, randPkg.Use(), `.New(`, randPkg.Use(), `.NewSource(`, timePkg.Use(), `.Now().UnixNano()))`) p.P(`in := NewPopulated`, ccTypeName, `(popr, true)`) p.P(`out := in.Copy()`) p.P(`if !in.Equal(out) {`) p.In() p.P(`t.Fatalf("%#v != %#v", in, out)`) p.Out() p.P(`}`) for _, f := range message.Field { fName := generator.CamelCase(*f.Name) if gogoproto.IsCustomName(f) { fName = gogoproto.GetCustomName(f) } if f.OneofIndex != nil { odp := message.OneofDecl[int(*f.OneofIndex)] fName = generator.CamelCase(odp.GetName()) } p.P(`if &in.`, fName, ` == &out.`, fName, ` {`) p.In() p.P(`t.Fatalf("`, fName, `: %#v == %#v", &in.`, fName, `, &out.`, fName, `)`) p.Out() p.P(`}`) } p.Out() p.P(`}`) } return used }
func (g *Generator) GetFieldName(message *Descriptor, field *descriptor.FieldDescriptorProto) string { goTyp, _ := g.GoType(message, field) fieldname := CamelCase(*field.Name) if gogoproto.IsCustomName(field) { fieldname = gogoproto.GetCustomName(field) } if gogoproto.IsEmbed(field) { fieldname = EmbedFieldName(goTyp) } for _, f := range methodNames { if f == fieldname { return fieldname + "_" } } return fieldname }
func (d *deepCopyGen) genOneOf(m *generator.Descriptor, oneof *descriptor.OneofDescriptorProto, fields []*descriptor.FieldDescriptorProto) { oneOfName := generator.CamelCase(oneof.GetName()) d.P("if o.", oneOfName, " != nil {") d.In() d.P("switch o.", oneOfName, ".(type) {") for _, f := range fields { ccTypeName := generator.CamelCaseSlice(m.TypeName()) fName := generator.CamelCase(*f.Name) if gogoproto.IsCustomName(f) { fName = gogoproto.GetCustomName(f) } tName := ccTypeName + "_" + fName d.P("case *", tName, ":") d.In() d.P("v := ", tName, " {") d.In() var rhs string if f.IsMessage() { goType := d.TypeName(d.ObjectNamed(f.GetTypeName())) // elides [] or * rhs = "&" + goType + "{}" } else { rhs = "o.Get" + fName + "()" } d.P(fName, ": ", rhs, ",") d.Out() d.P("}") if f.IsMessage() { d.genCopyFunc("v."+fName, "o.Get"+fName+"()") } d.P("m.", oneOfName, " = &v") d.Out() } d.Out() d.P("}") d.Out() d.P("}") d.P() }
func (d *deepCopyGen) genMap(m *generator.Descriptor, f *descriptor.FieldDescriptorProto) bool { fName := generator.CamelCase(*f.Name) if gogoproto.IsCustomName(f) { fName = gogoproto.GetCustomName(f) } dv := d.ObjectNamed(f.GetTypeName()) desc, ok := dv.(*generator.Descriptor) if !ok || !desc.GetOptions().GetMapEntry() { return false } mt := d.GoMapType(desc, f) typename := mt.GoType d.P("if o.", fName, " != nil {") d.In() d.P("m.", fName, " = make(", typename, ", ", "len(o.", fName, "))") d.P("for k, v := range o.", fName, " {") d.In() if mt.ValueField.IsMessage() { if !gogoproto.IsNullable(f) { d.P("n := ", d.TypeName(d.ObjectNamed(mt.ValueField.GetTypeName())), "{}") d.genCopyFunc("&n", "&v") d.P("m.", fName, "[k] = ", "n") } else { d.P("m.", fName, "[k] = &", d.TypeName(d.ObjectNamed(mt.ValueField.GetTypeName())), "{}") d.genCopyFunc("m."+fName+"[k]", "v") } } else { d.P("m.", fName, "[k] = v") } d.Out() d.P("}") d.Out() d.P("}") d.P() return true }
func (d *deepCopyGen) genMsgDeepCopy(m *generator.Descriptor) { if !plugin.DeepcopyEnabled(m.Options) { return } ccTypeName := generator.CamelCaseSlice(m.TypeName()) d.gen.P("func (m *", ccTypeName, ") Copy() *", ccTypeName, "{") d.gen.P("\tif m == nil {") d.gen.P("\t\treturn nil") d.gen.P("\t}") d.gen.P() d.gen.P("\to := &", ccTypeName, "{") var funcs []func() oneOfFuncs := make(map[string][]func()) for _, f := range m.Field { fName := generator.CamelCase(*f.Name) if gogoproto.IsCustomName(f) { fName = gogoproto.GetCustomName(f) } notNullablePrefix := "" if !gogoproto.IsNullable(f) { notNullablePrefix = "*" } // Handle all kinds of message type if f.IsMessage() { // Handle map type if mapfunc := d.genMapWriter(m, f, notNullablePrefix); mapfunc != nil { funcs = append(funcs, mapfunc) continue } // Handle any message which is not repeated or part of oneof if !f.IsRepeated() && f.OneofIndex == nil { d.gen.P("\t\t", fName, ": ", notNullablePrefix, "m.", fName, ".Copy(),") continue } } // Handle repeated field if f.IsRepeated() { funcs = append(funcs, d.genRepeatedWriter(m, f, notNullablePrefix)) continue } // Handle oneof type if f.OneofIndex != nil { d.genOneOfWriter(m, f, oneOfFuncs) continue } // Handle all kinds of scalar type d.gen.P("\t\t", fName, ": m.", fName, ",") } d.gen.P("\t}") d.gen.P() for _, fn := range funcs { fn() } // Sort map keys from oneOfFuncs so that generated code has consistent // ordering. oneOfKeys := make([]string, 0, len(oneOfFuncs)) for key := range oneOfFuncs { oneOfKeys = append(oneOfKeys, key) } sort.Strings(oneOfKeys) for _, key := range oneOfKeys { oneOfNameFuncs := oneOfFuncs[key] for _, oneOfFunc := range oneOfNameFuncs { oneOfFunc() } d.gen.P("\t}") d.gen.P() } d.gen.P("\treturn o") d.gen.P("}") d.gen.P() }
func (d *deepCopyGen) genMsgDeepCopy(m *generator.Descriptor) { ccTypeName := generator.CamelCaseSlice(m.TypeName()) // Generate backwards compatible, type-safe Copy() function. d.P("func (m *", ccTypeName, ") Copy() *", ccTypeName, "{") d.In() d.P("if m == nil {") d.In() d.P("return nil") d.Out() d.P("}") d.P("o := &", ccTypeName, "{}") d.P("o.CopyFrom(m)") d.P("return o") d.Out() d.P("}") d.P() if len(m.Field) == 0 { d.P("func (m *", ccTypeName, ") CopyFrom(src interface{})", " {}") return } d.P("func (m *", ccTypeName, ") CopyFrom(src interface{})", " {") d.P() d.P("o := src.(*", ccTypeName, ")") // shallow copy handles all scalars d.P("*m = *o") oneofByIndex := [][]*descriptor.FieldDescriptorProto{} for _, f := range m.Field { fName := generator.CamelCase(*f.Name) if gogoproto.IsCustomName(f) { fName = gogoproto.GetCustomName(f) } // Handle oneof type, we defer them to a loop below if f.OneofIndex != nil { if len(oneofByIndex) <= int(*f.OneofIndex) { oneofByIndex = append(oneofByIndex, []*descriptor.FieldDescriptorProto{}) } oneofByIndex[*f.OneofIndex] = append(oneofByIndex[*f.OneofIndex], f) continue } // Handle all kinds of message type if f.IsMessage() { // Handle map type if d.genMap(m, f) { continue } // Handle any message which is not repeated or part of oneof if !f.IsRepeated() && f.OneofIndex == nil { if !gogoproto.IsNullable(f) { d.genCopyFunc("&m."+fName, "&o."+fName) } else { d.P("if o.", fName, " != nil {") d.In() // allocate dst object d.P("m.", fName, " = &", d.TypeName(d.ObjectNamed(f.GetTypeName())), "{}") // copy into the allocated struct d.genCopyFunc("m."+fName, "o."+fName) d.Out() d.P("}") } continue } } // Handle repeated field if f.IsRepeated() { d.genRepeated(m, f) continue } // skip: field was a scalar handled by shallow copy! } for i, oo := range m.GetOneofDecl() { d.genOneOf(m, oo, oneofByIndex[i]) } d.P("}") d.P() }
func (p *plugin) Generate(file *generator.FileDescriptor) { for _, msg := range file.Messages() { for _, os := range overwriters { possible := true for _, overwriter := range os { if overwriter(file.FileDescriptorProto, msg.DescriptorProto) { possible = false } } if possible { p.checkOverwrite(msg, os) } } p.checkNameSpace(msg) for _, field := range msg.GetField() { if gogoproto.IsEmbed(field) && gogoproto.IsCustomName(field) { fmt.Fprintf(os.Stderr, "ERROR: field %v with custom name %v cannot be embedded", *field.Name, gogoproto.GetCustomName(field)) os.Exit(1) } } p.checkRepeated(msg) } for _, e := range file.GetExtension() { if gogoproto.IsEmbed(e) { fmt.Fprintf(os.Stderr, "ERROR: extended field %v cannot be embedded", generator.CamelCase(*e.Name)) os.Exit(1) } } }