graph/encoding: provide attribute handling out of the box
MODIFY
graph/encoding/dot/decode_test.go
graph/encoding/dot/decode_test.go
@@ -532,7 +532,13 @@ type dotDirectedGraph struct {
// newDotDirectedGraph returns a new directed capable of creating user-defined
// nodes and edges.
func newDotDirectedGraph() *dotDirectedGraph {
- return &dotDirectedGraph{DirectedGraph: simple.NewDirectedGraph()}
+ return &dotDirectedGraph{
+ DirectedGraph: simple.NewDirectedGraph(),
+
+ graph: &encoding.Attributes{},
+ node: &encoding.Attributes{},
+ edge: &encoding.Attributes{},
+ }
}
// NewNode returns a new node with a unique node ID for the graph.
@@ -552,7 +558,7 @@ func (g *dotDirectedGraph) DOTAttributers() (graph, node, edge encoding.Attribut
// DOTAttributeSetters implements the dot.AttributeSetters interface.
func (g *dotDirectedGraph) DOTAttributeSetters() (graph, node, edge encoding.AttributeSetter) {
- return &g.graph, &g.node, &g.edge
+ return g.graph, g.node, g.edge
}
// SetDOTID sets the DOT ID of the graph.
@@ -579,7 +585,13 @@ type dotUndirectedGraph struct {
// newDotUndirectedGraph returns a new undirected capable of creating user-
// defined nodes and edges.
func newDotUndirectedGraph() *dotUndirectedGraph {
- return &dotUndirectedGraph{UndirectedGraph: simple.NewUndirectedGraph()}
+ return &dotUndirectedGraph{
+ UndirectedGraph: simple.NewUndirectedGraph(),
+
+ graph: &encoding.Attributes{},
+ node: &encoding.Attributes{},
+ edge: &encoding.Attributes{},
+ }
}
// NewNode adds a new node with a unique node ID to the graph.
@@ -599,7 +611,7 @@ func (g *dotUndirectedGraph) DOTAttributers() (graph, node, edge encoding.Attrib
// DOTUnmarshalerAttrs implements the dot.UnmarshalerAttrs interface.
func (g *dotUndirectedGraph) DOTAttributeSetters() (graph, node, edge encoding.AttributeSetter) {
- return &g.graph, &g.node, &g.edge
+ return g.graph, g.node, g.edge
}
// SetDOTID sets the DOT ID of the graph.
@@ -705,15 +717,9 @@ func (e *dotEdge) ToPort() (port, compass string) {
return e.ToPortLabels.Port, e.ToPortLabels.Compass
}
-// attributes is a helper for global attributes.
-type attributes []encoding.Attribute
-
-func (a attributes) Attributes() []encoding.Attribute {
- return []encoding.Attribute(a)
-}
-func (a *attributes) SetAttribute(attr encoding.Attribute) error {
- *a = append(*a, attr)
- return nil
+type attributes interface {
+ encoding.Attributer
+ encoding.AttributeSetter
}
const undirectedSelfLoopGraph = `graph {

MODIFY
graph/encoding/dot/encode_test.go
graph/encoding/dot/encode_test.go
@@ -321,10 +321,6 @@ type graphAttributer struct {
edge encoding.Attributer
}
-type attributer []encoding.Attribute
-
-func (a attributer) Attributes() []encoding.Attribute { return a }
-
func (g graphAttributer) DOTAttributers() (graph, node, edge encoding.Attributer) {
return g.graph, g.node, g.edge
}
@@ -1192,8 +1188,8 @@ var encodeTests = []struct {
{from: 2, to: 4}: {},
{from: 3, to: 4}: {{Key: "color", Value: "red"}},
}),
- graph: attributer{{Key: "rankdir", Value: `"LR"`}},
- node: attributer{{Key: "fontsize", Value: "16"}, {Key: "shape", Value: "ellipse"}},
+ graph: &encoding.Attributes{{Key: "rankdir", Value: `"LR"`}},
+ node: &encoding.Attributes{{Key: "fontsize", Value: "16"}, {Key: "shape", Value: "ellipse"}},
edge: nil,
},

MODIFY
graph/encoding/encoding.go
graph/encoding/encoding.go
@@ -34,3 +34,36 @@ type Attributer interface {
type Attribute struct {
Key, Value string
}
+
+// Attributes is a helper type providing simple attribute handling.
+type Attributes []Attribute
+
+// Attributes returns all of the receiver's attributes.
+func (a *Attributes) Attributes() []Attribute {
+ return *a
+}
+
+// SetAttribute sets attr in the receiver. Calling SetAttribute with an
+// Attribute with a Key that is in the collection replaces the existing
+// value and calling with an empty Value removes the attribute from the
+// collection if it exists. SetAttribute always returns nil.
+func (a *Attributes) SetAttribute(attr Attribute) error {
+ if attr.Key == "" {
+ return nil
+ }
+ for i, v := range *a {
+ if v.Key == attr.Key {
+ if attr.Value == "" {
+ (*a)[i] = (*a)[len(*a)-1]
+ *a = (*a)[:len(*a)-1]
+ return nil
+ }
+ (*a)[i].Value = attr.Value
+ return nil
+ }
+ }
+ if attr.Value != "" {
+ *a = append(*a, attr)
+ }
+ return nil
+}

graph/encoding: provide attribute handling out of the box