github.com/turbot/steampipe@v1.7.0-rc.0.0.20240517123944-7cef272d4458/pkg/control/controlexecute/dimension_color_map.go (about) 1 package controlexecute 2 3 import ( 4 "fmt" 5 ) 6 7 type DimensionColorGenerator struct { 8 Map map[string]map[string]uint8 9 startingRow uint8 10 startingColumn uint8 11 12 // state 13 allocatedColorCodes []uint8 14 forbiddenColumns map[uint8]bool 15 currentRow uint8 16 currentColumn uint8 17 } 18 19 const minColumn = 16 20 const maxColumn = 51 21 const minRow = 0 22 const maxRow = 5 23 24 // NewDimensionColorGenerator creates a new NewDimensionColorGenerator 25 func NewDimensionColorGenerator(startingRow, startingColumn uint8) (*DimensionColorGenerator, error) { 26 forbiddenColumns := map[uint8]bool{ 27 16: true, 17: true, 18: true, 19: true, 20: true, // red 28 22: true, 23: true, 27: true, 28: true, 29: true, //orange 29 34: true, 35: true, 36: true, 40: true, 41: true, 42: true, //green/orange 30 46: true, 47: true, 48: true, 49: true, // green 31 } 32 if startingColumn < minColumn || startingColumn > maxColumn { 33 return nil, fmt.Errorf("starting column must be between 16 and 51") 34 } 35 if startingRow < minRow || startingRow > maxRow { 36 return nil, fmt.Errorf("starting row must be between 0 and 5") 37 } 38 39 g := &DimensionColorGenerator{ 40 Map: make(map[string]map[string]uint8), 41 startingRow: startingRow, 42 startingColumn: startingColumn, 43 forbiddenColumns: forbiddenColumns, 44 } 45 g.reset() 46 return g, nil 47 } 48 49 func (g *DimensionColorGenerator) GetDimensionProperties() []string { 50 var res []string 51 for d := range g.Map { 52 res = append(res, d) 53 } 54 return res 55 } 56 57 func (g *DimensionColorGenerator) reset() { 58 // create the state map 59 g.currentRow = g.startingRow 60 g.currentColumn = g.startingColumn 61 // clear allocated colors 62 g.allocatedColorCodes = nil 63 } 64 65 func (g *DimensionColorGenerator) populate(e *ExecutionTree) { 66 for _, run := range e.ControlRuns { 67 for _, r := range run.Rows { 68 for _, d := range r.Dimensions { 69 if !g.hasDimensionValue(d) { 70 g.addDimensionValue(d) 71 } 72 } 73 } 74 } 75 } 76 77 func (g *DimensionColorGenerator) hasDimensionValue(dimension Dimension) bool { 78 dimensionMap, ok := g.Map[dimension.Key] 79 var gotValue bool 80 if ok { 81 // so we have a dimension map for this dimension 82 // - do we have a dimension color for this property value? 83 _, gotValue = dimensionMap[dimension.Value] 84 } 85 return gotValue 86 } 87 88 func (g *DimensionColorGenerator) addDimensionValue(d Dimension) { 89 // do we have a dimension map for this dimension property? 90 if g.Map[d.Key] == nil { 91 g.Map[d.Key] = make(map[string]uint8) 92 } 93 94 // store the color keyed by property VALUE 95 color := g.getNextColor() 96 97 g.Map[d.Key][d.Value] = color 98 } 99 100 func (g *DimensionColorGenerator) getNextColor() uint8 { 101 g.incrementCurrentColumn(2) 102 g.incrementCurrentRow(2) 103 104 // does this color clash, or is it forbidden 105 color := g.getCurrentColor() 106 origColor := color 107 for g.colorClashes(color) { 108 g.incrementCurrentColumn(1) 109 g.incrementCurrentRow(1) 110 color = g.getCurrentColor() 111 if color == origColor { 112 // we have tried them all reset and start from the first color 113 g.reset() 114 return g.getNextColor() 115 } 116 } 117 118 // store this color code 119 g.allocatedColorCodes = append(g.allocatedColorCodes, color) 120 return color 121 } 122 123 func (g *DimensionColorGenerator) getCurrentColor() uint8 { 124 return g.currentColumn + g.currentRow*36 125 } 126 127 func (g *DimensionColorGenerator) incrementCurrentRow(increment uint8) { 128 g.currentRow += increment 129 if g.currentRow > maxRow { 130 g.currentRow -= maxRow 131 } 132 } 133 134 func (g *DimensionColorGenerator) incrementCurrentColumn(increment uint8) { 135 g.currentColumn += increment 136 if g.currentColumn > maxColumn { 137 // reset to 16 138 g.currentColumn -= maxColumn - minColumn + 1 139 } 140 for ; g.forbiddenColumns[g.currentColumn]; g.currentColumn++ { 141 } 142 } 143 144 // check map our map of color indexes - if we are within 5 of any other element, skip this color 145 func (g *DimensionColorGenerator) colorClashes(color uint8) bool { 146 for _, a := range g.allocatedColorCodes { 147 if a == color { 148 return true 149 } 150 } 151 152 return false 153 }