diff --git a/graph/encoding/dot/decode.go b/graph/encoding/dot/decode.go index 55fc0c3b..98b208c4 100644 --- a/graph/encoding/dot/decode.go +++ b/graph/encoding/dot/decode.go @@ -8,32 +8,17 @@ import ( "fmt" "gonum.org/v1/gonum/graph" + "gonum.org/v1/gonum/graph/encoding" "gonum.org/v1/gonum/graph/formats/dot" "gonum.org/v1/gonum/graph/formats/dot/ast" "gonum.org/v1/gonum/graph/internal/set" ) -// Builder is a graph that can have user-defined nodes and edges added. -type Builder interface { - graph.Graph - graph.Builder - // NewEdge adds a new edge from the source to the destination node to the - // graph, or returns the existing edge if already present. - NewEdge(from, to graph.Node) graph.Edge -} - // UnmashalerAttrs is implemented by graph values that can unmarshal global // DOT attributes. type UnmarshalerAttrs interface { // DOTUnmarshalerAttrs returns the global attribute unmarshalers. - DOTUnmarshalerAttrs() (graph, node, edge UnmarshalerAttr) -} - -// UnmarshalerAttr is implemented by types that can unmarshal a DOT -// attribute description of themselves. -type UnmarshalerAttr interface { - // UnmarshalDOTAttr decodes a single DOT attribute. - UnmarshalDOTAttr(attr Attribute) error + DOTUnmarshalerAttrs() (graph, node, edge encoding.UnmarshalerAttr) } // UnmarshalerID is implemented by types that can unmarshal a DOT ID. @@ -43,7 +28,7 @@ type UnmarshalerID interface { } // Unmarshal parses the Graphviz DOT-encoded data and stores the result in dst. -func Unmarshal(data []byte, dst Builder) error { +func Unmarshal(data []byte, dst encoding.Builder) error { file, err := dot.ParseBytes(data) if err != nil { return err @@ -56,7 +41,7 @@ func Unmarshal(data []byte, dst Builder) error { // copyGraph copies the nodes and edges from the Graphviz AST source graph to // the destination graph. Edge direction is maintained if present. -func copyGraph(dst Builder, src *ast.Graph) (err error) { +func copyGraph(dst encoding.Builder, src *ast.Graph) (err error) { defer func() { switch e := recover().(type) { case nil: @@ -93,12 +78,12 @@ type generator struct { // corresponds to the start index of the active (or inner-most) subgraph. subStart []int // graphAttr, nodeAttr and edgeAttr are global graph attributes. - graphAttr, nodeAttr, edgeAttr UnmarshalerAttr + graphAttr, nodeAttr, edgeAttr encoding.UnmarshalerAttr } // node returns the gonum node corresponding to the given dot AST node ID, // generating a new such node if none exist. -func (gen *generator) node(dst Builder, id string) graph.Node { +func (gen *generator) node(dst encoding.Builder, id string) graph.Node { if n, ok := gen.ids[id]; ok { return n } @@ -119,26 +104,26 @@ func (gen *generator) node(dst Builder, id string) graph.Node { } // addStmt adds the given statement to the graph. -func (gen *generator) addStmt(dst Builder, stmt ast.Stmt) { +func (gen *generator) addStmt(dst encoding.Builder, stmt ast.Stmt) { switch stmt := stmt.(type) { case *ast.NodeStmt: - n, ok := gen.node(dst, stmt.Node.ID).(UnmarshalerAttr) + n, ok := gen.node(dst, stmt.Node.ID).(encoding.UnmarshalerAttr) if !ok { return } for _, attr := range stmt.Attrs { - a := Attribute{ + a := encoding.Attribute{ Key: attr.Key, Value: attr.Val, } - if err := n.UnmarshalDOTAttr(a); err != nil { + if err := n.UnmarshalAttr(a); err != nil { panic(fmt.Errorf("unable to unmarshal node DOT attribute (%s=%s)", a.Key, a.Value)) } } case *ast.EdgeStmt: gen.addEdgeStmt(dst, stmt) case *ast.AttrStmt: - var n UnmarshalerAttr + var n encoding.UnmarshalerAttr var dst string switch stmt.Kind { case ast.GraphKind: @@ -163,11 +148,11 @@ func (gen *generator) addStmt(dst Builder, stmt ast.Stmt) { panic("unreachable") } for _, attr := range stmt.Attrs { - a := Attribute{ + a := encoding.Attribute{ Key: attr.Key, Value: attr.Val, } - if err := n.UnmarshalDOTAttr(a); err != nil { + if err := n.UnmarshalAttr(a); err != nil { panic(fmt.Errorf("unable to unmarshal global %s DOT attribute (%s=%s)", dst, a.Key, a.Value)) } } @@ -183,21 +168,21 @@ func (gen *generator) addStmt(dst Builder, stmt ast.Stmt) { } // addEdgeStmt adds the given edge statement to the graph. -func (gen *generator) addEdgeStmt(dst Builder, e *ast.EdgeStmt) { +func (gen *generator) addEdgeStmt(dst encoding.Builder, e *ast.EdgeStmt) { fs := gen.addVertex(dst, e.From) ts := gen.addEdge(dst, e.To) for _, f := range fs { for _, t := range ts { - edge, ok := dst.NewEdge(f, t).(UnmarshalerAttr) + edge, ok := dst.NewEdge(f, t).(encoding.UnmarshalerAttr) if !ok { continue } for _, attr := range e.Attrs { - a := Attribute{ + a := encoding.Attribute{ Key: attr.Key, Value: attr.Val, } - if err := edge.UnmarshalDOTAttr(a); err != nil { + if err := edge.UnmarshalAttr(a); err != nil { panic(fmt.Errorf("unable to unmarshal edge DOT attribute (%s=%s)", a.Key, a.Value)) } } @@ -206,7 +191,7 @@ func (gen *generator) addEdgeStmt(dst Builder, e *ast.EdgeStmt) { } // addVertex adds the given vertex to the graph, and returns its set of nodes. -func (gen *generator) addVertex(dst Builder, v ast.Vertex) []graph.Node { +func (gen *generator) addVertex(dst encoding.Builder, v ast.Vertex) []graph.Node { switch v := v.(type) { case *ast.Node: n := gen.node(dst, v.ID) @@ -223,7 +208,7 @@ func (gen *generator) addVertex(dst Builder, v ast.Vertex) []graph.Node { } // addEdge adds the given edge to the graph, and returns its set of nodes. -func (gen *generator) addEdge(dst Builder, to *ast.Edge) []graph.Node { +func (gen *generator) addEdge(dst encoding.Builder, to *ast.Edge) []graph.Node { if !gen.directed && to.Directed { panic(fmt.Errorf("directed edge to %v in undirected graph", to.Vertex)) } diff --git a/graph/encoding/dot/decode_test.go b/graph/encoding/dot/decode_test.go index 5892a1a7..42181653 100644 --- a/graph/encoding/dot/decode_test.go +++ b/graph/encoding/dot/decode_test.go @@ -9,6 +9,7 @@ import ( "testing" "gonum.org/v1/gonum/graph" + "gonum.org/v1/gonum/graph/encoding" "gonum.org/v1/gonum/graph/simple" ) @@ -27,7 +28,7 @@ func TestRoundTrip(t *testing.T) { }, } for i, g := range golden { - var dst Builder + var dst encoding.Builder if g.directed { dst = newDotDirectedGraph() } else { @@ -129,12 +130,12 @@ func (g *dotDirectedGraph) NewEdge(from, to graph.Node) graph.Edge { } // DOTAttributers implements the dot.Attributers interface. -func (g *dotDirectedGraph) DOTAttributers() (graph, node, edge Attributer) { +func (g *dotDirectedGraph) DOTAttributers() (graph, node, edge encoding.Attributer) { return g.graph, g.node, g.edge } // DOTUnmarshalerAttrs implements the dot.UnmarshalerAttrs interface. -func (g *dotDirectedGraph) DOTUnmarshalerAttrs() (graph, node, edge UnmarshalerAttr) { +func (g *dotDirectedGraph) DOTUnmarshalerAttrs() (graph, node, edge encoding.UnmarshalerAttr) { return &g.graph, &g.node, &g.edge } @@ -170,12 +171,12 @@ func (g *dotUndirectedGraph) NewEdge(from, to graph.Node) graph.Edge { } // DOTAttributers implements the dot.Attributers interface. -func (g *dotUndirectedGraph) DOTAttributers() (graph, node, edge Attributer) { +func (g *dotUndirectedGraph) DOTAttributers() (graph, node, edge encoding.Attributer) { return g.graph, g.node, g.edge } // DOTUnmarshalerAttrs implements the dot.UnmarshalerAttrs interface. -func (g *dotUndirectedGraph) DOTUnmarshalerAttrs() (graph, node, edge UnmarshalerAttr) { +func (g *dotUndirectedGraph) DOTUnmarshalerAttrs() (graph, node, edge encoding.UnmarshalerAttr) { return &g.graph, &g.node, &g.edge } @@ -198,8 +199,8 @@ func (n *dotNode) UnmarshalDOTID(id string) { n.dotID = id } -// UnmarshalDOTAttr decodes a single DOT attribute. -func (n *dotNode) UnmarshalDOTAttr(attr Attribute) error { +// UnmarshalAttr decodes a single DOT attribute. +func (n *dotNode) UnmarshalAttr(attr encoding.Attribute) error { if attr.Key != "label" { return fmt.Errorf("unable to unmarshal node DOT attribute with key %q", attr.Key) } @@ -207,16 +208,16 @@ func (n *dotNode) UnmarshalDOTAttr(attr Attribute) error { return nil } -// DOTAttributes returns the DOT attributes of the node. -func (n *dotNode) DOTAttributes() []Attribute { +// Attributes returns the DOT attributes of the node. +func (n *dotNode) Attributes() []encoding.Attribute { if len(n.Label) == 0 { return nil } - attr := Attribute{ + attr := encoding.Attribute{ Key: "label", Value: n.Label, } - return []Attribute{attr} + return []encoding.Attribute{attr} } // dotEdge extends simple.Edge with a label field to test round-trip encoding and @@ -227,8 +228,8 @@ type dotEdge struct { Label string } -// UnmarshalDOTAttr decodes a single DOT attribute. -func (e *dotEdge) UnmarshalDOTAttr(attr Attribute) error { +// UnmarshalAttr decodes a single DOT attribute. +func (e *dotEdge) UnmarshalAttr(attr encoding.Attribute) error { if attr.Key != "label" { return fmt.Errorf("unable to unmarshal node DOT attribute with key %q", attr.Key) } @@ -236,25 +237,25 @@ func (e *dotEdge) UnmarshalDOTAttr(attr Attribute) error { return nil } -// DOTAttributes returns the DOT attributes of the edge. -func (e *dotEdge) DOTAttributes() []Attribute { +// Attributes returns the DOT attributes of the edge. +func (e *dotEdge) Attributes() []encoding.Attribute { if len(e.Label) == 0 { return nil } - attr := Attribute{ + attr := encoding.Attribute{ Key: "label", Value: e.Label, } - return []Attribute{attr} + return []encoding.Attribute{attr} } // attributes is a helper for global attributes. -type attributes []Attribute +type attributes []encoding.Attribute -func (a attributes) DOTAttributes() []Attribute { - return []Attribute(a) +func (a attributes) Attributes() []encoding.Attribute { + return []encoding.Attribute(a) } -func (a *attributes) UnmarshalDOTAttr(attr Attribute) error { +func (a *attributes) UnmarshalAttr(attr encoding.Attribute) error { *a = append(*a, attr) return nil } diff --git a/graph/encoding/dot/encode.go b/graph/encoding/dot/encode.go index cd269b1e..a7a15493 100644 --- a/graph/encoding/dot/encode.go +++ b/graph/encoding/dot/encode.go @@ -12,6 +12,7 @@ import ( "strings" "gonum.org/v1/gonum/graph" + "gonum.org/v1/gonum/graph/encoding" "gonum.org/v1/gonum/graph/internal/ordered" ) @@ -32,18 +33,7 @@ type Node interface { // Attributers are graph.Graph values that specify top-level DOT // attributes. type Attributers interface { - DOTAttributers() (graph, node, edge Attributer) -} - -// Attributer defines graph.Node or graph.Edge values that can -// specify DOT attributes. -type Attributer interface { - DOTAttributes() []Attribute -} - -// Attribute is a DOT language key value attribute pair. -type Attribute struct { - Key, Value string + DOTAttributers() (graph, node, edge encoding.Attributer) } // Porter defines the behavior of graph.Edge values that can specify @@ -184,7 +174,7 @@ func (p *printer) print(g graph.Graph, name string, needsIndent, isSubgraph bool } p.newline() p.writeNode(n) - if a, ok := n.(Attributer); ok { + if a, ok := n.(encoding.Attributer); ok { p.writeAttributeList(a) } p.buf.WriteByte(';') @@ -252,7 +242,7 @@ func (p *printer) print(g graph.Graph, name string, needsIndent, isSubgraph bool p.writePorts(e.ToPort()) } - if a, ok := g.Edge(n, t).(Attributer); ok { + if a, ok := g.Edge(n, t).(encoding.Attributer); ok { p.writeAttributeList(a) } @@ -297,8 +287,8 @@ func graphID(g graph.Graph, n graph.Node) string { } } -func (p *printer) writeAttributeList(a Attributer) { - attributes := a.DOTAttributes() +func (p *printer) writeAttributeList(a encoding.Attributer) { + attributes := a.Attributes() switch len(attributes) { case 0: case 1: @@ -324,8 +314,8 @@ var attType = []string{"graph", "node", "edge"} func (p *printer) writeAttributeComplex(ca Attributers) { g, n, e := ca.DOTAttributers() haveWrittenBlock := false - for i, a := range []Attributer{g, n, e} { - attributes := a.DOTAttributes() + for i, a := range []encoding.Attributer{g, n, e} { + attributes := a.Attributes() if len(attributes) == 0 { continue } diff --git a/graph/encoding/dot/encode_test.go b/graph/encoding/dot/encode_test.go index 309e3719..0e94291c 100644 --- a/graph/encoding/dot/encode_test.go +++ b/graph/encoding/dot/encode_test.go @@ -9,6 +9,7 @@ import ( "testing" "gonum.org/v1/gonum/graph" + "gonum.org/v1/gonum/graph/encoding" "gonum.org/v1/gonum/graph/simple" ) @@ -112,17 +113,17 @@ func undirectedNamedIDGraphFrom(g []intset) graph.Graph { type attrNode struct { id int64 name string - attr []Attribute + attr []encoding.Attribute } -func (n attrNode) ID() int64 { return n.id } -func (n attrNode) DOTAttributes() []Attribute { return n.attr } +func (n attrNode) ID() int64 { return n.id } +func (n attrNode) Attributes() []encoding.Attribute { return n.attr } -func directedNodeAttrGraphFrom(g []intset, attr [][]Attribute) graph.Directed { +func directedNodeAttrGraphFrom(g []intset, attr [][]encoding.Attribute) graph.Directed { dg := simple.NewDirectedGraph(0, math.Inf(1)) for u, e := range g { u := int64(u) - var at []Attribute + var at []encoding.Attribute if u < int64(len(attr)) { at = attr[u] } @@ -138,11 +139,11 @@ func directedNodeAttrGraphFrom(g []intset, attr [][]Attribute) graph.Directed { return dg } -func undirectedNodeAttrGraphFrom(g []intset, attr [][]Attribute) graph.Graph { +func undirectedNodeAttrGraphFrom(g []intset, attr [][]encoding.Attribute) graph.Graph { dg := simple.NewUndirectedGraph(0, math.Inf(1)) for u, e := range g { u := int64(u) - var at []Attribute + var at []encoding.Attribute if u < int64(len(attr)) { at = attr[u] } @@ -161,18 +162,18 @@ func undirectedNodeAttrGraphFrom(g []intset, attr [][]Attribute) graph.Graph { type namedAttrNode struct { id int64 name string - attr []Attribute + attr []encoding.Attribute } -func (n namedAttrNode) ID() int64 { return n.id } -func (n namedAttrNode) DOTID() string { return n.name } -func (n namedAttrNode) DOTAttributes() []Attribute { return n.attr } +func (n namedAttrNode) ID() int64 { return n.id } +func (n namedAttrNode) DOTID() string { return n.name } +func (n namedAttrNode) Attributes() []encoding.Attribute { return n.attr } -func directedNamedIDNodeAttrGraphFrom(g []intset, attr [][]Attribute) graph.Directed { +func directedNamedIDNodeAttrGraphFrom(g []intset, attr [][]encoding.Attribute) graph.Directed { dg := simple.NewDirectedGraph(0, math.Inf(1)) for u, e := range g { u := int64(u) - var at []Attribute + var at []encoding.Attribute if u < int64(len(attr)) { at = attr[u] } @@ -188,11 +189,11 @@ func directedNamedIDNodeAttrGraphFrom(g []intset, attr [][]Attribute) graph.Dire return dg } -func undirectedNamedIDNodeAttrGraphFrom(g []intset, attr [][]Attribute) graph.Graph { +func undirectedNamedIDNodeAttrGraphFrom(g []intset, attr [][]encoding.Attribute) graph.Graph { dg := simple.NewUndirectedGraph(0, math.Inf(1)) for u, e := range g { u := int64(u) - var at []Attribute + var at []encoding.Attribute if u < int64(len(attr)) { at = attr[u] } @@ -211,15 +212,15 @@ func undirectedNamedIDNodeAttrGraphFrom(g []intset, attr [][]Attribute) graph.Gr type attrEdge struct { from, to graph.Node - attr []Attribute + attr []encoding.Attribute } -func (e attrEdge) From() graph.Node { return e.from } -func (e attrEdge) To() graph.Node { return e.to } -func (e attrEdge) Weight() float64 { return 0 } -func (e attrEdge) DOTAttributes() []Attribute { return e.attr } +func (e attrEdge) From() graph.Node { return e.from } +func (e attrEdge) To() graph.Node { return e.to } +func (e attrEdge) Weight() float64 { return 0 } +func (e attrEdge) Attributes() []encoding.Attribute { return e.attr } -func directedEdgeAttrGraphFrom(g []intset, attr map[edge][]Attribute) graph.Directed { +func directedEdgeAttrGraphFrom(g []intset, attr map[edge][]encoding.Attribute) graph.Directed { dg := simple.NewDirectedGraph(0, math.Inf(1)) for u, e := range g { u := int64(u) @@ -230,7 +231,7 @@ func directedEdgeAttrGraphFrom(g []intset, attr map[edge][]Attribute) graph.Dire return dg } -func undirectedEdgeAttrGraphFrom(g []intset, attr map[edge][]Attribute) graph.Graph { +func undirectedEdgeAttrGraphFrom(g []intset, attr map[edge][]encoding.Attribute) graph.Graph { dg := simple.NewUndirectedGraph(0, math.Inf(1)) for u, e := range g { u := int64(u) @@ -271,11 +272,11 @@ func (e portedEdge) ToPort() (port, compass string) { return e.toPort, e.toCompass } -func directedPortedAttrGraphFrom(g []intset, attr [][]Attribute, ports map[edge]portedEdge) graph.Directed { +func directedPortedAttrGraphFrom(g []intset, attr [][]encoding.Attribute, ports map[edge]portedEdge) graph.Directed { dg := simple.NewDirectedGraph(0, math.Inf(1)) for u, e := range g { u := int64(u) - var at []Attribute + var at []encoding.Attribute if u < int64(len(attr)) { at = attr[u] } @@ -293,11 +294,11 @@ func directedPortedAttrGraphFrom(g []intset, attr [][]Attribute, ports map[edge] return dg } -func undirectedPortedAttrGraphFrom(g []intset, attr [][]Attribute, ports map[edge]portedEdge) graph.Graph { +func undirectedPortedAttrGraphFrom(g []intset, attr [][]encoding.Attribute, ports map[edge]portedEdge) graph.Graph { dg := simple.NewUndirectedGraph(0, math.Inf(1)) for u, e := range g { u := int64(u) - var at []Attribute + var at []encoding.Attribute if u < int64(len(attr)) { at = attr[u] } @@ -322,11 +323,11 @@ type graphAttributer struct { edge attributer } -type attributer []Attribute +type attributer []encoding.Attribute -func (a attributer) DOTAttributes() []Attribute { return a } +func (a attributer) Attributes() []encoding.Attribute { return a } -func (g graphAttributer) DOTAttributers() (graph, node, edge Attributer) { +func (g graphAttributer) DOTAttributers() (graph, node, edge encoding.Attributer) { return g.graph, g.node, g.edge } @@ -748,7 +749,7 @@ var encodeTests = []struct { }`, }, { - g: directedNodeAttrGraphFrom(powerMethodGraph, [][]Attribute{ + g: directedNodeAttrGraphFrom(powerMethodGraph, [][]encoding.Attribute{ 2: {{"fontsize", "16"}, {"shape", "ellipse"}}, 4: {}, }), @@ -775,7 +776,7 @@ var encodeTests = []struct { }`, }, { - g: undirectedNodeAttrGraphFrom(powerMethodGraph, [][]Attribute{ + g: undirectedNodeAttrGraphFrom(powerMethodGraph, [][]encoding.Attribute{ 2: {{"fontsize", "16"}, {"shape", "ellipse"}}, 4: {}, }), @@ -802,7 +803,7 @@ var encodeTests = []struct { }`, }, { - g: directedNamedIDNodeAttrGraphFrom(powerMethodGraph, [][]Attribute{ + g: directedNamedIDNodeAttrGraphFrom(powerMethodGraph, [][]encoding.Attribute{ 2: {{"fontsize", "16"}, {"shape", "ellipse"}}, 4: {}, }), @@ -829,7 +830,7 @@ var encodeTests = []struct { }`, }, { - g: undirectedNamedIDNodeAttrGraphFrom(powerMethodGraph, [][]Attribute{ + g: undirectedNamedIDNodeAttrGraphFrom(powerMethodGraph, [][]encoding.Attribute{ 0: nil, 1: nil, 2: {{"fontsize", "16"}, {"shape", "ellipse"}}, @@ -903,7 +904,7 @@ var encodeTests = []struct { }`, }, { - g: directedEdgeAttrGraphFrom(powerMethodGraph, map[edge][]Attribute{ + g: directedEdgeAttrGraphFrom(powerMethodGraph, map[edge][]encoding.Attribute{ {from: 0, to: 2}: {{"label", `"???"`}, {"style", "dashed"}}, {from: 2, to: 4}: {}, {from: 3, to: 4}: {{"color", "red"}}, @@ -931,7 +932,7 @@ var encodeTests = []struct { }`, }, { - g: undirectedEdgeAttrGraphFrom(powerMethodGraph, map[edge][]Attribute{ + g: undirectedEdgeAttrGraphFrom(powerMethodGraph, map[edge][]encoding.Attribute{ {from: 0, to: 2}: {{"label", `"???"`}, {"style", "dashed"}}, {from: 2, to: 4}: {}, {from: 3, to: 4}: {{"color", "red"}}, @@ -1004,7 +1005,7 @@ var encodeTests = []struct { }, { g: directedPortedAttrGraphFrom(powerMethodGraph, - [][]Attribute{ + [][]encoding.Attribute{ 2: {{"shape", "record"}, {"label", `"English|German"`}}, 4: {{"shape", "record"}, {"label", `"English|German"`}}, }, @@ -1044,7 +1045,7 @@ var encodeTests = []struct { }, { g: undirectedPortedAttrGraphFrom(powerMethodGraph, - [][]Attribute{ + [][]encoding.Attribute{ 2: {{"shape", "record"}, {"label", `"English|German"`}}, 4: {{"shape", "record"}, {"label", `"English|German"`}}, }, @@ -1092,7 +1093,7 @@ var encodeTests = []struct { // Handling graph attributes. { - g: graphAttributer{Graph: undirectedEdgeAttrGraphFrom(powerMethodGraph, map[edge][]Attribute{ + g: graphAttributer{Graph: undirectedEdgeAttrGraphFrom(powerMethodGraph, map[edge][]encoding.Attribute{ {from: 0, to: 2}: {{"label", `"???"`}, {"style", "dashed"}}, {from: 2, to: 4}: {}, {from: 3, to: 4}: {{"color", "red"}}, @@ -1120,13 +1121,13 @@ var encodeTests = []struct { }`, }, { - g: graphAttributer{Graph: undirectedEdgeAttrGraphFrom(powerMethodGraph, map[edge][]Attribute{ + g: graphAttributer{Graph: undirectedEdgeAttrGraphFrom(powerMethodGraph, map[edge][]encoding.Attribute{ {from: 0, to: 2}: {{"label", `"???"`}, {"style", "dashed"}}, {from: 2, to: 4}: {}, {from: 3, to: 4}: {{"color", "red"}}, }), - graph: []Attribute{{"rankdir", `"LR"`}}, - node: []Attribute{{"fontsize", "16"}, {"shape", "ellipse"}}, + graph: []encoding.Attribute{{"rankdir", `"LR"`}}, + node: []encoding.Attribute{{"fontsize", "16"}, {"shape", "ellipse"}}, }, want: `graph { diff --git a/graph/encoding/encoding.go b/graph/encoding/encoding.go new file mode 100644 index 00000000..dcfa089d --- /dev/null +++ b/graph/encoding/encoding.go @@ -0,0 +1,35 @@ +// Copyright ©2017 The gonum Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package encoding provides a common graph encoding API. +package encoding // import "gonum.org/v1/gonum/graph/encoding" + +import "gonum.org/v1/gonum/graph" + +// Builder is a graph that can have user-defined nodes and edges added. +type Builder interface { + graph.Graph + graph.Builder + // NewEdge adds a new edge from the source to the destination node to the + // graph, or returns the existing edge if already present. + NewEdge(from, to graph.Node) graph.Edge +} + +// UnmarshalerAttr is implemented by types that can unmarshal a graph +// attribute description of themselves. +type UnmarshalerAttr interface { + // UnmarshalAttr decodes a single attribute. + UnmarshalAttr(attr Attribute) error +} + +// Attributer defines graph.Node or graph.Edge values that can +// specify graph attributes. +type Attributer interface { + Attributes() []Attribute +} + +// Attribute is an encoded key value attribute pair use in graph encoding. +type Attribute struct { + Key, Value string +}