open-nomad/vendor/github.com/ryanuber/columnize/columnize.go

192 lines
4.6 KiB
Go

package columnize
import (
"bytes"
"fmt"
"strings"
)
// Config can be used to tune certain parameters which affect the way
// in which Columnize will format output text.
type Config struct {
// The string by which the lines of input will be split.
Delim string
// The string by which columns of output will be separated.
Glue string
// The string by which columns of output will be prefixed.
Prefix string
// A replacement string to replace empty fields.
Empty string
// NoTrim disables automatic trimming of inputs.
NoTrim bool
}
// DefaultConfig returns a *Config with default values.
func DefaultConfig() *Config {
return &Config{
Delim: "|",
Glue: " ",
Prefix: "",
Empty: "",
NoTrim: false,
}
}
// MergeConfig merges two config objects together and returns the resulting
// configuration. Values from the right take precedence over the left side.
func MergeConfig(a, b *Config) *Config {
// Return quickly if either side was nil
if a == nil {
return b
}
if b == nil {
return a
}
var result Config = *a
if b.Delim != "" {
result.Delim = b.Delim
}
if b.Glue != "" {
result.Glue = b.Glue
}
if b.Prefix != "" {
result.Prefix = b.Prefix
}
if b.Empty != "" {
result.Empty = b.Empty
}
if b.NoTrim {
result.NoTrim = true
}
return &result
}
// stringFormat, given a set of column widths and the number of columns in
// the current line, returns a sprintf-style format string which can be used
// to print output aligned properly with other lines using the same widths set.
func stringFormat(c *Config, widths []int, columns int) string {
// Create the buffer with an estimate of the length
buf := bytes.NewBuffer(make([]byte, 0, (6+len(c.Glue))*columns))
// Start with the prefix, if any was given. The buffer will not return an
// error so it does not need to be handled
buf.WriteString(c.Prefix)
// Create the format string from the discovered widths
for i := 0; i < columns && i < len(widths); i++ {
if i == columns-1 {
buf.WriteString("%s\n")
} else {
fmt.Fprintf(buf, "%%-%ds%s", widths[i], c.Glue)
}
}
return buf.String()
}
// elementsFromLine returns a list of elements, each representing a single
// item which will belong to a column of output.
func elementsFromLine(config *Config, line string) []interface{} {
separated := strings.Split(line, config.Delim)
elements := make([]interface{}, len(separated))
for i, field := range separated {
value := field
if !config.NoTrim {
value = strings.TrimSpace(field)
}
// Apply the empty value, if configured.
if value == "" && config.Empty != "" {
value = config.Empty
}
elements[i] = value
}
return elements
}
// runeLen calculates the number of visible "characters" in a string
func runeLen(s string) int {
l := 0
for _ = range s {
l++
}
return l
}
// widthsFromLines examines a list of strings and determines how wide each
// column should be considering all of the elements that need to be printed
// within it.
func widthsFromLines(config *Config, lines []string) []int {
widths := make([]int, 0, 8)
for _, line := range lines {
elems := elementsFromLine(config, line)
for i := 0; i < len(elems); i++ {
l := runeLen(elems[i].(string))
if len(widths) <= i {
widths = append(widths, l)
} else if widths[i] < l {
widths[i] = l
}
}
}
return widths
}
// Format is the public-facing interface that takes a list of strings and
// returns nicely aligned column-formatted text.
func Format(lines []string, config *Config) string {
conf := MergeConfig(DefaultConfig(), config)
widths := widthsFromLines(conf, lines)
// Estimate the buffer size
glueSize := len(conf.Glue)
var size int
for _, w := range widths {
size += w + glueSize
}
size *= len(lines)
// Create the buffer
buf := bytes.NewBuffer(make([]byte, 0, size))
// Create a cache for the string formats
fmtCache := make(map[int]string, 16)
// Create the formatted output using the format string
for _, line := range lines {
elems := elementsFromLine(conf, line)
// Get the string format using cache
numElems := len(elems)
stringfmt, ok := fmtCache[numElems]
if !ok {
stringfmt = stringFormat(conf, widths, numElems)
fmtCache[numElems] = stringfmt
}
fmt.Fprintf(buf, stringfmt, elems...)
}
// Get the string result
result := buf.String()
// Remove trailing newline without removing leading/trailing space
if n := len(result); n > 0 && result[n-1] == '\n' {
result = result[:n-1]
}
return result
}
// SimpleFormat is a convenience function to format text with the defaults.
func SimpleFormat(lines []string) string {
return Format(lines, nil)
}