@@ -220,7 +220,7 @@ func generate(conf *config, pkg *ast.Package, fset *token.FileSet) (src []byte,
220220 // Use the converters map to give every field a converter.
221221 for _ , ti := range toWrite {
222222 for _ , f := range ti .fields {
223- if f .converter != nil {
223+ if f .converter != nil || f . noConvert {
224224 continue
225225 }
226226 f .converter , err = makeConverter (f .af .Type , f .protoType , converters )
@@ -422,7 +422,7 @@ func processType(ti *typeInfo, tconf *typeConfig, typeInfos map[string]*typeInfo
422422 ti .spec .Name .Name = ti .veneerName
423423 switch t := ti .spec .Type .(type ) {
424424 case * ast.StructType :
425- // Check that all configured fields are present.
425+ // Check that all configured fields are present, and added fields are not .
426426 exportedFields := map [string ]bool {}
427427 for _ , f := range t .Fields .List {
428428 if len (f .Names ) > 1 {
@@ -433,13 +433,16 @@ func processType(ti *typeInfo, tconf *typeConfig, typeInfos map[string]*typeInfo
433433 }
434434 }
435435 if tconf != nil {
436- for name := range tconf .Fields {
437- if ! exportedFields [name ] {
436+ for name , fconfig := range tconf .Fields {
437+ if ! exportedFields [name ] && ! fconfig . Add {
438438 return fmt .Errorf ("%s: configured field %s is not present" , ti .protoName , name )
439439 }
440+ if exportedFields [name ] && fconfig .Add {
441+ return fmt .Errorf ("%s: added field %s is already present" , ti .protoName , name )
442+ }
440443 }
441444 }
442- // Process the fields.
445+ // Process the existing fields.
443446 fs := t .Fields .List
444447 t .Fields .List = t .Fields .List [:0 ]
445448 for _ , f := range fs {
@@ -452,6 +455,16 @@ func processType(ti *typeInfo, tconf *typeConfig, typeInfos map[string]*typeInfo
452455 ti .fields = append (ti .fields , fi )
453456 }
454457 }
458+ // Add additional fields.
459+ if tconf != nil {
460+ for name , fconfig := range tconf .Fields {
461+ if fconfig .Add {
462+ if err := addField (name , fconfig , t , ti ); err != nil {
463+ return err
464+ }
465+ }
466+ }
467+ }
455468 // Other processing.
456469 if tconf != nil && tconf .PopulateToFrom != "" {
457470 toFunc , fromFunc := parseCommaPair (tconf .PopulateToFrom )
@@ -552,6 +565,25 @@ func veneerType(protoType ast.Expr, typeInfos map[string]*typeInfo) ast.Expr {
552565 return wtype (protoType )
553566}
554567
568+ func addField (name string , fconfig fieldConfig , t * ast.StructType , ti * typeInfo ) error {
569+ expr , err := parser .ParseExpr (fconfig .Type )
570+ if err != nil {
571+ return err
572+ }
573+ af := & ast.Field {
574+ Names : []* ast.Ident {{Name : name }},
575+ Type : expr ,
576+ }
577+ t .Fields .List = append (t .Fields .List , af )
578+ ti .fields = append (ti .fields , & fieldInfo {
579+ af : af ,
580+ protoName : "" ,
581+ veneerName : name ,
582+ noConvert : true ,
583+ })
584+ return nil
585+ }
586+
555587// processEnumValues processes enum values.
556588// The proto compiler puts all the values for an enum in one GenDecl,
557589// and there are no other values in that GenDecl.
0 commit comments