Merge pull request #23871 from smarterclayton/proto_timestamp

Implement a simpler unversioned.Time serialization
This commit is contained in:
Wojciech Tyczynski 2016-04-10 22:47:49 -07:00
commit 6d954a1490
4 changed files with 45 additions and 26 deletions

View File

@ -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 {
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 {
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 {
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 {
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 {
} }
} }
} }
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
// 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
} }

View File

@ -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
} }

View File

@ -28,8 +28,9 @@ import (
// of the factory methods that the time package offers. // of the factory methods that the time package offers.
// //
// +protobuf.options.marshal=false // +protobuf.options.marshal=false
// +protobuf.as=Timestamp
type Time struct { type Time struct {
time.Time `protobuf:"Timestamp,1,req,name=time"` time.Time `protobuf:"-"`
} }
// NewTime returns a wrapped instance of the provided time // NewTime returns a wrapped instance of the provided time

View File

@ -22,15 +22,9 @@ import (
"time" "time"
) )
// ProtoTime is a struct that is equivalent to Time, but intended for // Timestamp is a struct that is equivalent to Time, but intended for
// protobuf marshalling/unmarshalling. It is generated into a serialization // protobuf marshalling/unmarshalling. It is generated into a serialization
// that matches Time. Do not use in Go structs. // that matches Time. Do not use in Go structs.
type ProtoTime struct {
// Represents the time of an event.
Timestamp Timestamp `json:"timestamp"`
}
// Timestamp is a protobuf Timestamp compatible representation of time.Time
type Timestamp struct { type Timestamp struct {
// Represents seconds of UTC time since Unix epoch // Represents seconds of UTC time since Unix epoch
// 1970-01-01T00:00:00Z. Must be from from 0001-01-01T00:00:00Z to // 1970-01-01T00:00:00Z. Must be from from 0001-01-01T00:00:00Z to
@ -39,20 +33,18 @@ type Timestamp struct {
// Non-negative fractions of a second at nanosecond resolution. Negative // Non-negative fractions of a second at nanosecond resolution. Negative
// second values with fractions must still have non-negative nanos values // second values with fractions must still have non-negative nanos values
// that count forward in time. Must be from 0 to 999,999,999 // that count forward in time. Must be from 0 to 999,999,999
// inclusive. // inclusive. This field may be limited in precision depending on context.
Nanos int32 `json:"nanos"` Nanos int32 `json:"nanos"`
} }
// ProtoTime returns the Time as a new ProtoTime value. // Timestamp returns the Time as a new Timestamp value.
func (m *Time) ProtoTime() *ProtoTime { func (m *Time) ProtoTime() *Timestamp {
if m == nil { if m == nil {
return &ProtoTime{} return &Timestamp{}
} }
return &ProtoTime{ return &Timestamp{
Timestamp: Timestamp{ Seconds: m.Time.Unix(),
Seconds: m.Time.Unix(), Nanos: int32(m.Time.Nanosecond()),
Nanos: int32(m.Time.Nanosecond()),
},
} }
} }
@ -61,11 +53,11 @@ func (m *Time) Size() (n int) { return m.ProtoTime().Size() }
// Reset implements the protobuf marshalling interface. // Reset implements the protobuf marshalling interface.
func (m *Time) Unmarshal(data []byte) error { func (m *Time) Unmarshal(data []byte) error {
p := ProtoTime{} p := Timestamp{}
if err := p.Unmarshal(data); err != nil { if err := p.Unmarshal(data); err != nil {
return err return err
} }
m.Time = time.Unix(p.Timestamp.Seconds, int64(p.Timestamp.Nanos)) m.Time = time.Unix(p.Seconds, int64(p.Nanos))
return nil return nil
} }