// graphviz package graphviz import ( "fmt" "io" "io/ioutil" "os" "os/exec" "strings" ) var replacer *strings.Replacer func init() { replacer = strings.NewReplacer("\n", "\\n", "\r", "\\r", "\t", "\\t") } type Attributes map[string]string func NewAttributes(attributes ...string) Attributes { me := make(Attributes) for i := 1; i < len(attributes); i += 2 { key := attributes[i-1] value := replacer.Replace(attributes[i]) me[key] = value } return me } type Node struct { Attributes ID string } func NewNode(id string, attributes ...string) *Node { me := &Node{} me.ID = id me.Attributes = NewAttributes(attributes...) return me } func (me Attributes) WriteTo(out io.Writer) { comma := false if len(me) > 0 { fmt.Fprintf(out, "[") for k, v := range me { if comma { fmt.Fprintf(out, ",") } fmt.Fprintf(out, "%s=\"%s\"", k, v) comma = true } fmt.Fprintf(out, "]") } } func (me Attributes) WriteForGraphTo(out io.Writer) { if len(me) > 0 { for k, v := range me { fmt.Fprintf(out, "%s=\"%s\";\n", k, v) } } } func (me *Node) WriteTo(out io.Writer) { fmt.Fprintf(out, "%s", me.ID) me.Attributes.WriteTo(out) fmt.Fprintf(out, ";\n") } type Edge struct { Attributes From *Node To *Node } func NewEdge(from, to *Node, attributes ...string) *Edge { me := &Edge{} me.From = from me.To = to me.Attributes = NewAttributes(attributes...) return me } func (me *Edge) WriteTo(out io.Writer) { if (me.From != nil) && (me.To != nil) { fmt.Fprintf(out, "%s -> %s ", me.From.ID, me.To.ID) me.Attributes.WriteTo(out) fmt.Fprintf(out, ";\n") } } type Digraph struct { Attributes nodes []*Node edges []*Edge } func NewDigraph(attributes ...string) *Digraph { me := &Digraph{} me.Attributes = NewAttributes(attributes...) return me } func (me *Digraph) AddNode(id string, attributes ...string) *Node { node := NewNode(id, attributes...) me.nodes = append(me.nodes, node) return node } func (me *Digraph) AddEdge(from, to *Node, attributes ...string) *Edge { edge := NewEdge(from, to, attributes...) me.edges = append(me.edges, edge) return edge } func (me *Digraph) FindNode(id string) *Node { /* XXX stupid linear search for now... */ for _, node := range me.nodes { if node.ID == id { return node } } return nil } func (me *Digraph) AddEdgeByName(from, to string, attributes ...string) *Edge { node_from := me.FindNode(from) node_to := me.FindNode(to) return me.AddEdge(node_from, node_to, attributes...) } func (me *Digraph) WriteTo(out io.Writer) { fmt.Fprintf(out, "digraph {\n") me.Attributes.WriteForGraphTo(out) for _, node := range me.nodes { node.WriteTo(out) } for _, edge := range me.edges { edge.WriteTo(out) } fmt.Fprintf(out, "\n}\n") } func (me *Digraph) Dotty() error { file, err := ioutil.TempFile("", "woe_gv_") if file == nil { return err } me.WriteTo(file) name := file.Name() file.Close() cmd := exec.Command("dotty", name) cmd.Stderr = os.Stderr cmd.Stdout = os.Stdout err = cmd.Start() if err != nil { return err } // cmd.Wait() return nil }