func pkgType(s string) (pkg, t string) {
	split := strings.Split(s, ".")
	pkg = strings.Join(split[0:len(split)-1], ".")
	t = split[len(split)-1]
	return pkg, t
}
pkgType returns the package and type from a given string of
the form path/to/package.Type.
package main
import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"go/types"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"strings"
	"golang.org/x/tools/go/loader"
	"gonum.org/v1/gonum/graph"
	"gonum.org/v1/gonum/graph/encoding"
	"gonum.org/v1/gonum/graph/encoding/dot"
	"gonum.org/v1/gonum/graph/simple"
)
type node struct {
	graph.Node
	name string
}
func newNode(g *simple.DirectedGraph, name string) node {
	return node{Node: g.NewNode(), name: name}
}
func (n node) Attributes() []encoding.Attribute {
	return []encoding.Attribute{{Key: "label", Value: n.name}}
}
func main() {
	h := flag.String("http", "", "Address to listen on for server (default: no server).")
	t := flag.String("type", "", "Type to analyze.")
	f := flag.String("filename", "", "Where to store data if -type is specified (default: stdout).")
	d := flag.Bool("debug", false, "Enable debug logging on Stderr")
	flag.Parse()
	if *t == "" && *h == "" {
		flag.Usage()
		os.Exit(1)
	}
	debug := ioutil.Discard
	if *d {
		debug = os.Stderr
	}
	if *t != "" {
		g, err := typeGraph(debug, *t)
		if err != nil {
			fmt.Fprintf(debug, "typeGraph error: %v\n", err)
			os.Exit(1)
		}
		w := os.Stdout
		if *f != "" {
			of, err := os.Create(*f)
			defer of.Close()
			if err != nil {
				fmt.Fprintf(debug, "os.Create error: %v\n", err)
			}
			w = of
		}
		writeDOT(w, g)
		w.Close()
	}
	if *h != "" {
		http.HandleFunc("/dot", logged(handleDOT))
		http.HandleFunc("/rawdot", logged(handleRawDOT))
		http.Handle("/", loggedHandler(http.FileServer(http.Dir("static"))))
		log.Println("Listening on", *h)
		http.ListenAndServe(*h, nil)
	}
}
func logged(h http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		log.Println(r.Method, r.URL)
		h(w, r)
	}
}
func loggedHandler(h http.Handler) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		log.Println(r.Method, r.URL.Path)
		h.ServeHTTP(w, r)
	}
}
type Response struct {
	DOT   string  
	Error string  
}
func handleRawDOT(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	j := json.NewEncoder(w)
	t := r.URL.Query()["type"]
	if len(t) != 1 {
		j.Encode(Response{Error: "more than one type= parameter"})
		return
	}
	g, err := typeGraph(os.Stderr, t[0])
	if err != nil {
		j.Encode(Response{Error: "error getting type: " + err.Error()})
		log.Printf("typeGraph error: %v\n", err)
		return
	}
	b, err := marshalDOT(g)
	if err != nil {
		j.Encode(Response{Error: "error encoding DOT: " + err.Error()})
		log.Printf("marshalDOT error: %v\n", err)
		return
	}
	j.Encode(Response{DOT: string(b)})
}
func handleDOT(w http.ResponseWriter, r *http.Request) {
	j := json.NewEncoder(w)
	t := r.URL.Query()["type"]
	if len(t) != 1 {
		j.Encode(Response{Error: "more than one type= parameter"})
		return
	}
	g, err := typeGraph(os.Stderr, t[0])
	if err != nil {
		j.Encode(Response{Error: "error getting type: " + err.Error()})
		return
	}
	b, err := marshalDOT(g)
	if err != nil {
		j.Encode(Response{Error: "error encoding DOT: " + err.Error()})
		return
	}
	buf := &bytes.Buffer{}
	buf.Write(b)
	cmd := exec.Command("dot", "-Tpng")
	cmd.Stdout = w
	cmd.Stdin = buf
	err = cmd.Run()
	if err != nil {
		j.Encode(Response{Error: "failed to run dot command"})
	}
}
func typeGraph(debug io.Writer, typeString string) (graph.Graph, error) {
	g := simple.NewDirectedGraph()
	rootType, err := findType(typeString)
	if err != nil {
		return nil, err
	}
	s, ok := rootType.Type().Underlying().(*types.Struct)
	if !ok {
		return nil, fmt.Errorf("not a struct")
	}
	fmt.Fprintf(debug, "%s\n", rootType.Type())
	root := newNode(g, fmt.Sprintf("%q", rootType.Type()))
	g.AddNode(root)
	processStruct(debug, "  ", g, root, s)
	return g, nil
}