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 }