github.com/hazelops/ize@v1.1.12-0.20230915191306-97d7c0e48f11/pkg/terminal/glint.go (about)

     1  package terminal
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"fmt"
     7  	"io"
     8  	"os"
     9  	"strings"
    10  	"text/tabwriter"
    11  	"time"
    12  
    13  	"github.com/mitchellh/go-glint"
    14  	"github.com/olekukonko/tablewriter"
    15  )
    16  
    17  type glintUI struct {
    18  	d *glint.Document
    19  }
    20  
    21  func GlintUI(ctx context.Context) UI {
    22  	result := &glintUI{
    23  		d: glint.New(),
    24  	}
    25  
    26  	go result.d.Render(ctx)
    27  
    28  	return result
    29  }
    30  
    31  func (ui *glintUI) Close() error {
    32  	return ui.d.Close()
    33  }
    34  
    35  func (ui *glintUI) Input(input *Input) (string, error) {
    36  	return "", ErrNonInteractive
    37  }
    38  
    39  // Interactive implements UI
    40  func (ui *glintUI) Interactive() bool {
    41  	// TODO(mitchellh): We can make this interactive later but Glint itself
    42  	// doesn't support input yet. We can pause the document, do some input,
    43  	// then resume potentially.
    44  	return false
    45  }
    46  
    47  // Output implements UI
    48  func (ui *glintUI) Output(msg string, raw ...interface{}) {
    49  	msg, style, _ := Interpret(msg, raw...)
    50  
    51  	var cs []glint.StyleOption
    52  	switch style {
    53  	case HeaderStyle:
    54  		cs = append(cs, glint.Bold())
    55  		msg = "\n» " + msg
    56  	case ErrorStyle, ErrorBoldStyle:
    57  		cs = append(cs, glint.Color("lightRed"))
    58  		if style == ErrorBoldStyle {
    59  			cs = append(cs, glint.Bold())
    60  		}
    61  
    62  		ui.d.Append(glint.Finalize(
    63  			glint.Style(
    64  				glint.Text("✗ "+msg),
    65  				cs...,
    66  			),
    67  		))
    68  
    69  		return
    70  
    71  	case WarningStyle, WarningBoldStyle:
    72  		cs = append(cs, glint.Color("lightYellow"))
    73  		if style == WarningBoldStyle {
    74  			cs = append(cs, glint.Bold())
    75  		}
    76  
    77  	case SuccessStyle, SuccessBoldStyle:
    78  		cs = append(cs, glint.Color("lightGreen"))
    79  		if style == SuccessBoldStyle {
    80  			cs = append(cs, glint.Bold())
    81  		}
    82  
    83  		msg = colorSuccess.Sprint(msg)
    84  
    85  	case InfoStyle:
    86  		lines := strings.Split(msg, "\n")
    87  		for i, line := range lines {
    88  			lines[i] = colorInfo.Sprintf("  %s", line)
    89  		}
    90  
    91  		msg = strings.Join(lines, "\n")
    92  	}
    93  
    94  	ui.d.Append(glint.Finalize(
    95  		glint.Style(
    96  			glint.Text(msg),
    97  			cs...,
    98  		),
    99  	))
   100  
   101  	time.Sleep(ui.minimumLag())
   102  }
   103  
   104  // NamedValues implements UI
   105  func (ui *glintUI) NamedValues(rows []NamedValue, opts ...Option) {
   106  	cfg := &config{}
   107  	for _, opt := range opts {
   108  		opt(cfg)
   109  	}
   110  
   111  	var buf bytes.Buffer
   112  	tr := tabwriter.NewWriter(&buf, 1, 8, 0, ' ', tabwriter.AlignRight)
   113  	for _, row := range rows {
   114  		switch v := row.Value.(type) {
   115  		case int, uint, int8, uint8, int16, uint16, int32, uint32, int64, uint64:
   116  			fmt.Fprintf(tr, "  %s: \t%d\n", row.Name, row.Value)
   117  		case float32, float64:
   118  			fmt.Fprintf(tr, "  %s: \t%f\n", row.Name, row.Value)
   119  		case bool:
   120  			fmt.Fprintf(tr, "  %s: \t%v\n", row.Name, row.Value)
   121  		case string:
   122  			if v == "" {
   123  				continue
   124  			}
   125  			fmt.Fprintf(tr, "  %s: \t%s\n", row.Name, row.Value)
   126  		default:
   127  			fmt.Fprintf(tr, "  %s: \t%s\n", row.Name, row.Value)
   128  		}
   129  	}
   130  	tr.Flush()
   131  
   132  	// We want to trim the trailing newline
   133  	text := buf.String()
   134  	if len(text) > 0 && text[len(text)-1] == '\n' {
   135  		text = text[:len(text)-1]
   136  	}
   137  
   138  	ui.d.Append(glint.Finalize(glint.Text(text)))
   139  }
   140  
   141  // OutputWriters implements UI
   142  func (ui *glintUI) OutputWriters() (io.Writer, io.Writer, error) {
   143  	return os.Stdout, os.Stderr, nil
   144  }
   145  
   146  // Status implements UI
   147  func (ui *glintUI) Status() Status {
   148  	st := newGlintStatus()
   149  	ui.d.Append(st)
   150  	return st
   151  }
   152  
   153  func (ui *glintUI) StepGroup() StepGroup {
   154  	ctx, cancel := context.WithCancel(context.Background())
   155  	sg := &glintStepGroup{ctx: ctx, cancel: cancel}
   156  	ui.d.Append(sg)
   157  	return sg
   158  }
   159  
   160  // Table implements UI
   161  func (ui *glintUI) Table(tbl *Table, opts ...Option) {
   162  	// Build our config and set our options
   163  	cfg := &config{}
   164  	for _, opt := range opts {
   165  		opt(cfg)
   166  	}
   167  
   168  	var buf bytes.Buffer
   169  	table := tablewriter.NewWriter(&buf)
   170  	table.SetHeader(tbl.Headers)
   171  	table.SetBorder(false)
   172  	table.SetAutoWrapText(false)
   173  
   174  	if cfg.Style == "Simple" {
   175  		// Format the table without borders, simple output
   176  
   177  		table.SetAutoFormatHeaders(true)
   178  		table.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
   179  		table.SetAlignment(tablewriter.ALIGN_LEFT)
   180  		table.SetCenterSeparator("")
   181  		table.SetColumnSeparator("")
   182  		table.SetRowSeparator("")
   183  		table.SetHeaderLine(false)
   184  		table.SetTablePadding("\t") // pad with tabs
   185  		table.SetNoWhiteSpace(true)
   186  	}
   187  
   188  	for _, row := range tbl.Rows {
   189  		colors := make([]tablewriter.Colors, len(row))
   190  		entries := make([]string, len(row))
   191  
   192  		for i, ent := range row {
   193  			entries[i] = ent.Value
   194  
   195  			color, ok := colorMapping[ent.Color]
   196  			if ok {
   197  				colors[i] = tablewriter.Colors{color}
   198  			}
   199  		}
   200  
   201  		table.Rich(entries, colors)
   202  	}
   203  
   204  	table.Render()
   205  
   206  	ui.d.Append(glint.Finalize(glint.Text(buf.String())))
   207  }
   208  
   209  func (ui *glintUI) minimumLag() time.Duration {
   210  	return time.Millisecond * 100
   211  }