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
}
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.
package columnize
import (
	"bytes"
	"fmt"
	"strings"
)
type Config struct {
	Delim string
	Glue string
	Prefix string
	Empty string
	NoTrim bool
}
func DefaultConfig() *Config {
	return &Config{
		Delim:  "|",
		Glue:   "  ",
		Prefix: "",
		Empty:  "",
		NoTrim: false,
	}
}
func MergeConfig(a, b *Config) *Config {
	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
}
func stringFormat(c *Config, widths []int, columns int) string {
	buf := bytes.NewBuffer(make([]byte, 0, (6+len(c.Glue))*columns))
	buf.WriteString(c.Prefix)
	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()
}
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)
		}
		if value == "" && config.Empty != "" {
			value = config.Empty
		}
		elements[i] = value
	}
	return elements
}
func runeLen(s string) int {
	l := 0
	for _ = range s {
		l++
	}
	return l
}