columnize.go 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. package columnize
  2. import (
  3. "fmt"
  4. "strings"
  5. )
  6. type Config struct {
  7. // The string by which the lines of input will be split.
  8. Delim string
  9. // The string by which columns of output will be separated.
  10. Glue string
  11. // The string by which columns of output will be prefixed.
  12. Prefix string
  13. // A replacement string to replace empty fields
  14. Empty string
  15. }
  16. // Returns a Config with default values.
  17. func DefaultConfig() *Config {
  18. return &Config{
  19. Delim: "|",
  20. Glue: " ",
  21. Prefix: "",
  22. }
  23. }
  24. // Returns a list of elements, each representing a single item which will
  25. // belong to a column of output.
  26. func getElementsFromLine(config *Config, line string) []interface{} {
  27. elements := make([]interface{}, 0)
  28. for _, field := range strings.Split(line, config.Delim) {
  29. value := strings.TrimSpace(field)
  30. if value == "" && config.Empty != "" {
  31. value = config.Empty
  32. }
  33. elements = append(elements, value)
  34. }
  35. return elements
  36. }
  37. // Examines a list of strings and determines how wide each column should be
  38. // considering all of the elements that need to be printed within it.
  39. func getWidthsFromLines(config *Config, lines []string) []int {
  40. var widths []int
  41. for _, line := range lines {
  42. elems := getElementsFromLine(config, line)
  43. for i := 0; i < len(elems); i++ {
  44. l := len(elems[i].(string))
  45. if len(widths) <= i {
  46. widths = append(widths, l)
  47. } else if widths[i] < l {
  48. widths[i] = l
  49. }
  50. }
  51. }
  52. return widths
  53. }
  54. // Given a set of column widths and the number of columns in the current line,
  55. // returns a sprintf-style format string which can be used to print output
  56. // aligned properly with other lines using the same widths set.
  57. func (c *Config) getStringFormat(widths []int, columns int) string {
  58. // Start with the prefix, if any was given.
  59. stringfmt := c.Prefix
  60. // Create the format string from the discovered widths
  61. for i := 0; i < columns && i < len(widths); i++ {
  62. if i == columns-1 {
  63. stringfmt += "%s\n"
  64. } else {
  65. stringfmt += fmt.Sprintf("%%-%ds%s", widths[i], c.Glue)
  66. }
  67. }
  68. return stringfmt
  69. }
  70. // MergeConfig merges two config objects together and returns the resulting
  71. // configuration. Values from the right take precedence over the left side.
  72. func MergeConfig(a, b *Config) *Config {
  73. var result Config = *a
  74. // Return quickly if either side was nil
  75. if a == nil || b == nil {
  76. return &result
  77. }
  78. if b.Delim != "" {
  79. result.Delim = b.Delim
  80. }
  81. if b.Glue != "" {
  82. result.Glue = b.Glue
  83. }
  84. if b.Prefix != "" {
  85. result.Prefix = b.Prefix
  86. }
  87. if b.Empty != "" {
  88. result.Empty = b.Empty
  89. }
  90. return &result
  91. }
  92. // Format is the public-facing interface that takes either a plain string
  93. // or a list of strings and returns nicely aligned output.
  94. func Format(lines []string, config *Config) string {
  95. var result string
  96. conf := MergeConfig(DefaultConfig(), config)
  97. widths := getWidthsFromLines(conf, lines)
  98. // Create the formatted output using the format string
  99. for _, line := range lines {
  100. elems := getElementsFromLine(conf, line)
  101. stringfmt := conf.getStringFormat(widths, len(elems))
  102. result += fmt.Sprintf(stringfmt, elems...)
  103. }
  104. // Remove trailing newline without removing leading/trailing space
  105. if n := len(result); n > 0 && result[n-1] == '\n' {
  106. result = result[:n-1]
  107. }
  108. return result
  109. }
  110. // Convenience function for using Columnize as easy as possible.
  111. func SimpleFormat(lines []string) string {
  112. return Format(lines, nil)
  113. }