initial commit
This commit is contained in:
		
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					table_header_test.go
 | 
				
			||||||
							
								
								
									
										7
									
								
								col_spec.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								col_spec.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,7 @@
 | 
				
			|||||||
 | 
					package tprint
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ColSpec struct {
 | 
				
			||||||
 | 
						Name      string
 | 
				
			||||||
 | 
						MaxWidth  int
 | 
				
			||||||
 | 
						MaxHeight int
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										17
									
								
								go.mod
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								go.mod
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					module git.tordarus.net/tordarus/tprint
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					go 1.19
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					require (
 | 
				
			||||||
 | 
						git.tordarus.net/tordarus/gmath v0.0.7
 | 
				
			||||||
 | 
						git.tordarus.net/tordarus/slices v0.0.14
 | 
				
			||||||
 | 
						github.com/fatih/color v1.18.0
 | 
				
			||||||
 | 
						github.com/mattn/go-runewidth v0.0.16
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					require (
 | 
				
			||||||
 | 
						github.com/mattn/go-colorable v0.1.13 // indirect
 | 
				
			||||||
 | 
						github.com/mattn/go-isatty v0.0.20 // indirect
 | 
				
			||||||
 | 
						github.com/rivo/uniseg v0.2.0 // indirect
 | 
				
			||||||
 | 
						golang.org/x/sys v0.25.0 // indirect
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
							
								
								
									
										19
									
								
								go.sum
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								go.sum
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,19 @@
 | 
				
			|||||||
 | 
					git.tordarus.net/tordarus/gmath v0.0.7 h1:tR48idt9AUL0r556ww3ZxByTKJEr6NWCTlhl2ihzYxQ=
 | 
				
			||||||
 | 
					git.tordarus.net/tordarus/gmath v0.0.7/go.mod h1:mO7aPlvNrGVE9UFXEuuACjZgMDsM63l3OcQy6xSQnoE=
 | 
				
			||||||
 | 
					git.tordarus.net/tordarus/slices v0.0.14 h1:Jy1VRMs777WewJ7mxTgjyQIMm/Zr+co18/XoQ01YZ3A=
 | 
				
			||||||
 | 
					git.tordarus.net/tordarus/slices v0.0.14/go.mod h1:RgE7A1aSAezIvPUgcbUuMHu0q4xGKoRevT+DC0eJmwI=
 | 
				
			||||||
 | 
					github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
 | 
				
			||||||
 | 
					github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
 | 
				
			||||||
 | 
					github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
 | 
				
			||||||
 | 
					github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
 | 
				
			||||||
 | 
					github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
 | 
				
			||||||
 | 
					github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
 | 
				
			||||||
 | 
					github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
 | 
				
			||||||
 | 
					github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
 | 
				
			||||||
 | 
					github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
 | 
				
			||||||
 | 
					github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
 | 
				
			||||||
 | 
					github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
 | 
				
			||||||
 | 
					golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
				
			||||||
 | 
					golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
				
			||||||
 | 
					golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
 | 
				
			||||||
 | 
					golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 | 
				
			||||||
							
								
								
									
										6
									
								
								options.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								options.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					package tprint
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type TableOptions struct {
 | 
				
			||||||
 | 
						Header bool
 | 
				
			||||||
 | 
						Border bool
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										168
									
								
								table.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										168
									
								
								table.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,168 @@
 | 
				
			|||||||
 | 
					package tprint
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"git.tordarus.net/tordarus/gmath"
 | 
				
			||||||
 | 
						"git.tordarus.net/tordarus/slices"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Table struct {
 | 
				
			||||||
 | 
						head [][]string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rowSep     bool
 | 
				
			||||||
 | 
						headheight int
 | 
				
			||||||
 | 
						maxcols    int
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						colwidth  []int
 | 
				
			||||||
 | 
						rowheight []int
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						data [][][]string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewTable(head ...string) *Table {
 | 
				
			||||||
 | 
						thead := slices.Map(head, func(s string) []string { return strings.Split(s, "\n") })
 | 
				
			||||||
 | 
						return &Table{
 | 
				
			||||||
 | 
							head:       thead,
 | 
				
			||||||
 | 
							colwidth:   slices.Map(head, func(s string) int { return strLen(s) }),
 | 
				
			||||||
 | 
							headheight: len(Search(thead, maxLengthSlice[string])),
 | 
				
			||||||
 | 
							rowheight:  []int{},
 | 
				
			||||||
 | 
							maxcols:    len(thead),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (t *Table) AddRow(row ...interface{}) {
 | 
				
			||||||
 | 
						irow := slices.Map(row, func(v interface{}) []string { return strings.Split(fmt.Sprint(v), "\n") })
 | 
				
			||||||
 | 
						t.data = append(t.data, irow)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rowheight := len(Search(irow, maxLengthSlice[string]))
 | 
				
			||||||
 | 
						t.rowheight = append(t.rowheight, rowheight)
 | 
				
			||||||
 | 
						if rowheight > 1 {
 | 
				
			||||||
 | 
							t.rowSep = true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						t.maxcols = gmath.Max(t.maxcols, len(irow))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for i, cell := range irow {
 | 
				
			||||||
 | 
							maxLineLength := strLen(Search(cell, maxLengthStr))
 | 
				
			||||||
 | 
							if i < len(t.colwidth) {
 | 
				
			||||||
 | 
								t.colwidth[i] = gmath.Max(t.colwidth[i], maxLineLength)
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								t.colwidth = append(t.colwidth, maxLineLength)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if i >= len(t.head) {
 | 
				
			||||||
 | 
								headname := "unknown column"
 | 
				
			||||||
 | 
								t.head = append(t.head, []string{headname})
 | 
				
			||||||
 | 
								t.colwidth[i] = gmath.Max(t.colwidth[i], strLen(headname))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (t *Table) StringNoHead() string {
 | 
				
			||||||
 | 
						b := new(strings.Builder)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						b.WriteRune('┏')
 | 
				
			||||||
 | 
						for i, colwidth := range t.colwidth {
 | 
				
			||||||
 | 
							b.WriteString(strings.Repeat("━", colwidth))
 | 
				
			||||||
 | 
							if i < len(t.colwidth)-1 {
 | 
				
			||||||
 | 
								b.WriteRune('┯')
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						b.WriteRune('┓')
 | 
				
			||||||
 | 
						b.WriteRune('\n')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for i, row := range t.data {
 | 
				
			||||||
 | 
							t.printRow(b, row, t.rowheight[i])
 | 
				
			||||||
 | 
							if t.rowSep && i < len(t.data)-1 {
 | 
				
			||||||
 | 
								t.addNextCellLine(b)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						t.addLastCellLine(b)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return b.String()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (t *Table) String() string {
 | 
				
			||||||
 | 
						b := new(strings.Builder)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						b.WriteRune('┏')
 | 
				
			||||||
 | 
						for i, colwidth := range t.colwidth {
 | 
				
			||||||
 | 
							b.WriteString(strings.Repeat("━", colwidth))
 | 
				
			||||||
 | 
							if i < len(t.colwidth)-1 {
 | 
				
			||||||
 | 
								b.WriteRune('┯')
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						b.WriteRune('┓')
 | 
				
			||||||
 | 
						b.WriteRune('\n')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						t.printRow(b, t.head, t.headheight)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						b.WriteRune('┣')
 | 
				
			||||||
 | 
						for i, colwidth := range t.colwidth {
 | 
				
			||||||
 | 
							b.WriteString(strings.Repeat("━", colwidth))
 | 
				
			||||||
 | 
							if i < len(t.colwidth)-1 {
 | 
				
			||||||
 | 
								b.WriteRune('┿')
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						b.WriteRune('┫')
 | 
				
			||||||
 | 
						b.WriteRune('\n')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for i, row := range t.data {
 | 
				
			||||||
 | 
							t.printRow(b, row, t.rowheight[i])
 | 
				
			||||||
 | 
							if t.rowSep && i < len(t.data)-1 {
 | 
				
			||||||
 | 
								t.addNextCellLine(b)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						t.addLastCellLine(b)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return b.String()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (t *Table) printRow(b *strings.Builder, rowData [][]string, rowHeight int) {
 | 
				
			||||||
 | 
						for lineIndex := 0; lineIndex < rowHeight; lineIndex++ {
 | 
				
			||||||
 | 
							b.WriteRune('┃')
 | 
				
			||||||
 | 
							for colIndex := 0; colIndex < t.maxcols; colIndex++ {
 | 
				
			||||||
 | 
								line := ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if colIndex < len(rowData) {
 | 
				
			||||||
 | 
									cell := rowData[colIndex]
 | 
				
			||||||
 | 
									if lineIndex < len(cell) {
 | 
				
			||||||
 | 
										line = cell[lineIndex]
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								b.WriteString(fmt.Sprint(padStringRight(line, ' ', t.colwidth[colIndex])))
 | 
				
			||||||
 | 
								if colIndex < t.maxcols-1 {
 | 
				
			||||||
 | 
									b.WriteRune('│')
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							b.WriteString("┃\n")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (t *Table) addNextCellLine(b *strings.Builder) {
 | 
				
			||||||
 | 
						b.WriteRune('┠')
 | 
				
			||||||
 | 
						for i, colwidth := range t.colwidth {
 | 
				
			||||||
 | 
							b.WriteString(strings.Repeat("─", colwidth))
 | 
				
			||||||
 | 
							if i < len(t.colwidth)-1 {
 | 
				
			||||||
 | 
								b.WriteRune('┼')
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						b.WriteRune('┨')
 | 
				
			||||||
 | 
						b.WriteRune('\n')
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (t *Table) addLastCellLine(b *strings.Builder) {
 | 
				
			||||||
 | 
						b.WriteRune('┗')
 | 
				
			||||||
 | 
						for i, colwidth := range t.colwidth {
 | 
				
			||||||
 | 
							b.WriteString(strings.Repeat("━", colwidth))
 | 
				
			||||||
 | 
							if i < len(t.colwidth)-1 {
 | 
				
			||||||
 | 
								b.WriteRune('┷')
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						b.WriteString("┛\n")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										44
									
								
								table_header.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								table_header.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,44 @@
 | 
				
			|||||||
 | 
					package tprint
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"git.tordarus.net/tordarus/slices"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func FormatHeaderTable(header string, table *Table) string {
 | 
				
			||||||
 | 
						return formatHeaderTableStr(header, table.String())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func FormatHeaderTableNoHead(header string, table *Table) string {
 | 
				
			||||||
 | 
						return formatHeaderTableStr(header, table.StringNoHead())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TODO formatHeaderTableStr is a quick hack
 | 
				
			||||||
 | 
					// a better solution would be to support nested tables.
 | 
				
			||||||
 | 
					// in that case, a header table is just a table with a singe col, single header row and single data row
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func formatHeaderTableStr(header string, tab string) string {
 | 
				
			||||||
 | 
						b := new(strings.Builder)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						splits := strings.Split(tab, "\n")
 | 
				
			||||||
 | 
						tabwidth := strLen(splits[0])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						b.WriteRune('┏')
 | 
				
			||||||
 | 
						b.WriteString(strings.Repeat("━", tabwidth-2))
 | 
				
			||||||
 | 
						b.WriteString("┓\n┃")
 | 
				
			||||||
 | 
						b.WriteString(padStringCenter(header, ' ', tabwidth-2))
 | 
				
			||||||
 | 
						b.WriteString("┃\n")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						b.WriteRune('┣')
 | 
				
			||||||
 | 
						b.WriteString(splits[0][3 : len(splits[0])-3])
 | 
				
			||||||
 | 
						b.WriteString("┫\n")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						slices.Each(splits[1:], func(s string) {
 | 
				
			||||||
 | 
							b.WriteString(s)
 | 
				
			||||||
 | 
							b.WriteRune('\n')
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ret := b.String()
 | 
				
			||||||
 | 
						return ret[:len(ret)-1]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										72
									
								
								utils.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								utils.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,72 @@
 | 
				
			|||||||
 | 
					package tprint
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"unicode"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/mattn/go-runewidth"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func Search[T any](slice []T, cmp func(a, b T) T) T {
 | 
				
			||||||
 | 
						best := *new(T)
 | 
				
			||||||
 | 
						for _, v := range slice {
 | 
				
			||||||
 | 
							best = cmp(v, best)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return best
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func maxLengthStr(a, b string) string {
 | 
				
			||||||
 | 
						if strLen(a) > strLen(b) {
 | 
				
			||||||
 | 
							return a
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return b
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func maxLengthSlice[T any](a, b []T) []T {
 | 
				
			||||||
 | 
						if len(a) > len(b) {
 | 
				
			||||||
 | 
							return a
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return b
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func strLen(str string) int {
 | 
				
			||||||
 | 
						l := 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						runes := []rune(str)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for i := 0; i < len(runes); i++ {
 | 
				
			||||||
 | 
							rn := runes[i]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// skip control sequences
 | 
				
			||||||
 | 
							if unicode.IsControl(rn) {
 | 
				
			||||||
 | 
								for j := i; j < len(runes)-1 && runes[j] != 'm'; j++ {
 | 
				
			||||||
 | 
									i = j
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								i++
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if rn <= 0xFF || rn >= '─' && rn <= '╿' {
 | 
				
			||||||
 | 
								l++
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								l += runewidth.RuneWidth(rn)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return l
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func padStringRight(str string, pad rune, length int) string {
 | 
				
			||||||
 | 
						padding := length - strLen(str)
 | 
				
			||||||
 | 
						return str + strings.Repeat(string(pad), padding)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func padStringLeft(str string, pad rune, length int) string {
 | 
				
			||||||
 | 
						padding := length - strLen(str)
 | 
				
			||||||
 | 
						return strings.Repeat(string(pad), padding) + str
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func padStringCenter(str string, pad rune, length int) string {
 | 
				
			||||||
 | 
						l := strLen(str)
 | 
				
			||||||
 | 
						return padStringLeft(padStringRight(str, pad, (length-l)/2+l), pad, length)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user