// The skeleton command prints the boilerplate for a concrete type // that implements the specified interface type. // // Example: // // $ ./skeleton io ReadWriteCloser buffer // // *buffer implements io.ReadWriteCloser. // type buffer struct{ /* ... */ } // func (b *buffer) Close() error { panic("unimplemented") } // func (b *buffer) Read(p []byte) (n int, err error) { panic("unimplemented") } // func (b *buffer) Write(p []byte) (n int, err error) { panic("unimplemented") } package main import ( "fmt" "go/types" "log" "os" "strings" "unicode" "unicode/utf8" "golang.org/x/tools/go/packages" ) const usage = "Usage: skeleton " // !+ func PrintSkeleton(pkg *types.Package, ifacename, concname string) error { obj := pkg.Scope().Lookup(ifacename) if obj == nil { return fmt.Errorf("%s.%s not found", pkg.Path(), ifacename) } if _, ok := obj.(*types.TypeName); !ok { return fmt.Errorf("%v is not a named type", obj) } iface, ok := obj.Type().Underlying().(*types.Interface) if !ok { return fmt.Errorf("type %v is a %T, not an interface", obj, obj.Type().Underlying()) } // Use first letter of type name as receiver parameter. if !isValidIdentifier(concname) { return fmt.Errorf("invalid concrete type name: %q", concname) } r, _ := utf8.DecodeRuneInString(concname) fmt.Printf("// *%s implements %s.%s.\n", concname, pkg.Path(), ifacename) fmt.Printf("type %s struct{}\n", concname) mset := types.NewMethodSet(iface) for i := 0; i < mset.Len(); i++ { meth := mset.At(i).Obj() sig := types.TypeString(meth.Type(), (*types.Package).Name) fmt.Printf("func (%c *%s) %s%s {\n\tpanic(\"unimplemented\")\n}\n", r, concname, meth.Name(), strings.TrimPrefix(sig, "func")) } return nil } //!- func isValidIdentifier(id string) bool { for i, r := range id { if !unicode.IsLetter(r) && !(i > 0 && unicode.IsDigit(r)) { return false } } return id != "" } func main() { if len(os.Args) != 4 { log.Fatal(usage) } pkgpath, ifacename, concname := os.Args[1], os.Args[2], os.Args[3] // Load only exported type information for the specified package. conf := &packages.Config{Mode: packages.NeedTypes} pkgs, err := packages.Load(conf, pkgpath) if err != nil { log.Fatal(err) // failed to load anything } if packages.PrintErrors(pkgs) > 0 { os.Exit(1) // some packages contained errors } if err := PrintSkeleton(pkgs[0].Types, ifacename, concname); err != nil { log.Fatal(err) } } /* //!+output1 $ ./skeleton io ReadWriteCloser buffer // *buffer implements io.ReadWriteCloser. type buffer struct{} func (b *buffer) Close() error { panic("unimplemented") } func (b *buffer) Read(p []byte) (n int, err error) { panic("unimplemented") } func (b *buffer) Write(p []byte) (n int, err error) { panic("unimplemented") } //!-output1 //!+output2 $ ./skeleton net/http Handler myHandler // *myHandler implements net/http.Handler. type myHandler struct{} func (m *myHandler) ServeHTTP(http.ResponseWriter, *http.Request) { panic("unimplemented") } //!-output2 */