Commit 529bd2bf authored by Clayton Coleman's avatar Clayton Coleman

Support protobuf.as=Message for aliasing

In order to allow a Go type to have a different internal representation, provide the comment `// +protobuf.as=MESSAGENAME` which substitutes the current message's members with the named message's members. Used by the unversioned.Time type to have the same fields as unversioned.Timestamp and to have an alternate serialization.
parent fd192309
...@@ -135,8 +135,9 @@ func (g *genProtoIDL) GenerateType(c *generator.Context, t *types.Type, w io.Wri ...@@ -135,8 +135,9 @@ func (g *genProtoIDL) GenerateType(c *generator.Context, t *types.Type, w io.Wri
sw := generator.NewSnippetWriter(w, c, "$", "$") sw := generator.NewSnippetWriter(w, c, "$", "$")
b := bodyGen{ b := bodyGen{
locator: &protobufLocator{ locator: &protobufLocator{
namer: c.Namers["proto"].(ProtobufFromGoNamer), namer: c.Namers["proto"].(ProtobufFromGoNamer),
tracker: g.imports, tracker: g.imports,
universe: c.Universe,
localGoPackage: g.localGoPackage.Package, localGoPackage: g.localGoPackage.Package,
}, },
...@@ -164,12 +165,14 @@ type ProtobufFromGoNamer interface { ...@@ -164,12 +165,14 @@ type ProtobufFromGoNamer interface {
type ProtobufLocator interface { type ProtobufLocator interface {
ProtoTypeFor(t *types.Type) (*types.Type, error) ProtoTypeFor(t *types.Type) (*types.Type, error)
GoTypeForName(name types.Name) *types.Type
CastTypeName(name types.Name) string CastTypeName(name types.Name) string
} }
type protobufLocator struct { type protobufLocator struct {
namer ProtobufFromGoNamer namer ProtobufFromGoNamer
tracker namer.ImportTracker tracker namer.ImportTracker
universe types.Universe
localGoPackage string localGoPackage string
} }
...@@ -183,6 +186,13 @@ func (p protobufLocator) CastTypeName(name types.Name) string { ...@@ -183,6 +186,13 @@ func (p protobufLocator) CastTypeName(name types.Name) string {
return name.String() return name.String()
} }
func (p protobufLocator) GoTypeForName(name types.Name) *types.Type {
if len(name.Package) == 0 {
name.Package = p.localGoPackage
}
return p.universe.Type(name)
}
// ProtoTypeFor locates a Protobuf type for the provided Go type (if possible). // ProtoTypeFor locates a Protobuf type for the provided Go type (if possible).
func (p protobufLocator) ProtoTypeFor(t *types.Type) (*types.Type, error) { func (p protobufLocator) ProtoTypeFor(t *types.Type) (*types.Type, error) {
switch { switch {
...@@ -231,6 +241,7 @@ func (b bodyGen) doStruct(sw *generator.SnippetWriter) error { ...@@ -231,6 +241,7 @@ func (b bodyGen) doStruct(sw *generator.SnippetWriter) error {
return nil return nil
} }
var alias *types.Type
var fields []protoField var fields []protoField
options := []string{} options := []string{}
allOptions := types.ExtractCommentTags("+", b.t.CommentLines) allOptions := types.ExtractCommentTags("+", b.t.CommentLines)
...@@ -254,6 +265,14 @@ func (b bodyGen) doStruct(sw *generator.SnippetWriter) error { ...@@ -254,6 +265,14 @@ func (b bodyGen) doStruct(sw *generator.SnippetWriter) error {
options = append(options, fmt.Sprintf("%s = %s", key, v)) options = append(options, fmt.Sprintf("%s = %s", key, v))
} }
} }
// protobuf.as allows a type to have the same message contents as another Go type
case k == "protobuf.as":
fields = nil
if alias = b.locator.GoTypeForName(types.Name{Name: v}); alias == nil {
return fmt.Errorf("type %v references alias %q which does not exist", b.t, v)
}
// protobuf.embed instructs the generator to use the named type in this package
// as an embedded message.
case k == "protobuf.embed": case k == "protobuf.embed":
fields = []protoField{ fields = []protoField{
{ {
...@@ -270,10 +289,13 @@ func (b bodyGen) doStruct(sw *generator.SnippetWriter) error { ...@@ -270,10 +289,13 @@ func (b bodyGen) doStruct(sw *generator.SnippetWriter) error {
} }
} }
} }
if alias == nil {
alias = b.t
}
// If we don't explicitly embed anything, generate fields by traversing fields. // If we don't explicitly embed anything, generate fields by traversing fields.
if fields == nil { if fields == nil {
memberFields, err := membersToFields(b.locator, b.t, b.localPackage, b.omitFieldTypes) memberFields, err := membersToFields(b.locator, alias, b.localPackage, b.omitFieldTypes)
if err != nil { if err != nil {
return fmt.Errorf("type %v cannot be converted to protobuf: %v", b.t, err) return fmt.Errorf("type %v cannot be converted to protobuf: %v", b.t, err)
} }
...@@ -454,7 +476,7 @@ func memberTypeToProtobufField(locator ProtobufLocator, field *protoField, t *ty ...@@ -454,7 +476,7 @@ func memberTypeToProtobufField(locator ProtobufLocator, field *protoField, t *ty
// protobufTagToField extracts information from an existing protobuf tag // protobufTagToField extracts information from an existing protobuf tag
func protobufTagToField(tag string, field *protoField, m types.Member, t *types.Type, localPackage types.Name) error { func protobufTagToField(tag string, field *protoField, m types.Member, t *types.Type, localPackage types.Name) error {
if len(tag) == 0 { if len(tag) == 0 || tag == "-" {
return nil return nil
} }
......
...@@ -131,7 +131,11 @@ func assignGoTypeToProtoPackage(p *protobufPackage, t *types.Type, local, global ...@@ -131,7 +131,11 @@ func assignGoTypeToProtoPackage(p *protobufPackage, t *types.Type, local, global
continue continue
} }
field := &protoField{} field := &protoField{}
if err := protobufTagToField(reflect.StructTag(m.Tags).Get("protobuf"), field, m, t, p.ProtoTypeName()); err == nil && field.Type != nil { tag := reflect.StructTag(m.Tags).Get("protobuf")
if tag == "-" {
continue
}
if err := protobufTagToField(tag, field, m, t, p.ProtoTypeName()); err == nil && field.Type != nil {
assignGoTypeToProtoPackage(p, field.Type, local, global) assignGoTypeToProtoPackage(p, field.Type, local, global)
continue continue
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment