Skip to content

Cross-Shape Conversion

Three pure functions in the root package convert between data shapes:

// skip-validate
output.TableToGraph(t, opts...) → Graph
output.GraphToTree(g) → *TreeNode
output.GraphToTable(g) → *Table

These are projections — the input is never modified. Each returns a new value.

Generates one node per row, with edges between consecutive rows:

data := output.NewTable([]string{"Step", "Action"})
data.AddRow([]string{"1", "Build"})
data.AddRow([]string{"2", "Test"})
data.AddRow([]string{"3", "Deploy"})
g := output.TableToGraph(data)
// Node "1: Build" -> Node "2: Test" -> Node "3: Deploy"
// Now render as any graph format
dot, _ := graph.RenderDOT(g)
mermaid, _ := graph.RenderMermaid(g)
g := output.TableToGraph(data, output.WithGraphNodeLabelFunc(func(row []string) string {
return row[1] // Use the "Action" column as the label
}))

Follows edges from the root, building a hierarchical tree. Cycle-guarded.

g := output.TableToGraph(data)
root := output.GraphToTree(g)
ascii, _ := tree.RenderASCII(root)
// 1: Build
// └── 2: Test
// └── 3: Deploy

For disconnected graphs, only the first root’s subtree is included.

One row per node, with columns for ID and Label:

g := b.Build()
tbl := output.GraphToTable(g)
csv, _ := delimited.RenderCSV(tbl)
// Full pipeline: Table -> Graph -> Tree -> ASCII
data := output.NewTable([]string{"A", "B"})
data.AddRow([]string{"x", "1"})
data.AddRow([]string{"y", "2"})
g := output.TableToGraph(data)
root := output.GraphToTree(g)
out, _ := tree.RenderASCII(root)

The renderer-specific constructors still work (they wrap the same projections):

dot := graph.NewDOTFromTable(data) // Table -> DOT
mmd := graph.NewMermaidFromTable(data) // Table -> Mermaid
plantuml := plantuml.NewPlantUMLFromTable(data)
d2Diagram := d2.NewD2FromTable(data)
tree := tree.TreeRendererFromTable(data)