Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JSONL output and interface refactoring #148

Merged
merged 8 commits into from
Aug 9, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 26 additions & 12 deletions prettyprinters/digraph_prettyprinter.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,32 @@ package prettyprinters

import "github.com/asteris-llc/converge/graph"

// A DigraphPrettyPrinter is a concrete implementation of some output format
// that can be used to prettyprint a Directed Graph.
type DigraphPrettyPrinter interface {
// Hey! Are you here to get output before any node but after all the nodes?
// Well, that doesn't exist right now. If you need it, you should add it.
// It used to be `StartNodeSection` and `FinishNodeSection`, so maybe call it
// `NodeSectionPrinter` and add those methods.

// BasePrinter is an implementation of some output format that can be used to
// prettyprint a Directed Graph. Printer is the zero value for these kinds of
// printers. To get any functionality, printers should implement one or more of
// the following interfaces.
type BasePrinter interface{}

// GraphPrinter should be implemented by printers that need to add a preamble or
// addendum to the pretty printed output
type GraphPrinter interface {
// StartPP should take a graph and shall return a string used to start the
// graph output, or an error which will be returned to the user.
StartPP(graph *graph.Graph) (Renderable, error)

// FinishPP will be given a graph and shall return a string used to finalize
// graph output, or an error which will be returned to the user.
FinishPP(graph *graph.Graph) (Renderable, error)
}

// SubgraphPrinter should be implemented by printers that need to control
// subgraph rendering (that is, grouping of nodes of any kind)
type SubgraphPrinter interface {
// MarkNode() is a function used to identify the boundaries of subgraphs
// within the larger graph. MarkNode() is called with a graph and the id of a
// node within the graph, and should return a *SubgraphMarker with the
Expand All @@ -48,22 +62,19 @@ type DigraphPrettyPrinter interface {
// and should return a string used to end a subgraph, or an error that will be
// returned to the user.
FinishSubgraph(graph *graph.Graph, subgraphID SubgraphID) (Renderable, error)
}

// StartNodeSection will be given a graph and should return a string used to
// start the node section, or an error that will be returned to the user.
StartNodeSection(graph *graph.Graph) (Renderable, error)

// FinishNodeSection will be given a graph and should return a string used to
// finish the node section in the final output, or an error that will be
// returned to the user.
FinishNodeSection(graph *graph.Graph) (Renderable, error)

// NodePrinter should be implemented by printers that want to render nodes
type NodePrinter interface {
// DrawNode will be called once for each node in the graph. The function will
// be given a graph and a string ID for the current node in the graph, and
// should return a string representing the node in the final output, or an
// error that will be returned to the user.
DrawNode(graph *graph.Graph, nodeID string) (Renderable, error)
}

// EdgeSectionPrinter should be implemented by printers that want to render edge sections (TODO: what are these?)
type EdgeSectionPrinter interface {
// StartEdgeSection will be given a graph and should return a string used to
// start the edge section, or an error that will be returned to the user.
StartEdgeSection(graph *graph.Graph) (Renderable, error)
Expand All @@ -72,7 +83,10 @@ type DigraphPrettyPrinter interface {
// finish the edge section in the final output, or an error that will be
// returned to the user.
FinishEdgeSection(graph *graph.Graph) (Renderable, error)
}

// EdgePrinter should be implemented by printers that want to render Edges
type EdgePrinter interface {
// DrawEdge will be called once for each edge in the graph. It is called with
// a graph, the ID of the source vertex, and the ID of the target vertex. It
// should return a string representing the edge in the final output, or an
Expand Down
85 changes: 32 additions & 53 deletions prettyprinters/graphviz/graphviz.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,13 @@ type PrintProvider interface {
// Given a graph entity, this function shall return a unique ID that will be
// used for the vertex. Note that this is not used for display, see
// VertexGetLabel() for information on vertex displays.
VertexGetID(GraphEntity) (pp.Renderable, error)
VertexGetID(GraphEntity) (pp.VisibleRenderable, error)

// Defines the label associated with the vertex. This will be the name
// applied to the vertex when it is drawn. Labels are stored in the vertex
// attribute list automatically. The 'label' parameter should therefore not
// be returned as part of VertexGetProperties().
VertexGetLabel(GraphEntity) (pp.Renderable, error)
VertexGetLabel(GraphEntity) (pp.VisibleRenderable, error)

// VertexGetProperties allows the PrintProvider to provide additional
// attributes to a given vertex. Note that the 'label' attribute is special
Expand All @@ -75,7 +75,7 @@ type PrintProvider interface {

// Defines the label associated with the edge formed between two graph
// entities. This will be the name applied to the edge when it is drawn.
EdgeGetLabel(GraphEntity, GraphEntity) (pp.Renderable, error)
EdgeGetLabel(GraphEntity, GraphEntity) (pp.VisibleRenderable, error)

// EdgeGetProperties allows the PrintProvider to provide additional
// attributes to a given edge. Note that the 'label' attribute is special
Expand Down Expand Up @@ -112,7 +112,6 @@ func DefaultOptions() Options {
// Printer is a DigraphPrettyPrinter implementation for drawing graphviz
// compatible DOT source code from a digraph.
type Printer struct {
pp.DigraphPrettyPrinter
options Options
printProvider PrintProvider
clusterIndex int
Expand Down Expand Up @@ -159,21 +158,26 @@ func (p *Printer) DrawNode(g *graph.Graph, id string) (pp.Renderable, error) {
graphValue := g.Get(id)
graphEntity := GraphEntity{id, graphValue}
vertexID, err := p.printProvider.VertexGetID(graphEntity)
if err != nil {
return pp.HiddenString(""), err
if err != nil || !vertexID.Visible() {
return pp.HiddenString(), err
}

vertexLabel, err := p.printProvider.VertexGetLabel(graphEntity)
if err != nil {
return pp.HiddenString(""), err
return pp.HiddenString(), err
}

attributes := p.printProvider.VertexGetProperties(graphEntity)
attributes = maybeSetProperty(attributes, "label", escapeNewline(vertexLabel))
attributeStr := buildAttributeString(attributes)
vertexID = escapeNewline(vertexID)
dotCode := pp.ApplyRenderable(vertexID, func(s string) string {
return fmt.Sprintf("\"%s\" %s;\n", s, attributeStr)
})
return dotCode, nil

return pp.SprintfRenderable(
true,
"\"%s\" %s;\n",
vertexID,
attributeStr,
), nil
}

// DrawEdge prints edge data in a fashion similar to DrawNode. It will return a
Expand All @@ -183,25 +187,27 @@ func (p *Printer) DrawEdge(g *graph.Graph, id1, id2 string) (pp.Renderable, erro
destEntity := GraphEntity{id2, g.Get(id2)}
sourceVertex, err := p.printProvider.VertexGetID(sourceEntity)
if err != nil {
return pp.HiddenString(""), err
return pp.HiddenString(), err
}
destVertex, err := p.printProvider.VertexGetID(destEntity)
if err != nil {
return pp.HiddenString(""), err
return pp.HiddenString(), err
}
label, err := p.printProvider.EdgeGetLabel(sourceEntity, destEntity)
if err != nil {
return pp.HiddenString(""), err
return pp.HiddenString(), err
}
attributes := p.printProvider.EdgeGetProperties(sourceEntity, destEntity)
maybeSetProperty(attributes, "label", escapeNewline(label))
edgeStr := pp.VisibleString(fmt.Sprintf("\"%s\" -> \"%s\" %s;\n",

edgeStr := fmt.Sprintf("\"%s\" -> \"%s\" %s;\n",
escapeNewline(sourceVertex),
escapeNewline(destVertex),
buildAttributeString(attributes)),
buildAttributeString(attributes),
)
visible := sourceVertex.Visible() && destVertex.Visible()
return pp.SetVisibility(edgeStr, visible), nil

return pp.RenderableString(edgeStr, visible), nil
}

// StartSubgraph returns a string with the beginning of the subgraph cluster
Expand All @@ -215,31 +221,6 @@ func (*Printer) FinishSubgraph(*graph.Graph, pp.SubgraphID) (pp.Renderable, erro
return pp.VisibleString("}\n"), nil
}

// StartNodeSection would begin the node section of the output; DOT does not
// require any special formatting for a node section so we return "".
func (p *Printer) StartNodeSection(*graph.Graph) (pp.Renderable, error) {
return pp.HiddenString(""), nil
}

// FinishNodeSection would finish the section started by StartNodeSection.
// Since DOT has no special formatting for starting/ending node sections we
// return "".
func (p *Printer) FinishNodeSection(*graph.Graph) (pp.Renderable, error) {
return pp.HiddenString(""), nil
}

// StartEdgeSection returns "" because DOT doesn't require anything special for
// an edge section.
func (p *Printer) StartEdgeSection(*graph.Graph) (pp.Renderable, error) {
return pp.HiddenString(""), nil
}

// FinishEdgeSection returns "" because DOT doesnt' require anything special for
// an edge section.
func (p *Printer) FinishEdgeSection(*graph.Graph) (pp.Renderable, error) {
return pp.HiddenString(""), nil
}

// StartPP begins the DOT output as an unnamed digraph.
func (p *Printer) StartPP(*graph.Graph) (pp.Renderable, error) {
attrs := p.GraphAttributes()
Expand Down Expand Up @@ -300,13 +281,13 @@ type BasicProvider struct{}

// VertexGetID provides a basic implementation that returns the %v quoted value
// of the node.
func (p BasicProvider) VertexGetID(e GraphEntity) (pp.Renderable, error) {
func (p BasicProvider) VertexGetID(e GraphEntity) (pp.VisibleRenderable, error) {
return pp.VisibleString(fmt.Sprintf("%v", e.Value)), nil
}

// VertexGetLabel provides a basic implementation that returns the %v quoted
// value of the node (as with VertexGetID).
func (p BasicProvider) VertexGetLabel(e GraphEntity) (pp.Renderable, error) {
func (p BasicProvider) VertexGetLabel(e GraphEntity) (pp.VisibleRenderable, error) {
return pp.VisibleString(fmt.Sprintf("%v", e.Value)), nil
}

Expand All @@ -317,8 +298,8 @@ func (p BasicProvider) VertexGetProperties(GraphEntity) PropertySet {
}

// EdgeGetLabel provides a basic implementation leaves the edge unlabeled.
func (p BasicProvider) EdgeGetLabel(GraphEntity, GraphEntity) (pp.Renderable, error) {
return pp.HiddenString(""), nil
func (p BasicProvider) EdgeGetLabel(GraphEntity, GraphEntity) (pp.VisibleRenderable, error) {
return pp.HiddenString(), nil
}

// EdgeGetProperties provides a basic implementation that returns an empty
Expand All @@ -334,26 +315,24 @@ func (p BasicProvider) SubgraphMarker(GraphEntity) SubgraphMarkerKey {

// VertexGetID provides a basic implementation that uses the ID from the graph
// to generate the VertexID.
func (p GraphIDProvider) VertexGetID(e GraphEntity) (pp.Renderable, error) {
func (p GraphIDProvider) VertexGetID(e GraphEntity) (pp.VisibleRenderable, error) {
return pp.VisibleString(e.Name), nil
}

// VertexGetLabel provides a basic implementation that uses the ID from the
// graph to generate the Vertex Label.
func (p GraphIDProvider) VertexGetLabel(e GraphEntity) (pp.Renderable, error) {
func (p GraphIDProvider) VertexGetLabel(e GraphEntity) (pp.VisibleRenderable, error) {
return pp.VisibleString(e.Name), nil
}

// Replace embedded newlines with their escaped form.
func escapeNewline(r pp.Renderable) pp.Renderable {
return pp.ApplyRenderable(r, func(s string) string {
return strings.Replace(s, "\n", "\\n", -1)
})
func escapeNewline(r pp.Renderable) pp.VisibleRenderable {
return pp.VisibleString(strings.Replace(r.String(), "\n", "\\n", -1))
}

// maybeSetProperty will add an attribute to the property set iff the value is
// renderable.
func maybeSetProperty(p PropertySet, key string, r pp.Renderable) PropertySet {
func maybeSetProperty(p PropertySet, key string, r pp.VisibleRenderable) PropertySet {
if r.Visible() {
p[key] = r.String()
}
Expand Down
58 changes: 10 additions & 48 deletions prettyprinters/graphviz/graphviz_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,44 +251,6 @@ func Test_FinishPP_ReturnsGraphvizStart(t *testing.T) {
assert.Equal(t, expected, actual.String())
}

// NB: The node and edge section callbacks are unnecessary for graphviz output
// so we just assert that they all return no errors and empty strings.
func Test_StartNodeSection_ReturnsEmptyString(t *testing.T) {
provider := defaultMockProvider()
printer := graphviz.New(graphviz.DefaultOptions(), provider)
expected := ""
actual, err := printer.StartNodeSection(emptyGraph)
assert.NoError(t, err)
assert.Equal(t, expected, actual.String())
}

func Test_FinishNodeSection_ReturnsEmptyString(t *testing.T) {
provider := defaultMockProvider()
printer := graphviz.New(graphviz.DefaultOptions(), provider)
expected := ""
actual, err := printer.FinishNodeSection(emptyGraph)
assert.NoError(t, err)
assert.Equal(t, expected, actual.String())
}

func Test_StartEdgeSection_ReturnsEmptyString(t *testing.T) {
provider := defaultMockProvider()
printer := graphviz.New(graphviz.DefaultOptions(), provider)
expected := ""
actual, err := printer.StartEdgeSection(emptyGraph)
assert.NoError(t, err)
assert.Equal(t, expected, actual.String())
}

func Test_FinishEdgeSection_ReturnsEmptyString(t *testing.T) {
provider := defaultMockProvider()
printer := graphviz.New(graphviz.DefaultOptions(), provider)
expected := ""
actual, err := printer.FinishEdgeSection(emptyGraph)
assert.NoError(t, err)
assert.Equal(t, expected, actual.String())
}

func Test_FinishSubgraph_ReturnsClosingBrace(t *testing.T) {
provider := defaultMockProvider()
printer := graphviz.New(graphviz.DefaultOptions(), provider)
Expand All @@ -302,14 +264,14 @@ type MockPrintProvider struct {
mock.Mock
}

func (m *MockPrintProvider) VertexGetID(i graphviz.GraphEntity) (pp.Renderable, error) {
func (m *MockPrintProvider) VertexGetID(i graphviz.GraphEntity) (pp.VisibleRenderable, error) {
args := m.Called(i)
return args.Get(0).(pp.Renderable), args.Error(1)
return args.Get(0).(pp.VisibleRenderable), args.Error(1)
}

func (m *MockPrintProvider) VertexGetLabel(i graphviz.GraphEntity) (pp.Renderable, error) {
func (m *MockPrintProvider) VertexGetLabel(i graphviz.GraphEntity) (pp.VisibleRenderable, error) {
args := m.Called(i)
return args.Get(0).(pp.Renderable), args.Error(1)
return args.Get(0).(pp.VisibleRenderable), args.Error(1)
}

func (m *MockPrintProvider) VertexGetProperties(i graphviz.GraphEntity) graphviz.PropertySet {
Expand All @@ -322,9 +284,9 @@ func (m *MockPrintProvider) SubgraphMarker(i graphviz.GraphEntity) graphviz.Subg
return args.Get(0).(graphviz.SubgraphMarkerKey)
}

func (m *MockPrintProvider) EdgeGetLabel(i, j graphviz.GraphEntity) (pp.Renderable, error) {
func (m *MockPrintProvider) EdgeGetLabel(i, j graphviz.GraphEntity) (pp.VisibleRenderable, error) {
args := m.Called(i, j)
return args.Get(0).(pp.Renderable), args.Error(1)
return args.Get(0).(pp.VisibleRenderable), args.Error(1)
}

func (m *MockPrintProvider) EdgeGetProperties(i, j graphviz.GraphEntity) graphviz.PropertySet {
Expand Down Expand Up @@ -352,7 +314,7 @@ func stubMarker(_ interface{}) graphviz.SubgraphMarkerKey {
}

func getDotNodeID(r pp.Renderable) string {
s, _ := pp.Render(r)
s := r.String()
trimmed := strings.TrimSpace(s)
firstChar := trimmed[0]
if firstChar == '\'' || firstChar == '"' {
Expand All @@ -363,7 +325,7 @@ func getDotNodeID(r pp.Renderable) string {
}

func getDotNodeLabel(r pp.Renderable) string {
s, _ := pp.Render(r)
s := r.String()
labelSplit := strings.Split(s, "label=")
if len(labelSplit) < 2 {
return ""
Expand Down Expand Up @@ -401,7 +363,7 @@ func get_kv(attr string) (string, string) {
}

func getDotAttributes(r pp.Renderable) map[string]string {
s, _ := pp.Render(r)
s := r.String()
results := make(map[string]string)
attributes, found := getAttributeSubstr(s)

Expand All @@ -419,7 +381,7 @@ func getDotAttributes(r pp.Renderable) map[string]string {
}

func parseDotEdge(r pp.Renderable) (string, string) {
e, _ := pp.Render(r)
e := r.String()
var dest string
ef := strings.Split(strings.TrimSpace(e), "->")
source := stripQuotes(strings.TrimSpace(ef[0]))
Expand Down
4 changes: 2 additions & 2 deletions prettyprinters/graphviz/providers/preparer.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ type PreparerProvider struct {

// VertexGetID returns a the Graph ID as the VertexID, possibly masking it
// depending on the vertex type and configuration
func (p PreparerProvider) VertexGetID(e graphviz.GraphEntity) (pp.Renderable, error) {
func (p PreparerProvider) VertexGetID(e graphviz.GraphEntity) (pp.VisibleRenderable, error) {
switch e.Value.(type) {
case *param.Preparer:
return pp.RenderableString(e.Name, p.ShowParams), nil
Expand All @@ -49,7 +49,7 @@ func (p PreparerProvider) VertexGetID(e graphviz.GraphEntity) (pp.Renderable, er
// Modules: Return 'Module' and the module name
// Params: Return 'name -> "value"'
// otherwise: Return 'name'
func (p PreparerProvider) VertexGetLabel(e graphviz.GraphEntity) (pp.Renderable, error) {
func (p PreparerProvider) VertexGetLabel(e graphviz.GraphEntity) (pp.VisibleRenderable, error) {
var name string

if e.Name == rootNodeID {
Expand Down
Loading