github.com/anacrolix/torrent@v1.61.0/write-status.go (about)

     1  package torrent
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"fmt"
     7  	"io"
     8  	"iter"
     9  )
    10  
    11  func indented(w io.Writer) iter.Seq[statusWriter] {
    12  	return func(yield func(sw statusWriter) bool) {
    13  		iw := newIndentWriter(w, "  ")
    14  		sw := statusWriter{iw}
    15  		yield(sw)
    16  		sw.nl()
    17  	}
    18  }
    19  
    20  type indentWriter struct {
    21  	indent   string
    22  	w        bufio.Writer
    23  	indented bool
    24  }
    25  
    26  func newIndentWriter(w io.Writer, indent string) *indentWriter {
    27  	ret := &indentWriter{
    28  		indent: indent,
    29  	}
    30  	ret.w.Reset(w)
    31  	return ret
    32  }
    33  
    34  func (me *indentWriter) Write(b []byte) (n int, err error) {
    35  	for len(b) != 0 && err == nil {
    36  		if !me.indented {
    37  			me.w.WriteString(me.indent)
    38  			me.indented = true
    39  		}
    40  		b1 := b
    41  		i := bytes.IndexByte(b1, '\n')
    42  		if i != -1 {
    43  			b1 = b1[:i+1]
    44  		}
    45  		var n1 int
    46  		n1, err = me.w.Write(b1)
    47  		if n1 > 0 && n1 == i+1 {
    48  			me.indented = false
    49  		}
    50  		n += n1
    51  		b = b[n1:]
    52  	}
    53  	if err == nil {
    54  		err = me.w.Flush()
    55  	}
    56  	return
    57  }
    58  
    59  type statusWriter struct {
    60  	w io.Writer
    61  }
    62  
    63  func (me statusWriter) Write(b []byte) (n int, err error) {
    64  	return me.w.Write(b)
    65  }
    66  
    67  func (me *statusWriter) f(fmtStr string, args ...any) {
    68  	fmt.Fprintf(me, fmtStr, args...)
    69  }
    70  
    71  func (me *statusWriter) tab() *tableWriter {
    72  	tw := &tableWriter{
    73  		parent: me,
    74  	}
    75  	tw.statusWriter.w = &tw.buf
    76  	return tw
    77  }
    78  
    79  func (me *statusWriter) nl() {
    80  	fmt.Fprintln(me)
    81  }
    82  
    83  func (me *statusWriter) indented() iter.Seq[statusWriter] {
    84  	return indented(me)
    85  }
    86  
    87  type tableWriter struct {
    88  	parent *statusWriter
    89  	cells  [][]string
    90  	curRow []string
    91  	buf    bytes.Buffer
    92  	statusWriter
    93  }
    94  
    95  // Flushes/ends a column. Probably want to check we don't have buffered data before starting new
    96  // table elements.
    97  func (me *tableWriter) col() {
    98  	me.curRow = append(me.curRow, me.buf.String())
    99  	me.buf.Reset()
   100  }
   101  
   102  func (me *tableWriter) cols(args ...any) {
   103  	for _, a := range args {
   104  		fmt.Fprint(me, a)
   105  		me.col()
   106  	}
   107  }
   108  
   109  func (me *tableWriter) row() {
   110  	me.col()
   111  	me.cells = append(me.cells, me.curRow)
   112  	me.curRow = nil
   113  }
   114  
   115  func (me *tableWriter) getColWidths() (widths []int) {
   116  	// Imagine if we made this column-oriented...
   117  	for _, row := range me.cells {
   118  		for i, col := range row {
   119  			for i >= len(widths) {
   120  				widths = append(widths, 0)
   121  			}
   122  			widths[i] = max(widths[i], len(col))
   123  		}
   124  	}
   125  	return
   126  }
   127  
   128  func (me *tableWriter) end() {
   129  	widths := me.getColWidths()
   130  	for _, row := range me.cells {
   131  		// You could use an indentWriter here to maintain multi-line cells at the same indent.
   132  		for i, col := range row {
   133  			if i != 0 {
   134  				me.parent.w.Write([]byte{' '})
   135  			}
   136  			fmt.Fprintf(me.parent, "%-*s", widths[i], col)
   137  		}
   138  		me.parent.nl()
   139  	}
   140  	me.parent = nil
   141  }