// NameSystems returns the name system used by the generators in this package. func NameSystems() namer.NameSystems { return namer.NameSystems{ "public": namer.NewPublicNamer(0), "private": namer.NewPrivateNamer(0), "raw": namer.NewRawNamer("", nil), } }
// NameSystems returns the name system used by the generators in this package. func NameSystems() namer.NameSystems { pluralExceptions := map[string]string{ "Endpoints": "Endpoints", "ComponentStatus": "ComponentStatus", } return namer.NameSystems{ "public": namer.NewPublicNamer(0), "private": namer.NewPrivateNamer(0), "raw": namer.NewRawNamer("", nil), "publicPlural": namer.NewPublicPluralNamer(pluralExceptions), "privatePlural": namer.NewPrivatePluralNamer(pluralExceptions), } }
// NameSystems returns the name system used by the generators in this package. func NameSystems() namer.NameSystems { pluralExceptions := map[string]string{ "Endpoints": "Endpoints", "SecurityContextConstraints": "SecurityContextConstraints", } return namer.NameSystems{ "public": namer.NewPublicNamer(0), "private": namer.NewPrivateNamer(0), "raw": namer.NewRawNamer("", nil), "publicPlural": namer.NewPublicPluralNamer(pluralExceptions), "privatePlural": namer.NewPrivatePluralNamer(pluralExceptions), "allLowercasePlural": namer.NewAllLowercasePluralNamer(pluralExceptions), } }
func construct(t *testing.T, files map[string]string) *generator.Context { b := parser.New() for name, src := range files { if err := b.AddFile(name, []byte(src)); err != nil { t.Fatal(err) } } c, err := generator.NewContext(b, namer.NameSystems{ "public": namer.NewPublicNamer(0), "private": namer.NewPrivateNamer(0), }, "public") if err != nil { t.Fatal(err) } return c }
func construct(t *testing.T, files map[string]string) *generator.Context { b := parser.New() dir, err := ioutil.TempDir("", "snippet_writer_test") if err != nil { t.Fatal(err) } for name, src := range files { if err := b.AddFile(filepath.Join(dir, name), name, []byte(src)); err != nil { t.Fatal(err) } } c, err := generator.NewContext(b, namer.NameSystems{ "public": namer.NewPublicNamer(0), "private": namer.NewPrivateNamer(0), }, "public") if err != nil { t.Fatal(err) } return c }
func TestStructParse(t *testing.T) { var structTest = map[string]string{ "base/foo/proto/foo.go": ` package foo // Blah is a test. // A test, I tell you. type Blah struct { // A is the first field. A int64 ` + "`" + `json:"a"` + "`" + ` // B is the second field. // Multiline comments work. B string ` + "`" + `json:"b"` + "`" + ` } `, } _, u, o := construct(t, structTest, namer.NewPublicNamer(0)) t.Logf("%#v", o) blahT := u.Get(types.Name{Package: "base/foo/proto", Name: "Blah"}) if blahT == nil { t.Fatal("type not found") } if e, a := types.Struct, blahT.Kind; e != a { t.Errorf("struct kind wrong, wanted %v, got %v", e, a) } if e, a := "Blah is a test.\nA test, I tell you.\n", blahT.CommentLines; e != a { t.Errorf("struct comment wrong, wanted %v, got %v", e, a) } m := types.Member{ Name: "B", Embedded: false, CommentLines: "B is the second field.\nMultiline comments work.\n", Tags: `json:"b"`, Type: types.String, } if e, a := m, blahT.Members[1]; !reflect.DeepEqual(e, a) { t.Errorf("wanted, got:\n%#v\n%#v", e, a) } }
func TestParseSecondClosestCommentLines(t *testing.T) { const fileName = "base/foo/proto/foo.go" testCases := []struct { testFile map[string]string expected string }{ { map[string]string{fileName: `package foo // Blah's SecondClosestCommentLines. // Another line. // Blah is a test. // A test, I tell you. type Blah struct { a int } `}, "Blah's SecondClosestCommentLines.\nAnother line.\n", }, { map[string]string{fileName: `package foo // Blah's SecondClosestCommentLines. // Another line. type Blah struct { a int } `}, "Blah's SecondClosestCommentLines.\nAnother line.\n", }, } for _, test := range testCases { _, u, o := construct(t, test.testFile, namer.NewPublicNamer(0)) t.Logf("%#v", o) blahT := u.Type(types.Name{Package: "base/foo/proto", Name: "Blah"}) if e, a := test.expected, blahT.SecondClosestCommentLines; e != a { t.Errorf("struct second closest comment wrong, wanted %v, got %v", e, a) } } }
func TestBuilder(t *testing.T) { var testFiles = map[string]string{ "base/foo/proto/foo.go": ` package foo import ( "base/common/proto" ) type Blah struct { common.Object Count int64 Frobbers map[string]*Frobber Baz []Object Nickname *string NumberIsAFavorite map[int]bool } type Frobber struct { Name string Amount int64 } type Object struct { common.Object } func AFunc(obj1 common.Object, obj2 Object) Frobber { } `, "base/common/proto/common.go": ` package common type Object struct { ID int64 } `, } var tmplText = ` package o {{define "Struct"}}type {{Name .}} interface { {{range $m := .Members}}{{$n := Name $m.Type}} {{if $m.Embedded}}{{$n}}{{else}}{{$m.Name}}() {{$n}}{{if $m.Type.Elem}}{{else}} Set{{$m.Name}}({{$n}}){{end}}{{end}}{{end}} } {{end}} {{define "Func"}}{{$s := .Signature}}func {{Raw .}}( {{range $s.Parameters}}{{Raw .}} {{end}}) {{range $s.Results}}{{Raw .}} {{end}}{} {{end}} {{range $t := .}}{{if eq $t.Kind "Struct"}}{{template "Struct" $t}}{{end}}{{end}} {{range $t := .}}{{if eq $t.Kind "Func"}}{{template "Func" $t}}{{end}}{{end}}` var expect = ` package o type CommonObject interface { ID() Int64 SetID(Int64) } type FooBlah interface { CommonObject Count() Int64 SetCount(Int64) Frobbers() MapStringToPointerFooFrobber Baz() SliceFooObject Nickname() PointerString NumberIsAFavorite() MapIntToBool } type FooFrobber interface { Name() String SetName(String) Amount() Int64 SetAmount(Int64) } type FooObject interface { CommonObject } func proto.AFunc( proto.Object proto.Object ) proto.Frobber {} ` testNamer := namer.NewPublicNamer(1, "proto") rawNamer := namer.NewRawNamer("o", nil) _, u, o := construct(t, testFiles, testNamer) t.Logf("\n%v\n\n", o) tmpl := template.Must( template.New(""). Funcs( map[string]interface{}{ "Name": testNamer.Name, "Raw": rawNamer.Name, }). Parse(tmplText), ) buf := &bytes.Buffer{} tmpl.Execute(buf, o) if e, a := expect, buf.String(); e != a { t.Errorf("Wanted, got:\n%v\n-----\n%v\n", e, a) } if p := u.Package("base/foo/proto"); !p.HasImport("base/common/proto") { t.Errorf("Unexpected lack of import line: %#s", p.Imports) } }
func TestTypeKindParse(t *testing.T) { var testFiles = map[string]string{ "a/foo.go": "package a\ntype Test string\n", "b/foo.go": "package b\ntype Test map[int]string\n", "c/foo.go": "package c\ntype Test []string\n", "d/foo.go": "package d\ntype Test struct{a int; b struct{a int}; c map[int]string; d *string}\n", "e/foo.go": "package e\ntype Test *string\n", "f/foo.go": ` package f import ( "a" "b" ) type Test []a.Test type Test2 *a.Test type Test3 map[a.Test]b.Test type Test4 struct { a struct {a a.Test; b b.Test} b map[a.Test]b.Test c *a.Test d []a.Test e []string } `, "g/foo.go": ` package g type Test func(a, b string) (c, d string) func (t Test) Method(a, b string) (c, d string) { return t(a, b) } type Interface interface{Method(a, b string) (c, d string)} `, } // Check that the right types are found, and the namers give the expected names. assertions := []struct { Package, Name string k types.Kind names []string }{ { Package: "a", Name: "Test", k: types.Alias, names: []string{"Test", "ATest", "test", "aTest", "a.Test"}, }, { Package: "b", Name: "Test", k: types.Map, names: []string{"Test", "BTest", "test", "bTest", "b.Test"}, }, { Package: "c", Name: "Test", k: types.Slice, names: []string{"Test", "CTest", "test", "cTest", "c.Test"}, }, { Package: "d", Name: "Test", k: types.Struct, names: []string{"Test", "DTest", "test", "dTest", "d.Test"}, }, { Package: "e", Name: "Test", k: types.Pointer, names: []string{"Test", "ETest", "test", "eTest", "e.Test"}, }, { Package: "f", Name: "Test", k: types.Slice, names: []string{"Test", "FTest", "test", "fTest", "f.Test"}, }, { Package: "g", Name: "Test", k: types.Func, names: []string{"Test", "GTest", "test", "gTest", "g.Test"}, }, { Package: "g", Name: "Interface", k: types.Interface, names: []string{"Interface", "GInterface", "interface", "gInterface", "g.Interface"}, }, { Package: "", Name: "string", k: types.Builtin, names: []string{"String", "String", "string", "string", "string"}, }, { Package: "", Name: "int", k: types.Builtin, names: []string{"Int", "Int", "int", "int", "int"}, }, { Package: "", Name: "struct{a int}", k: types.Struct, names: []string{"StructInt", "StructInt", "structInt", "structInt", "struct{a int}"}, }, { Package: "", Name: "struct{a a.Test; b b.Test}", k: types.Struct, names: []string{"StructTestTest", "StructATestBTest", "structTestTest", "structATestBTest", "struct{a a.Test; b b.Test}"}, }, { Package: "", Name: "map[int]string", k: types.Map, names: []string{"MapIntToString", "MapIntToString", "mapIntToString", "mapIntToString", "map[int]string"}, }, { Package: "", Name: "map[a.Test]b.Test", k: types.Map, names: []string{"MapTestToTest", "MapATestToBTest", "mapTestToTest", "mapATestToBTest", "map[a.Test]b.Test"}, }, { Package: "", Name: "[]string", k: types.Slice, names: []string{"SliceString", "SliceString", "sliceString", "sliceString", "[]string"}, }, { Package: "", Name: "[]a.Test", k: types.Slice, names: []string{"SliceTest", "SliceATest", "sliceTest", "sliceATest", "[]a.Test"}, }, { Package: "", Name: "*string", k: types.Pointer, names: []string{"PointerString", "PointerString", "pointerString", "pointerString", "*string"}, }, { Package: "", Name: "*a.Test", k: types.Pointer, names: []string{"PointerTest", "PointerATest", "pointerTest", "pointerATest", "*a.Test"}, }, } namers := []namer.Namer{ namer.NewPublicNamer(0), namer.NewPublicNamer(1), namer.NewPrivateNamer(0), namer.NewPrivateNamer(1), namer.NewRawNamer("", nil), } for nameIndex, namer := range namers { _, u, _ := construct(t, testFiles, namer) t.Logf("Found types:\n") for pkgName, pkg := range u { for typeName, cur := range pkg.Types { t.Logf("%q-%q: %s %s", pkgName, typeName, cur.Name, cur.Kind) } } t.Logf("\n\n") for _, item := range assertions { n := types.Name{Package: item.Package, Name: item.Name} thisType := u.Get(n) if thisType == nil { t.Errorf("type %s not found", n) continue } if e, a := item.k, thisType.Kind; e != a { t.Errorf("%v-%s: type kind wrong, wanted %v, got %v (%#v)", nameIndex, n, e, a, thisType) } if e, a := item.names[nameIndex], namer.Name(thisType); e != a { t.Errorf("%v-%s: Expected %q, got %q", nameIndex, n, e, a) } } // Also do some one-off checks gtest := u.Get(types.Name{Package: "g", Name: "Test"}) if e, a := 1, len(gtest.Methods); e != a { t.Errorf("expected %v but found %v methods: %#v", e, a, gtest) } iface := u.Get(types.Name{Package: "g", Name: "Interface"}) if e, a := 1, len(iface.Methods); e != a { t.Errorf("expected %v but found %v methods: %#v", e, a, iface) } } }
func Run(g *Generator) { if g.Common.VerifyOnly { g.OnlyIDL = true g.Clean = false } b := parser.New() b.AddBuildTags("proto") omitTypes := map[types.Name]struct{}{} for _, t := range strings.Split(g.DropEmbeddedFields, ",") { name := types.Name{} if i := strings.LastIndex(t, "."); i != -1 { name.Package, name.Name = t[:i], t[i+1:] } else { name.Name = t } if len(name.Name) == 0 { log.Fatalf("--drop-embedded-types requires names in the form of [GOPACKAGE.]TYPENAME: %v", t) } omitTypes[name] = struct{}{} } boilerplate, err := g.Common.LoadGoBoilerplate() if err != nil { log.Fatalf("Failed loading boilerplate: %v", err) } protobufNames := NewProtobufNamer() outputPackages := generator.Packages{} for _, d := range strings.Split(g.Packages, ",") { generateAllTypes, outputPackage := true, true switch { case strings.HasPrefix(d, "+"): d = d[1:] generateAllTypes = false case strings.HasPrefix(d, "-"): d = d[1:] outputPackage = false } name := protoSafePackage(d) parts := strings.SplitN(d, "=", 2) if len(parts) > 1 { d = parts[0] name = parts[1] } p := newProtobufPackage(d, name, generateAllTypes, omitTypes) header := append([]byte{}, boilerplate...) header = append(header, p.HeaderText...) p.HeaderText = header protobufNames.Add(p) if outputPackage { outputPackages = append(outputPackages, p) } } if !g.Common.VerifyOnly { for _, p := range outputPackages { if err := p.(*protobufPackage).Clean(g.OutputBase); err != nil { log.Fatalf("Unable to clean package %s: %v", p.Name(), err) } } } if g.Clean { return } for _, p := range protobufNames.List() { if err := b.AddDir(p.Path()); err != nil { log.Fatalf("Unable to add directory %q: %v", p.Path(), err) } } c, err := generator.NewContext( b, namer.NameSystems{ "public": namer.NewPublicNamer(3), "proto": protobufNames, }, "public", ) c.Verify = g.Common.VerifyOnly c.FileTypes["protoidl"] = NewProtoFile() if err != nil { log.Fatalf("Failed making a context: %v", err) } if err := protobufNames.AssignTypesToPackages(c); err != nil { log.Fatalf("Failed to identify Common types: %v", err) } if err := c.ExecutePackages(g.OutputBase, outputPackages); err != nil { log.Fatalf("Failed executing generator: %v", err) } if g.OnlyIDL { return } if _, err := exec.LookPath("protoc"); err != nil { log.Fatalf("Unable to find 'protoc': %v", err) } searchArgs := []string{"-I", ".", "-I", g.OutputBase} if len(g.ProtoImport) != 0 { for _, s := range g.ProtoImport { searchArgs = append(searchArgs, "-I", s) } } args := append(searchArgs, fmt.Sprintf("--gogo_out=%s", g.OutputBase)) buf := &bytes.Buffer{} if len(g.Conditional) > 0 { fmt.Fprintf(buf, "// +build %s\n\n", g.Conditional) } buf.Write(boilerplate) for _, outputPackage := range outputPackages { p := outputPackage.(*protobufPackage) path := filepath.Join(g.OutputBase, p.ImportPath()) outputPath := filepath.Join(g.OutputBase, p.OutputPath()) // generate the gogoprotobuf protoc cmd := exec.Command("protoc", append(args, path)...) out, err := cmd.CombinedOutput() if len(out) > 0 { log.Printf(string(out)) } if err != nil { log.Println(strings.Join(cmd.Args, " ")) log.Fatalf("Unable to generate protoc on %s: %v", p.PackageName, err) } if g.SkipGeneratedRewrite { continue } // alter the generated protobuf file to remove the generated types (but leave the serializers) and rewrite the // package statement to match the desired package name if err := RewriteGeneratedGogoProtobufFile(outputPath, p.ExtractGeneratedType, buf.Bytes()); err != nil { log.Fatalf("Unable to rewrite generated %s: %v", outputPath, err) } // sort imports cmd = exec.Command("goimports", "-w", outputPath) out, err = cmd.CombinedOutput() if len(out) > 0 { log.Printf(string(out)) } if err != nil { log.Println(strings.Join(cmd.Args, " ")) log.Fatalf("Unable to rewrite imports for %s: %v", p.PackageName, err) } // format and simplify the generated file cmd = exec.Command("gofmt", "-s", "-w", outputPath) out, err = cmd.CombinedOutput() if len(out) > 0 { log.Printf(string(out)) } if err != nil { log.Println(strings.Join(cmd.Args, " ")) log.Fatalf("Unable to apply gofmt for %s: %v", p.PackageName, err) } } if g.SkipGeneratedRewrite { return } if !g.KeepGogoproto { // generate, but do so without gogoprotobuf extensions for _, outputPackage := range outputPackages { p := outputPackage.(*protobufPackage) p.OmitGogo = true } if err := c.ExecutePackages(g.OutputBase, outputPackages); err != nil { log.Fatalf("Failed executing generator: %v", err) } } for _, outputPackage := range outputPackages { p := outputPackage.(*protobufPackage) if len(p.StructTags) == 0 { continue } pattern := filepath.Join(g.OutputBase, p.PackagePath, "*.go") files, err := filepath.Glob(pattern) if err != nil { log.Fatalf("Can't glob pattern %q: %v", pattern, err) } for _, s := range files { if strings.HasSuffix(s, "_test.go") { continue } if err := RewriteTypesWithProtobufStructTags(s, p.StructTags); err != nil { log.Fatalf("Unable to rewrite with struct tags %s: %v", s, err) } } } }