blob: ab79b2c3185ed3fa99717b41e8be8a3588597229 [file] [log] [blame] [edit]
// 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 <package> <interface> <concrete>"
// !+
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
*/