github.com/mattevans/edward@v1.9.2/output/rendering_completion.go (about)

     1  package output
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"time"
     7  
     8  	"github.com/fatih/color"
     9  	"github.com/pkg/errors"
    10  	"github.com/mattevans/edward/tracker"
    11  )
    12  
    13  type CompletionRenderer struct {
    14  	indent     string
    15  	minSpacing int
    16  	targetTask tracker.Task
    17  }
    18  
    19  func NewCompletionRenderer(task tracker.Task) *CompletionRenderer {
    20  	return &CompletionRenderer{
    21  		indent:     "  ",
    22  		minSpacing: 3,
    23  		targetTask: task,
    24  	}
    25  }
    26  
    27  func (r *CompletionRenderer) Render(w io.Writer) error {
    28  	task := r.targetTask.Lineage()[0]
    29  	err := errors.WithStack(r.doRenderWithPrefix("", 0, w, task))
    30  	if err != nil {
    31  		return errors.WithStack(err)
    32  	}
    33  	return nil
    34  }
    35  
    36  func extendPrefix(prefix string, child tracker.Task) string {
    37  	if prefix == "" {
    38  		return child.Name()
    39  	}
    40  	return fmt.Sprintf("%v > %v", prefix, child.Name())
    41  }
    42  
    43  func (r *CompletionRenderer) doRenderWithPrefix(prefix string, maxNameWidth int, w io.Writer, task tracker.Task) error {
    44  	newMax := r.getLongestName(w, prefix, task)
    45  	if newMax > maxNameWidth {
    46  		maxNameWidth = newMax
    47  	}
    48  
    49  	children := task.Children()
    50  	for _, child := range children {
    51  		r.doRenderWithPrefix(extendPrefix(prefix, child), maxNameWidth, w, child)
    52  	}
    53  
    54  	if task != r.targetTask {
    55  		return nil
    56  	}
    57  
    58  	ts := task.State()
    59  	// Print name
    60  	nameFormat := fmt.Sprintf("%%-%ds", maxNameWidth+r.minSpacing)
    61  	fmt.Fprintf(w, nameFormat, fmt.Sprintf("%v:", prefix))
    62  
    63  	tmpOutput := color.Output
    64  	defer func() {
    65  		color.Output = tmpOutput
    66  	}()
    67  	color.Output = w
    68  	fmt.Fprint(w, "[")
    69  	switch ts {
    70  	case tracker.TaskStateSuccess:
    71  		color.Set(color.FgGreen)
    72  		fmt.Fprint(w, "OK")
    73  	case tracker.TaskStateFailed:
    74  		color.Set(color.FgRed)
    75  		fmt.Fprint(w, "Failed")
    76  	case tracker.TaskStateWarning:
    77  		color.Set(color.FgYellow)
    78  		fmt.Fprint(w, "Warning")
    79  	case tracker.TaskStatePending:
    80  		color.Set(color.FgCyan)
    81  		fmt.Fprint(w, "Pending")
    82  	default:
    83  		color.Set(color.FgCyan)
    84  		fmt.Fprint(w, "In Progress")
    85  	}
    86  	color.Unset()
    87  	fmt.Fprint(w, "]")
    88  	if ts != tracker.TaskStateInProgress && ts != tracker.TaskStatePending {
    89  		fmt.Fprintf(w, " (%v)", autoRoundTime(task.Duration()))
    90  	}
    91  	fmt.Fprintln(w)
    92  	if ts == tracker.TaskStateFailed || ts == tracker.TaskStateWarning {
    93  		for _, line := range task.Messages() {
    94  			fmt.Fprintln(w, line)
    95  		}
    96  	}
    97  
    98  	return nil
    99  }
   100  
   101  func (r *CompletionRenderer) getLongestName(w io.Writer, prefix string, task tracker.Task) int {
   102  	children := task.Children()
   103  	var max = len(prefix)
   104  	for _, child := range children {
   105  		childMax := r.getLongestName(w, extendPrefix(prefix, child), child)
   106  		if childMax > max {
   107  			max = childMax
   108  		}
   109  	}
   110  	return max
   111  }
   112  
   113  func autoRoundTime(d time.Duration) time.Duration {
   114  	if d > time.Hour {
   115  		return roundTime(d, time.Second)
   116  	}
   117  	if d > time.Minute {
   118  		return roundTime(d, time.Second)
   119  	}
   120  	if d > time.Second {
   121  		return roundTime(d, time.Millisecond)
   122  	}
   123  	if d > time.Millisecond {
   124  		return roundTime(d, time.Microsecond)
   125  	}
   126  	return d
   127  }
   128  
   129  // Based on the example at https://play.golang.org/p/QHocTHl8iR
   130  func roundTime(d, r time.Duration) time.Duration {
   131  	if r <= 0 {
   132  		return d
   133  	}
   134  	neg := d < 0
   135  	if neg {
   136  		d = -d
   137  	}
   138  	if m := d % r; m+m < r {
   139  		d = d - m
   140  	} else {
   141  		d = d + r - m
   142  	}
   143  	if neg {
   144  		return -d
   145  	}
   146  	return d
   147  }