-
Notifications
You must be signed in to change notification settings - Fork 263
/
group.go
177 lines (155 loc) · 5.43 KB
/
group.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
package controldisplay
import (
"fmt"
"log"
"strings"
"github.com/turbot/steampipe/pkg/control/controlexecute"
"github.com/turbot/steampipe/pkg/steampipeconfig/modconfig"
)
type GroupRenderer struct {
group *controlexecute.ResultGroup
// screen width
width int
maxFailedControls int
maxTotalControls int
resultTree *controlexecute.ExecutionTree
lastChild bool
parent *GroupRenderer
}
func NewGroupRenderer(group *controlexecute.ResultGroup, parent *GroupRenderer, maxFailedControls, maxTotalControls int, resultTree *controlexecute.ExecutionTree, width int) *GroupRenderer {
r := &GroupRenderer{
group: group,
parent: parent,
resultTree: resultTree,
maxFailedControls: maxFailedControls,
maxTotalControls: maxTotalControls,
width: width,
}
r.lastChild = r.isLastChild(group)
return r
}
// are we the last child of our parent?
// this affects the tree rendering
func (r GroupRenderer) isLastChild(group *controlexecute.ResultGroup) bool {
if group.Parent == nil || group.Parent.GroupItem == nil {
return true
}
siblings := group.Parent.GroupItem.GetChildren()
// get the name of the last sibling which has controls (or is a control)
var finalSiblingName string
for _, s := range siblings {
if b, ok := s.(*modconfig.Benchmark); ok {
// find the result group for this benchmark and see if it has controls
resultGroup := r.resultTree.Root.GetChildGroupByName(b.Name())
// if the result group has not controls, we will not find it in the result tree
if resultGroup == nil || resultGroup.ControlRunCount() == 0 {
continue
}
}
// store the name of this sibling
finalSiblingName = s.Name()
}
res := group.GroupItem.Name() == finalSiblingName
return res
}
// the indent for blank lines
// same as for (not last) children
func (r GroupRenderer) blankLineIndent() string {
return r.childIndent()
}
// the indent for group heading
func (r GroupRenderer) headingIndent() string {
// if this is the first displayed node, no indent
if r.parent == nil || r.parent.group.GroupId == controlexecute.RootResultGroupName {
return ""
}
// as our parent for the indent for a group
i := r.parent.childGroupIndent()
return i
}
// the indent for child groups/controls (which are not the final child)
// include the tree '|'
func (r GroupRenderer) childIndent() string {
return r.parentIndent() + "| "
}
// the indent for the FINAL child groups/controls
// just a space
func (r GroupRenderer) lastChildIndent() string {
return r.parentIndent() + " "
}
// the indent for child groups - our parent indent with the group expander "+ "
func (r GroupRenderer) childGroupIndent() string {
return r.parentIndent() + "+ "
}
// get the indent inherited from our parent
// - this will depend on whether we are our parents last child
func (r GroupRenderer) parentIndent() string {
if r.parent == nil || r.parent.group.GroupId == controlexecute.RootResultGroupName {
return ""
}
if r.lastChild {
return r.parent.lastChildIndent()
}
return r.parent.childIndent()
}
func (r GroupRenderer) Render() string {
if r.width <= 0 {
// this should never happen, since the minimum width is set by the formatter
log.Printf("[WARN] group renderer has width of %d\n", r.width)
return ""
}
if r.group.GroupId == controlexecute.RootResultGroupName {
return r.renderRootResultGroup()
}
groupHeadingRenderer := NewGroupHeadingRenderer(
r.group.Title,
r.group.Summary.Status.FailedCount(),
r.group.Summary.Status.TotalCount(),
r.maxFailedControls,
r.maxTotalControls,
r.width,
r.headingIndent())
// render this group header
tableStrings := append([]string{},
groupHeadingRenderer.Render(),
// newline after group
fmt.Sprintf("%s", ControlColors.Indent(r.blankLineIndent())))
// now render the group children, in the order they are specified
childStrings := r.renderChildren()
tableStrings = append(tableStrings, childStrings...)
return strings.Join(tableStrings, "\n")
}
// for root result group, there will either be one or more groups, or one or more control runs
// there will be no order specified so just loop through them
func (r GroupRenderer) renderRootResultGroup() string {
var resultStrings = make([]string, len(r.group.Groups)+len(r.group.ControlRuns))
for i, group := range r.group.Groups {
groupRenderer := NewGroupRenderer(group, &r, r.maxFailedControls, r.maxTotalControls, r.resultTree, r.width)
resultStrings[i] = groupRenderer.Render()
}
for i, run := range r.group.ControlRuns {
controlRenderer := NewControlRenderer(run, &r)
resultStrings[i] = controlRenderer.Render()
}
return strings.Join(resultStrings, "\n")
}
// render the children of this group, in the order they are specified in the hcl
func (r GroupRenderer) renderChildren() []string {
children := r.group.GroupItem.GetChildren()
var childStrings []string
for _, child := range children {
if control, ok := child.(*modconfig.Control); ok {
// get Result group with a matching name
if run := r.group.GetControlRunByName(control.Name()); run != nil {
controlRenderer := NewControlRenderer(run, &r)
childStrings = append(childStrings, controlRenderer.Render())
}
} else {
if childGroup := r.group.GetGroupByName(child.Name()); childGroup != nil {
groupRenderer := NewGroupRenderer(childGroup, &r, r.maxFailedControls, r.maxTotalControls, r.resultTree, r.width)
childStrings = append(childStrings, groupRenderer.Render())
}
}
}
return childStrings
}