asteris-llc/converge

View on GitHub
prettyprinters/prettyprinter.go

Summary

Maintainability
A
2 hrs
Test Coverage
// Copyright © 2016 Asteris, LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package prettyprinters

import (
    "bytes"
    "strings"

    "github.com/asteris-llc/converge/graph"
    "golang.org/x/net/context"
)

// New creates a new prettyprinter instance from the specified
// DigraphPrettyPrinter instance.
func New(p BasePrinter) Printer {
    return Printer{pp: p}
}

// Show will take the given graph and return a string representing the text
// output of the graph according to the associated prettyprinter.  If an error
// is returned at any stage of the prettyprinting process it is returned
// unmodified to the user.
func (p Printer) Show(ctx context.Context, g *graph.Graph) (string, error) {
    outputBuffer := new(bytes.Buffer)
    write := func(s Renderable) { outputBuffer.WriteString(s.String()) }

    subgraphs := makeSubgraphMap()
    p.loadSubgraphs(ctx, g, subgraphs)
    rootNodes := subgraphs[nil].Nodes

    graphPrinter, gpOK := p.pp.(GraphPrinter)
    if gpOK {
        if str, err := graphPrinter.StartPP(g); err == nil {
            outputBuffer.WriteString(str.String())
        } else {
            return "", err
        }
    }

    nodePrinter, npOK := p.pp.(NodePrinter)
    if npOK {
        for _, node := range rootNodes {
            if str, err := nodePrinter.DrawNode(g, node); err == nil {
                if !strings.Contains(str.String(), "error in dependency") {
                    write(str)
                }
            } else {
                return "", err
            }
        }
    }

    for graphID, graph := range subgraphs {
        if graphID == SubgraphBottomID {
            continue
        }

        if str, err := p.drawSubgraph(g, graphID, graph); err == nil {
            write(str)
        } else {
            return "", err
        }
    }

    if str, err := p.drawEdges(g); err == nil {
        write(str)
    } else {
        return "", err
    }

    if gpOK {
        if str, err := graphPrinter.FinishPP(g); err == nil {
            write(str)
        } else {
            return "", err
        }
    }

    return outputBuffer.String(), nil
}

func (p Printer) drawEdges(g *graph.Graph) (Renderable, error) {
    edgePrinter, epOK := p.pp.(EdgePrinter)
    edgeSectionPrinter, espOK := p.pp.(EdgeSectionPrinter)

    if !epOK && !espOK {
        return HiddenString(), nil
    }

    outputBuffer := new(bytes.Buffer)
    write := func(s Renderable) { outputBuffer.WriteString(s.String()) }

    edges := g.Edges()
    if espOK {
        if str, err := edgeSectionPrinter.StartEdgeSection(g); err == nil {
            write(str)
        } else {
            return nil, err
        }
    }

    if epOK {
        for _, edge := range edges {
            if str, err := edgePrinter.DrawEdge(g, edge.Source, edge.Dest); err == nil {
                write(str)
            } else {
                return nil, err
            }
        }
    }

    if espOK {
        if str, err := edgeSectionPrinter.FinishEdgeSection(g); err == nil {
            write(str)
        } else {
            return nil, err
        }
    }

    return outputBuffer, nil
}

// Printer is the top-level structure for a pretty printer.
type Printer struct {
    pp BasePrinter
}