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

Render json output #7

Merged
merged 2 commits into from
Feb 23, 2023
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
31 changes: 31 additions & 0 deletions cmd/render.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,40 @@
package cmd

import (
"context"
"fmt"

"github.com/spf13/cobra"

"dep-tree/internal/dep_tree"
"dep-tree/internal/js"
"dep-tree/internal/tui"
)

func printStructured[T any](
ctx context.Context,
entrypoint string,
parserBuilder func(string) (dep_tree.NodeParser[T], error),
) error {
parser, err := parserBuilder(entrypoint)
if err != nil {
return err
}
_, dt, err := dep_tree.NewDepTree(ctx, parser)
if err != nil {
return err
}
output, err := dt.RenderStructured(parser.Display)
if err != nil {
return err
}
fmt.Println(string(output))
return nil
}

func RenderCmd() *cobra.Command {
var jsonFormat bool

cmd := &cobra.Command{
Use: "render <path/to/entrypoint.ext>",
Short: "Render the dependency tree starting from the provided entrypoint",
Expand All @@ -19,6 +44,10 @@ func RenderCmd() *cobra.Command {
entrypoint := args[0]

if endsWith(entrypoint, js.Extensions) {
if jsonFormat {
return printStructured(ctx, entrypoint, js.MakeJsParser)
}

return tui.Loop(
ctx,
entrypoint,
Expand All @@ -31,5 +60,7 @@ func RenderCmd() *cobra.Command {
},
}

cmd.Flags().BoolVar(&jsonFormat, "json", false, "render the dependency try in a machine readable json format")

return cmd
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"tree": {
"0": {
"1": null,
"2": {
"1": null
}
}
},
"circularDependencies": [],
"errors": {}
}
15 changes: 15 additions & 0 deletions internal/dep_tree/.render_test/Cyclic deps.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"tree": {
"0": {
"1": null
}
},
"circularDependencies": [
[
"1",
"2",
"1"
]
],
"errors": {}
}
32 changes: 32 additions & 0 deletions internal/dep_tree/.render_test/Simple.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"tree": {
"0": {
"1": {
"2": {
"3": null,
"4": {
"3": null
}
},
"4": {
"3": null
}
},
"2": {
"3": null,
"4": {
"3": null
}
},
"3": null
}
},
"circularDependencies": [
[
"3",
"4",
"3"
]
],
"errors": {}
}
27 changes: 27 additions & 0 deletions internal/dep_tree/.render_test/Some nodes have errors.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"tree": {
"0": {
"1": {
"2": {
"3": null,
"4": null
},
"4": null
},
"2": {
"3": null,
"4": null
},
"3": null
}
},
"circularDependencies": [],
"errors": {
"1": [
"4275 not present in spec"
],
"3": [
"1423 not present in spec"
]
}
}
8 changes: 8 additions & 0 deletions internal/dep_tree/.render_test/Some nodes have errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
0
├▷1
│ │
├─┼▷2
│ │ │
└─│─┼▷3
└─┴▷4
15 changes: 15 additions & 0 deletions internal/dep_tree/.render_test/Two in the same level.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"tree": {
"0": {
"1": {
"3": null
},
"2": {
"3": null
},
"3": null
}
},
"circularDependencies": [],
"errors": {}
}
18 changes: 18 additions & 0 deletions internal/dep_tree/.render_test/Weird cycle combination.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"tree": {
"0": {
"1": {
"2": null
}
}
},
"circularDependencies": [
[
"2",
"3",
"4",
"2"
]
],
"errors": {}
}
8 changes: 8 additions & 0 deletions internal/dep_tree/.render_test/Weird cycle combination.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
0
└▷1
│4◁───┐
││ │
└┴▷2 │
│ │
└▷3┘
10 changes: 9 additions & 1 deletion internal/dep_tree/level.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,17 @@ func (lc *LevelCalculator[T]) calculateLevel(
var level int
ctx, level = lc.calculateLevel(ctx, parent.Id, stack)
if level == cyclic {
cycleStack := []string{parent.Id}
for _, stackElement := range stack {
cycleStack = append(cycleStack, stackElement)
if stackElement == parent.Id {
break
}
}

lc.Cycles.Set(dep, DepCycle{
Cause: dep,
Stack: append([]string{parent.Id}, stack...),
Stack: cycleStack,
})
} else if level > maxLevel {
maxLevel = level
Expand Down
12 changes: 12 additions & 0 deletions internal/dep_tree/level_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,18 @@ func TestNode_Level(t *testing.T) {
ExpectedLevels: []int{0, 3, 4, 1, 2},
ExpectedCycles: [][]string{{"1", "2", "3", "4", "1"}},
},
{
Name: "Cycle 6",
Children: map[int][]int{
0: {1},
1: {2},
2: {3},
3: {4},
4: {2},
},
ExpectedLevels: []int{0, 1, 2, 3, 1},
ExpectedCycles: [][]string{{"2", "3", "4", "2"}},
},
{
Name: "Avoid same level",
Children: map[int][]int{
Expand Down
74 changes: 66 additions & 8 deletions internal/dep_tree/render.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package dep_tree

import (
"context"
"encoding/json"
"fmt"
"strconv"

"dep-tree/internal/board"
Expand All @@ -16,10 +17,7 @@ const ConnectorOriginNodeIdTag = "connectorOrigin"
const ConnectorDestinationNodeIdTag = "connectorDestination"
const NodeParentsTag = "nodeParents"

func (dt *DepTree[T]) Render(
ctx context.Context,
display func(node *graph.Node[T]) string,
) (context.Context, *board.Board, error) {
func (dt *DepTree[T]) Render(display func(node *graph.Node[T]) string) (*board.Board, error) {
b := board.MakeBoard()

lastLevel := -1
Expand Down Expand Up @@ -64,7 +62,7 @@ func (dt *DepTree[T]) Render(
},
)
if err != nil {
return ctx, nil, err
return nil, err
}
}

Expand All @@ -77,9 +75,69 @@ func (dt *DepTree[T]) Render(

err := b.AddConnector(n.Node.Id, child.Id, tags)
if err != nil {
return ctx, nil, err
return nil, err
}
}
}
return b, nil
}

type StructuredTree struct {
Tree map[string]interface{} `json:"tree" yaml:"tree"`
CircularDependencies [][]string `json:"circularDependencies" yaml:"circularDependencies"`
Errors map[string][]string `json:"errors" yaml:"errors"`
}

func (dt *DepTree[T]) makeStructuredTree(
node string,
display func(node *graph.Node[T]) string,
) map[string]interface{} {
var result map[string]interface{}
for _, child := range dt.Graph.Children(node) {
if _, ok := dt.Cycles.Get([2]string{node, child.Id}); ok {
continue
}
if result == nil {
result = make(map[string]interface{})
}
result[display(child)] = dt.makeStructuredTree(child.Id, display)
}
return result
}

func (dt *DepTree[T]) RenderStructured(display func(node *graph.Node[T]) string) ([]byte, error) {
root := dt.Graph.Get(dt.RootId)
if root == nil {
return nil, fmt.Errorf("could not retrieve root node from rootId %s", dt.RootId)
}

structuredTree := StructuredTree{
Tree: map[string]interface{}{
display(root): dt.makeStructuredTree(dt.RootId, display),
},
CircularDependencies: make([][]string, 0),
Errors: make(map[string][]string),
}

for _, cycle := range dt.Cycles.Keys() {
cycleDep, _ := dt.Cycles.Get(cycle)
renderedCycle := make([]string, len(cycleDep.Stack))
for i, cycleDepEntry := range cycleDep.Stack {
renderedCycle[i] = display(dt.Graph.Get(cycleDepEntry))
}
structuredTree.CircularDependencies = append(structuredTree.CircularDependencies, renderedCycle)
}

for _, node := range dt.Nodes {
if node.Node.Errors != nil && len(node.Node.Errors) > 0 {
erroredNode := display(dt.Graph.Get(node.Node.Id))
errors := make([]string, len(node.Node.Errors))
for i, err := range node.Node.Errors {
errors[i] = err.Error()
}
structuredTree.Errors[erroredNode] = errors
}
}
return ctx, b, nil

return json.MarshalIndent(structuredTree, "", " ")
}
Loading