github.com/turbot/steampipe@v1.7.0-rc.0.0.20240517123944-7cef272d4458/pkg/control/controldisplay/control.go (about)

     1  package controldisplay
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  
     7  	"github.com/spf13/viper"
     8  	typehelpers "github.com/turbot/go-kit/types"
     9  	"github.com/turbot/steampipe/pkg/constants"
    10  	"github.com/turbot/steampipe/pkg/control/controlexecute"
    11  )
    12  
    13  type ControlRenderer struct {
    14  	run               *controlexecute.ControlRun
    15  	parent            *GroupRenderer
    16  	maxFailedControls int
    17  	maxTotalControls  int
    18  	// screen width
    19  	width          int
    20  	colorGenerator *controlexecute.DimensionColorGenerator
    21  	lastChild      bool
    22  }
    23  
    24  func NewControlRenderer(run *controlexecute.ControlRun, parent *GroupRenderer) *ControlRenderer {
    25  	r := &ControlRenderer{
    26  		run:               run,
    27  		parent:            parent,
    28  		maxFailedControls: parent.maxFailedControls,
    29  		maxTotalControls:  parent.maxTotalControls,
    30  		colorGenerator:    parent.resultTree.DimensionColorGenerator,
    31  		width:             parent.width,
    32  	}
    33  	r.lastChild = r.isLastChild()
    34  	return r
    35  }
    36  
    37  // are we the last child of our parent?
    38  // this affects the tree rendering
    39  func (r ControlRenderer) isLastChild() bool {
    40  	if r.parent.group == nil || r.parent.group.GroupItem == nil {
    41  		return true
    42  	}
    43  	siblings := r.parent.group.GroupItem.GetChildren()
    44  	return r.run.Control.Name() == siblings[len(siblings)-1].Name()
    45  }
    46  
    47  // get the indent inherited from our parent
    48  // - this will depend on whether we are our parents last child
    49  func (r ControlRenderer) parentIndent() string {
    50  	if r.lastChild {
    51  		return r.parent.lastChildIndent()
    52  	}
    53  	return r.parent.childIndent()
    54  }
    55  
    56  // indent before first result
    57  func (r ControlRenderer) preResultIndent() string {
    58  	// when we do not have any rows, do not add a '|' to indent
    59  	if viper.GetBool(constants.ArgDryRun) || len(r.run.Rows) == 0 {
    60  		return r.parentIndent()
    61  	}
    62  	return r.parentIndent() + "| "
    63  }
    64  
    65  // indent before first result
    66  func (r ControlRenderer) resultIndent() string {
    67  	return r.parentIndent()
    68  }
    69  
    70  // indent after last result
    71  func (r ControlRenderer) postResultIndent() string {
    72  	return r.parentIndent()
    73  }
    74  
    75  func (r ControlRenderer) Render() string {
    76  	var controlStrings []string
    77  	// use group heading renderer to render the control title and counts
    78  	controlHeadingRenderer := NewGroupHeadingRenderer(typehelpers.SafeString(r.run.Control.Title),
    79  		r.run.Summary.FailedCount(),
    80  		r.run.Summary.TotalCount(),
    81  		r.maxFailedControls,
    82  		r.maxTotalControls,
    83  		r.width,
    84  		r.parent.childGroupIndent())
    85  
    86  	// set the severity on the heading renderer
    87  	controlHeadingRenderer.severity = typehelpers.SafeString(r.run.Control.Severity)
    88  
    89  	// get formatted indents
    90  	formattedPostResultIndent := fmt.Sprintf("%s", ControlColors.Indent(r.postResultIndent()))
    91  	formattedPreResultIndent := fmt.Sprintf("%s", ControlColors.Indent(r.preResultIndent()))
    92  
    93  	controlStrings = append(controlStrings,
    94  		controlHeadingRenderer.Render(),
    95  		// newline after control heading
    96  		formattedPreResultIndent)
    97  
    98  	// if the control is in error, render an error
    99  	if r.run.GetError() != nil {
   100  		errorRenderer := NewErrorRenderer(r.run.GetError(), r.width, r.parentIndent())
   101  		controlStrings = append(controlStrings,
   102  			errorRenderer.Render(),
   103  			// newline after error
   104  			formattedPostResultIndent)
   105  	}
   106  
   107  	// now render the results (if any)
   108  	var resultStrings []string
   109  	for _, row := range r.run.Rows {
   110  		resultRenderer := NewResultRenderer(
   111  			row.Status,
   112  			row.Reason,
   113  			row.Dimensions,
   114  			r.colorGenerator,
   115  			r.width,
   116  			r.resultIndent())
   117  		// the result renderer may not render the result - in quiet mode only failures are rendered
   118  		if resultString := resultRenderer.Render(); resultString != "" {
   119  			resultStrings = append(resultStrings, resultString)
   120  		}
   121  	}
   122  
   123  	// newline after results
   124  	if len(resultStrings) > 0 {
   125  		controlStrings = append(controlStrings, resultStrings...)
   126  		if len(r.run.Rows) > 0 || r.run.GetError() != nil {
   127  			controlStrings = append(controlStrings, formattedPostResultIndent)
   128  		}
   129  	}
   130  
   131  	return strings.Join(controlStrings, "\n")
   132  }