diff --git a/.travis.yml b/.travis.yml index eb0b2df..59a638d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,8 @@ language: go go: - - 1.7 - - 1.8 + - 1.10.x + - 1.11.x - tip matrix: diff --git a/example/categories/categories.go b/example/categories/categories.go index 39e6d49..96eca06 100644 --- a/example/categories/categories.go +++ b/example/categories/categories.go @@ -5,4 +5,9 @@ package categories type CategoryOptions struct { ShowPrices bool CanBuy bool + // The next field was deleted. + // Field3 int + + // 4th field, use `proteus:",4"` forward compatible + Field4 int `proteus:",4"` } diff --git a/protobuf/transform.go b/protobuf/transform.go index 1fe6bf2..ef40bed 100644 --- a/protobuf/transform.go +++ b/protobuf/transform.go @@ -256,6 +256,12 @@ func (t *Transformer) transformField(pkg *Package, msg *Message, field *scanner. Repeated: repeated, } + // If this field has set pos in tag, + // use customize protobuf positin. + if field.Pos != 0 { + f.Pos = field.Pos + } + // []byte is the only repeated type that maps to // a non-repeated type in protobuf, so we handle // it a bit differently. diff --git a/protobuf/transform_test.go b/protobuf/transform_test.go index 1e16da1..75ea44b 100644 --- a/protobuf/transform_test.go +++ b/protobuf/transform_test.go @@ -270,6 +270,7 @@ func (s *TransformerSuite) TestTransformField() { name string typ scanner.Type expected *Field + pos int }{ { "Foo", @@ -279,6 +280,7 @@ func (s *TransformerSuite) TestTransformField() { Type: NewBasic("int64"), Options: Options{"(gogoproto.casttype)": NewStringValue("int")}, }, + 0, }, { "Bar", @@ -288,6 +290,7 @@ func (s *TransformerSuite) TestTransformField() { Type: NewBasic("bytes"), Options: Options{}, }, + 0, }, { "BazBar", @@ -298,6 +301,7 @@ func (s *TransformerSuite) TestTransformField() { Repeated: true, Options: Options{"(gogoproto.casttype)": NewStringValue("int")}, }, + 0, }, { "CustomID", @@ -310,6 +314,7 @@ func (s *TransformerSuite) TestTransformField() { "(gogoproto.casttype)": NewStringValue("int"), }, }, + 0, }, { "NullableType", @@ -319,6 +324,7 @@ func (s *TransformerSuite) TestTransformField() { Type: NewNamed("my.pckg", "hello"), Options: Options{}, }, + 0, }, { "NonNullableType", @@ -330,11 +336,13 @@ func (s *TransformerSuite) TestTransformField() { "(gogoproto.nullable)": NewLiteralValue("false"), }, }, + 0, }, { "Invalid", scanner.NewBasic("complex64"), nil, + 0, }, { "MyEnum", @@ -344,6 +352,7 @@ func (s *TransformerSuite) TestTransformField() { Type: NewNamed("my.pckg", "MyEnum"), Options: Options{}, }, + 0, }, { "MyAlias", @@ -361,6 +370,7 @@ func (s *TransformerSuite) TestTransformField() { "(gogoproto.casttype)": NewStringValue("my/pckg.MyAlias"), }, }, + 0, }, { "MyRepeatedAlias", @@ -376,6 +386,18 @@ func (s *TransformerSuite) TestTransformField() { ), Options: Options{}, }, + 0, + }, + { + "ProtoID", + scanner.NewBasic("int64"), + &Field{ + Name: "proto_id", + Type: NewBasic("int64"), + Options: Options{"(gogoproto.customname)": NewStringValue("ProtoID")}, + Pos: 101, + }, + 101, }, } @@ -387,6 +409,7 @@ func (s *TransformerSuite) TestTransformField() { f := s.t.transformField(&Package{}, &Message{}, &scanner.Field{ Name: c.name, Type: c.typ, + Pos: c.pos, }, 0) if c.expected == nil { s.Nil(f, c.name) @@ -394,6 +417,7 @@ func (s *TransformerSuite) TestTransformField() { s.Equal(c.expected.Name, f.Name, fmt.Sprintf("Name in %s", c.name)) s.assertType(c.expected.Type, f.Type, c.name) s.Equal(c.expected.Options, f.Options, fmt.Sprintf("Options in %s", c.name)) + s.Equal(c.expected.Pos, f.Pos, fmt.Sprintf("Proto id in %d", c.pos)) } } } diff --git a/scanner/package.go b/scanner/package.go index 0ffbf5a..1e1e0e7 100644 --- a/scanner/package.go +++ b/scanner/package.go @@ -302,6 +302,7 @@ type Field struct { Docs Name string Type Type + Pos int } // Func is either a function or a method. Receiver will be nil in functions, diff --git a/scanner/scanner.go b/scanner/scanner.go index 5fd4c23..e933425 100644 --- a/scanner/scanner.go +++ b/scanner/scanner.go @@ -7,6 +7,7 @@ import ( "os" "path/filepath" "sort" + "strconv" "strings" "sync" @@ -294,6 +295,7 @@ func scanStruct(s *Struct, elem *types.Struct) *Struct { f := &Field{ Name: v.Name(), Type: scanType(v.Type()), + Pos: getProtoID(v, tags), } if f.Type == nil { continue @@ -390,6 +392,17 @@ func isIgnoredField(f *types.Var, tags []string) bool { return !f.Exported() || (len(tags) > 0 && tags[0] == "-") } +func getProtoID(f *types.Var, tags []string) int { + if len(tags) < 2 { + return 0 + } + i, err := strconv.Atoi(tags[1]) + if err != nil { + return 0 + } + return i +} + func objectsInScope(scope *types.Scope) (objs []types.Object) { for _, n := range scope.Names() { obj := scope.Lookup(n) diff --git a/scanner/scanner_test.go b/scanner/scanner_test.go index b18af67..c8e5c37 100644 --- a/scanner/scanner_test.go +++ b/scanner/scanner_test.go @@ -157,6 +157,36 @@ func TestScanStruct(t *testing.T) { }, }, }, + { + "struct with proto field id tag", + types.NewStruct( + []*types.Var{ + mkField("Foo", types.Typ[types.Int], false), + }, + []string{`proteus:",101"`}, + ), + &Struct{ + Fields: []*Field{ + {Name: "Foo", Type: NewBasic("int"), Pos: 101}, + }, + }, + }, + { + "invalid struct with proto field id tag", + types.NewStruct( + []*types.Var{ + mkField("Foo", types.Typ[types.Int], false), + mkField("Bar", types.Typ[types.Int], false), + }, + []string{`proteus:","`, `proteus:",lol"`}, + ), + &Struct{ + Fields: []*Field{ + {Name: "Foo", Type: NewBasic("int"), Pos: 0}, + {Name: "Bar", Type: NewBasic("int"), Pos: 0}, + }, + }, + }, { "struct with unsupported type", types.NewStruct(