Now handles `package.Type` and `*package.Type`
Fixes parsing issues with slice and map types.
Signed-off-by: Brian Goff <cpuguy83@gmail.com>
| ... | ... |
@@ -43,16 +43,6 @@ supplying `--tag`. This flag can be specified multiple times. |
| 43 | 43 |
|
| 44 | 44 |
## Known issues |
| 45 | 45 |
|
| 46 |
-The parser can currently only handle types which are not specifically a map or |
|
| 47 |
-a slice. |
|
| 48 |
-You can, however, create a type that uses a map or a slice internally, for instance: |
|
| 49 |
- |
|
| 50 |
-```go |
|
| 51 |
-type opts map[string]string |
|
| 52 |
-``` |
|
| 53 |
- |
|
| 54 |
-This `opts` type will work, whreas using a `map[string]string` directly will not. |
|
| 55 |
- |
|
| 56 | 46 |
## go-generate |
| 57 | 47 |
|
| 58 | 48 |
You can also use this with go-generate, which is pretty awesome. |
| ... | ... |
@@ -1,5 +1,17 @@ |
| 1 | 1 |
package foo |
| 2 | 2 |
|
| 3 |
+import ( |
|
| 4 |
+ "fmt" |
|
| 5 |
+ |
|
| 6 |
+ aliasedio "io" |
|
| 7 |
+ |
|
| 8 |
+ "github.com/docker/docker/pkg/plugins/pluginrpc-gen/fixtures/otherfixture" |
|
| 9 |
+) |
|
| 10 |
+ |
|
| 11 |
+var ( |
|
| 12 |
+ errFakeImport = fmt.Errorf("just to import fmt for imports tests")
|
|
| 13 |
+) |
|
| 14 |
+ |
|
| 3 | 15 |
type wobble struct {
|
| 4 | 16 |
Some string |
| 5 | 17 |
Val string |
| ... | ... |
@@ -22,6 +34,7 @@ type Fooer3 interface {
|
| 22 | 22 |
Qux(a, b string) (val string, err error) |
| 23 | 23 |
Wobble() (w *wobble) |
| 24 | 24 |
Wiggle() (w wobble) |
| 25 |
+ WiggleWobble(a []*wobble, b []wobble, c map[string]*wobble, d map[*wobble]wobble, e map[string][]wobble, f []*otherfixture.Spaceship) (g map[*wobble]wobble, h [][]*wobble, i otherfixture.Spaceship, j *otherfixture.Spaceship, k map[*otherfixture.Spaceship]otherfixture.Spaceship, l []otherfixture.Spaceship) |
|
| 25 | 26 |
} |
| 26 | 27 |
|
| 27 | 28 |
// Fooer4 is an interface used for tests. |
| ... | ... |
@@ -39,3 +52,38 @@ type Fooer5 interface {
|
| 39 | 39 |
Foo() |
| 40 | 40 |
Bar |
| 41 | 41 |
} |
| 42 |
+ |
|
| 43 |
+// Fooer6 is an interface used for tests. |
|
| 44 |
+type Fooer6 interface {
|
|
| 45 |
+ Foo(a otherfixture.Spaceship) |
|
| 46 |
+} |
|
| 47 |
+ |
|
| 48 |
+// Fooer7 is an interface used for tests. |
|
| 49 |
+type Fooer7 interface {
|
|
| 50 |
+ Foo(a *otherfixture.Spaceship) |
|
| 51 |
+} |
|
| 52 |
+ |
|
| 53 |
+// Fooer8 is an interface used for tests. |
|
| 54 |
+type Fooer8 interface {
|
|
| 55 |
+ Foo(a map[string]otherfixture.Spaceship) |
|
| 56 |
+} |
|
| 57 |
+ |
|
| 58 |
+// Fooer9 is an interface used for tests. |
|
| 59 |
+type Fooer9 interface {
|
|
| 60 |
+ Foo(a map[string]*otherfixture.Spaceship) |
|
| 61 |
+} |
|
| 62 |
+ |
|
| 63 |
+// Fooer10 is an interface used for tests. |
|
| 64 |
+type Fooer10 interface {
|
|
| 65 |
+ Foo(a []otherfixture.Spaceship) |
|
| 66 |
+} |
|
| 67 |
+ |
|
| 68 |
+// Fooer11 is an interface used for tests. |
|
| 69 |
+type Fooer11 interface {
|
|
| 70 |
+ Foo(a []*otherfixture.Spaceship) |
|
| 71 |
+} |
|
| 72 |
+ |
|
| 73 |
+// Fooer12 is an interface used for tests. |
|
| 74 |
+type Fooer12 interface {
|
|
| 75 |
+ Foo(a aliasedio.Reader) |
|
| 76 |
+} |
| ... | ... |
@@ -78,7 +78,7 @@ func main() {
|
| 78 | 78 |
|
| 79 | 79 |
errorOut("parser error", generatedTempl.Execute(&buf, analysis))
|
| 80 | 80 |
src, err := format.Source(buf.Bytes()) |
| 81 |
- errorOut("error formating generated source", err)
|
|
| 81 |
+ errorOut("error formating generated source:\n"+buf.String(), err)
|
|
| 82 | 82 |
errorOut("error writing file", ioutil.WriteFile(*outputFile, src, 0644))
|
| 83 | 83 |
} |
| 84 | 84 |
|
| ... | ... |
@@ -6,7 +6,9 @@ import ( |
| 6 | 6 |
"go/ast" |
| 7 | 7 |
"go/parser" |
| 8 | 8 |
"go/token" |
| 9 |
+ "path" |
|
| 9 | 10 |
"reflect" |
| 11 |
+ "strings" |
|
| 10 | 12 |
) |
| 11 | 13 |
|
| 12 | 14 |
var errBadReturn = errors.New("found return arg with no name: all args must be named")
|
| ... | ... |
@@ -25,6 +27,7 @@ func (e errUnexpectedType) Error() string {
|
| 25 | 25 |
type ParsedPkg struct {
|
| 26 | 26 |
Name string |
| 27 | 27 |
Functions []function |
| 28 |
+ Imports []importSpec |
|
| 28 | 29 |
} |
| 29 | 30 |
|
| 30 | 31 |
type function struct {
|
| ... | ... |
@@ -35,14 +38,29 @@ type function struct {
|
| 35 | 35 |
} |
| 36 | 36 |
|
| 37 | 37 |
type arg struct {
|
| 38 |
- Name string |
|
| 39 |
- ArgType string |
|
| 38 |
+ Name string |
|
| 39 |
+ ArgType string |
|
| 40 |
+ PackageSelector string |
|
| 40 | 41 |
} |
| 41 | 42 |
|
| 42 | 43 |
func (a *arg) String() string {
|
| 43 | 44 |
return a.Name + " " + a.ArgType |
| 44 | 45 |
} |
| 45 | 46 |
|
| 47 |
+type importSpec struct {
|
|
| 48 |
+ Name string |
|
| 49 |
+ Path string |
|
| 50 |
+} |
|
| 51 |
+ |
|
| 52 |
+func (s *importSpec) String() string {
|
|
| 53 |
+ var ss string |
|
| 54 |
+ if len(s.Name) != 0 {
|
|
| 55 |
+ ss += s.Name |
|
| 56 |
+ } |
|
| 57 |
+ ss += s.Path |
|
| 58 |
+ return ss |
|
| 59 |
+} |
|
| 60 |
+ |
|
| 46 | 61 |
// Parse parses the given file for an interface definition with the given name. |
| 47 | 62 |
func Parse(filePath string, objName string) (*ParsedPkg, error) {
|
| 48 | 63 |
fs := token.NewFileSet() |
| ... | ... |
@@ -73,6 +91,44 @@ func Parse(filePath string, objName string) (*ParsedPkg, error) {
|
| 73 | 73 |
return nil, err |
| 74 | 74 |
} |
| 75 | 75 |
|
| 76 |
+ // figure out what imports will be needed |
|
| 77 |
+ imports := make(map[string]importSpec) |
|
| 78 |
+ for _, f := range p.Functions {
|
|
| 79 |
+ args := append(f.Args, f.Returns...) |
|
| 80 |
+ for _, arg := range args {
|
|
| 81 |
+ if len(arg.PackageSelector) == 0 {
|
|
| 82 |
+ continue |
|
| 83 |
+ } |
|
| 84 |
+ |
|
| 85 |
+ for _, i := range pkg.Imports {
|
|
| 86 |
+ if i.Name != nil {
|
|
| 87 |
+ if i.Name.Name != arg.PackageSelector {
|
|
| 88 |
+ continue |
|
| 89 |
+ } |
|
| 90 |
+ imports[i.Path.Value] = importSpec{Name: arg.PackageSelector, Path: i.Path.Value}
|
|
| 91 |
+ break |
|
| 92 |
+ } |
|
| 93 |
+ |
|
| 94 |
+ _, name := path.Split(i.Path.Value) |
|
| 95 |
+ splitName := strings.Split(name, "-") |
|
| 96 |
+ if len(splitName) > 1 {
|
|
| 97 |
+ name = splitName[len(splitName)-1] |
|
| 98 |
+ } |
|
| 99 |
+ // import paths have quotes already added in, so need to remove them for name comparison |
|
| 100 |
+ name = strings.TrimPrefix(name, `"`) |
|
| 101 |
+ name = strings.TrimSuffix(name, `"`) |
|
| 102 |
+ if name == arg.PackageSelector {
|
|
| 103 |
+ imports[i.Path.Value] = importSpec{Path: i.Path.Value}
|
|
| 104 |
+ break |
|
| 105 |
+ } |
|
| 106 |
+ } |
|
| 107 |
+ } |
|
| 108 |
+ } |
|
| 109 |
+ |
|
| 110 |
+ for _, spec := range imports {
|
|
| 111 |
+ p.Imports = append(p.Imports, spec) |
|
| 112 |
+ } |
|
| 113 |
+ |
|
| 76 | 114 |
return p, nil |
| 77 | 115 |
} |
| 78 | 116 |
|
| ... | ... |
@@ -142,22 +198,66 @@ func parseArgs(fields []*ast.Field) ([]arg, error) {
|
| 142 | 142 |
return nil, errBadReturn |
| 143 | 143 |
} |
| 144 | 144 |
for _, name := range f.Names {
|
| 145 |
- var typeName string |
|
| 146 |
- switch argType := f.Type.(type) {
|
|
| 147 |
- case *ast.Ident: |
|
| 148 |
- typeName = argType.Name |
|
| 149 |
- case *ast.StarExpr: |
|
| 150 |
- i, ok := argType.X.(*ast.Ident) |
|
| 151 |
- if !ok {
|
|
| 152 |
- return nil, errUnexpectedType{"*ast.Ident", f.Type}
|
|
| 153 |
- } |
|
| 154 |
- typeName = "*" + i.Name |
|
| 155 |
- default: |
|
| 156 |
- return nil, errUnexpectedType{"*ast.Ident or *ast.StarExpr", f.Type}
|
|
| 145 |
+ p, err := parseExpr(f.Type) |
|
| 146 |
+ if err != nil {
|
|
| 147 |
+ return nil, err |
|
| 157 | 148 |
} |
| 158 |
- |
|
| 159 |
- args = append(args, arg{name.Name, typeName})
|
|
| 149 |
+ args = append(args, arg{name.Name, p.value, p.pkg})
|
|
| 160 | 150 |
} |
| 161 | 151 |
} |
| 162 | 152 |
return args, nil |
| 163 | 153 |
} |
| 154 |
+ |
|
| 155 |
+type parsedExpr struct {
|
|
| 156 |
+ value string |
|
| 157 |
+ pkg string |
|
| 158 |
+} |
|
| 159 |
+ |
|
| 160 |
+func parseExpr(e ast.Expr) (parsedExpr, error) {
|
|
| 161 |
+ var parsed parsedExpr |
|
| 162 |
+ switch i := e.(type) {
|
|
| 163 |
+ case *ast.Ident: |
|
| 164 |
+ parsed.value += i.Name |
|
| 165 |
+ case *ast.StarExpr: |
|
| 166 |
+ p, err := parseExpr(i.X) |
|
| 167 |
+ if err != nil {
|
|
| 168 |
+ return parsed, err |
|
| 169 |
+ } |
|
| 170 |
+ parsed.value += "*" |
|
| 171 |
+ parsed.value += p.value |
|
| 172 |
+ parsed.pkg = p.pkg |
|
| 173 |
+ case *ast.SelectorExpr: |
|
| 174 |
+ p, err := parseExpr(i.X) |
|
| 175 |
+ if err != nil {
|
|
| 176 |
+ return parsed, err |
|
| 177 |
+ } |
|
| 178 |
+ parsed.pkg = p.value |
|
| 179 |
+ parsed.value += p.value + "." |
|
| 180 |
+ parsed.value += i.Sel.Name |
|
| 181 |
+ case *ast.MapType: |
|
| 182 |
+ parsed.value += "map[" |
|
| 183 |
+ p, err := parseExpr(i.Key) |
|
| 184 |
+ if err != nil {
|
|
| 185 |
+ return parsed, err |
|
| 186 |
+ } |
|
| 187 |
+ parsed.value += p.value |
|
| 188 |
+ parsed.value += "]" |
|
| 189 |
+ p, err = parseExpr(i.Value) |
|
| 190 |
+ if err != nil {
|
|
| 191 |
+ return parsed, err |
|
| 192 |
+ } |
|
| 193 |
+ parsed.value += p.value |
|
| 194 |
+ parsed.pkg = p.pkg |
|
| 195 |
+ case *ast.ArrayType: |
|
| 196 |
+ parsed.value += "[]" |
|
| 197 |
+ p, err := parseExpr(i.Elt) |
|
| 198 |
+ if err != nil {
|
|
| 199 |
+ return parsed, err |
|
| 200 |
+ } |
|
| 201 |
+ parsed.value += p.value |
|
| 202 |
+ parsed.pkg = p.pkg |
|
| 203 |
+ default: |
|
| 204 |
+ return parsed, errUnexpectedType{"*ast.Ident or *ast.StarExpr", i}
|
|
| 205 |
+ } |
|
| 206 |
+ return parsed, nil |
|
| 207 |
+} |
| ... | ... |
@@ -47,7 +47,7 @@ func TestParseWithMultipleFuncs(t *testing.T) {
|
| 47 | 47 |
} |
| 48 | 48 |
|
| 49 | 49 |
assertName(t, "foo", pkg.Name) |
| 50 |
- assertNum(t, 6, len(pkg.Functions)) |
|
| 50 |
+ assertNum(t, 7, len(pkg.Functions)) |
|
| 51 | 51 |
|
| 52 | 52 |
f := pkg.Functions[0] |
| 53 | 53 |
assertName(t, "Foo", f.Name) |
| ... | ... |
@@ -105,6 +105,35 @@ func TestParseWithMultipleFuncs(t *testing.T) {
|
| 105 | 105 |
arg = f.Returns[0] |
| 106 | 106 |
assertName(t, "w", arg.Name) |
| 107 | 107 |
assertName(t, "wobble", arg.ArgType) |
| 108 |
+ |
|
| 109 |
+ f = pkg.Functions[6] |
|
| 110 |
+ assertName(t, "WiggleWobble", f.Name) |
|
| 111 |
+ assertNum(t, 6, len(f.Args)) |
|
| 112 |
+ assertNum(t, 6, len(f.Returns)) |
|
| 113 |
+ expectedArgs := [][]string{
|
|
| 114 |
+ {"a", "[]*wobble"},
|
|
| 115 |
+ {"b", "[]wobble"},
|
|
| 116 |
+ {"c", "map[string]*wobble"},
|
|
| 117 |
+ {"d", "map[*wobble]wobble"},
|
|
| 118 |
+ {"e", "map[string][]wobble"},
|
|
| 119 |
+ {"f", "[]*otherfixture.Spaceship"},
|
|
| 120 |
+ } |
|
| 121 |
+ for i, arg := range f.Args {
|
|
| 122 |
+ assertName(t, expectedArgs[i][0], arg.Name) |
|
| 123 |
+ assertName(t, expectedArgs[i][1], arg.ArgType) |
|
| 124 |
+ } |
|
| 125 |
+ expectedReturns := [][]string{
|
|
| 126 |
+ {"g", "map[*wobble]wobble"},
|
|
| 127 |
+ {"h", "[][]*wobble"},
|
|
| 128 |
+ {"i", "otherfixture.Spaceship"},
|
|
| 129 |
+ {"j", "*otherfixture.Spaceship"},
|
|
| 130 |
+ {"k", "map[*otherfixture.Spaceship]otherfixture.Spaceship"},
|
|
| 131 |
+ {"l", "[]otherfixture.Spaceship"},
|
|
| 132 |
+ } |
|
| 133 |
+ for i, ret := range f.Returns {
|
|
| 134 |
+ assertName(t, expectedReturns[i][0], ret.Name) |
|
| 135 |
+ assertName(t, expectedReturns[i][1], ret.ArgType) |
|
| 136 |
+ } |
|
| 108 | 137 |
} |
| 109 | 138 |
|
| 110 | 139 |
func TestParseWithUnamedReturn(t *testing.T) {
|
| ... | ... |
@@ -150,6 +179,31 @@ func TestEmbeddedInterface(t *testing.T) {
|
| 150 | 150 |
assertName(t, "error", arg.ArgType) |
| 151 | 151 |
} |
| 152 | 152 |
|
| 153 |
+func TestParsedImports(t *testing.T) {
|
|
| 154 |
+ cases := []string{"Fooer6", "Fooer7", "Fooer8", "Fooer9", "Fooer10", "Fooer11"}
|
|
| 155 |
+ for _, testCase := range cases {
|
|
| 156 |
+ pkg, err := Parse(testFixture, testCase) |
|
| 157 |
+ if err != nil {
|
|
| 158 |
+ t.Fatal(err) |
|
| 159 |
+ } |
|
| 160 |
+ |
|
| 161 |
+ assertNum(t, 1, len(pkg.Imports)) |
|
| 162 |
+ importPath := strings.Split(pkg.Imports[0].Path, "/") |
|
| 163 |
+ assertName(t, "otherfixture\"", importPath[len(importPath)-1]) |
|
| 164 |
+ assertName(t, "", pkg.Imports[0].Name) |
|
| 165 |
+ } |
|
| 166 |
+} |
|
| 167 |
+ |
|
| 168 |
+func TestAliasedImports(t *testing.T) {
|
|
| 169 |
+ pkg, err := Parse(testFixture, "Fooer12") |
|
| 170 |
+ if err != nil {
|
|
| 171 |
+ t.Fatal(err) |
|
| 172 |
+ } |
|
| 173 |
+ |
|
| 174 |
+ assertNum(t, 1, len(pkg.Imports)) |
|
| 175 |
+ assertName(t, "aliasedio", pkg.Imports[0].Name) |
|
| 176 |
+} |
|
| 177 |
+ |
|
| 153 | 178 |
func assertName(t *testing.T, expected, actual string) {
|
| 154 | 179 |
if expected != actual {
|
| 155 | 180 |
fatalOut(t, fmt.Sprintf("expected name to be `%s`, got: %s", expected, actual))
|
| ... | ... |
@@ -13,6 +13,19 @@ func printArgs(args []arg) string {
|
| 13 | 13 |
return strings.Join(argStr, ", ") |
| 14 | 14 |
} |
| 15 | 15 |
|
| 16 |
+func buildImports(specs []importSpec) string {
|
|
| 17 |
+ if len(specs) == 0 {
|
|
| 18 |
+ return `import "errors"` |
|
| 19 |
+ } |
|
| 20 |
+ imports := "import(\n" |
|
| 21 |
+ imports += "\t\"errors\"\n" |
|
| 22 |
+ for _, i := range specs {
|
|
| 23 |
+ imports += "\t" + i.String() + "\n" |
|
| 24 |
+ } |
|
| 25 |
+ imports += ")" |
|
| 26 |
+ return imports |
|
| 27 |
+} |
|
| 28 |
+ |
|
| 16 | 29 |
func marshalType(t string) string {
|
| 17 | 30 |
switch t {
|
| 18 | 31 |
case "error": |
| ... | ... |
@@ -44,6 +57,7 @@ var templFuncs = template.FuncMap{
|
| 44 | 44 |
"lower": strings.ToLower, |
| 45 | 45 |
"title": title, |
| 46 | 46 |
"tag": buildTag, |
| 47 |
+ "imports": buildImports, |
|
| 47 | 48 |
} |
| 48 | 49 |
|
| 49 | 50 |
func title(s string) string {
|
| ... | ... |
@@ -60,7 +74,7 @@ var generatedTempl = template.Must(template.New("rpc_cient").Funcs(templFuncs).P
|
| 60 | 60 |
|
| 61 | 61 |
package {{ .Name }}
|
| 62 | 62 |
|
| 63 |
-import "errors" |
|
| 63 |
+{{ imports .Imports }}
|
|
| 64 | 64 |
|
| 65 | 65 |
type client interface{
|
| 66 | 66 |
Call(string, interface{}, interface{}) error
|
| ... | ... |
@@ -24,15 +24,12 @@ func NewVolumeDriver(name string, c client) volume.Driver {
|
| 24 | 24 |
return &volumeDriverAdapter{name: name, proxy: proxy}
|
| 25 | 25 |
} |
| 26 | 26 |
|
| 27 |
-type opts map[string]string |
|
| 28 |
-type list []*proxyVolume |
|
| 29 |
- |
|
| 30 | 27 |
// volumeDriver defines the available functions that volume plugins must implement. |
| 31 | 28 |
// This interface is only defined to generate the proxy objects. |
| 32 | 29 |
// It's not intended to be public or reused. |
| 33 | 30 |
type volumeDriver interface {
|
| 34 | 31 |
// Create a volume with the given name |
| 35 |
- Create(name string, opts opts) (err error) |
|
| 32 |
+ Create(name string, opts map[string]string) (err error) |
|
| 36 | 33 |
// Remove the volume with the given name |
| 37 | 34 |
Remove(name string) (err error) |
| 38 | 35 |
// Get the mountpoint of the given volume |
| ... | ... |
@@ -42,7 +39,7 @@ type volumeDriver interface {
|
| 42 | 42 |
// Unmount the given volume |
| 43 | 43 |
Unmount(name, id string) (err error) |
| 44 | 44 |
// List lists all the volumes known to the driver |
| 45 |
- List() (volumes list, err error) |
|
| 45 |
+ List() (volumes []*proxyVolume, err error) |
|
| 46 | 46 |
// Get retrieves the volume with the requested name |
| 47 | 47 |
Get(name string) (volume *proxyVolume, err error) |
| 48 | 48 |
} |
| ... | ... |
@@ -14,14 +14,14 @@ type volumeDriverProxy struct {
|
| 14 | 14 |
|
| 15 | 15 |
type volumeDriverProxyCreateRequest struct {
|
| 16 | 16 |
Name string |
| 17 |
- Opts opts |
|
| 17 |
+ Opts map[string]string |
|
| 18 | 18 |
} |
| 19 | 19 |
|
| 20 | 20 |
type volumeDriverProxyCreateResponse struct {
|
| 21 | 21 |
Err string |
| 22 | 22 |
} |
| 23 | 23 |
|
| 24 |
-func (pp *volumeDriverProxy) Create(name string, opts opts) (err error) {
|
|
| 24 |
+func (pp *volumeDriverProxy) Create(name string, opts map[string]string) (err error) {
|
|
| 25 | 25 |
var ( |
| 26 | 26 |
req volumeDriverProxyCreateRequest |
| 27 | 27 |
ret volumeDriverProxyCreateResponse |
| ... | ... |
@@ -158,11 +158,11 @@ type volumeDriverProxyListRequest struct {
|
| 158 | 158 |
} |
| 159 | 159 |
|
| 160 | 160 |
type volumeDriverProxyListResponse struct {
|
| 161 |
- Volumes list |
|
| 161 |
+ Volumes []*proxyVolume |
|
| 162 | 162 |
Err string |
| 163 | 163 |
} |
| 164 | 164 |
|
| 165 |
-func (pp *volumeDriverProxy) List() (volumes list, err error) {
|
|
| 165 |
+func (pp *volumeDriverProxy) List() (volumes []*proxyVolume, err error) {
|
|
| 166 | 166 |
var ( |
| 167 | 167 |
req volumeDriverProxyListRequest |
| 168 | 168 |
ret volumeDriverProxyListResponse |