├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── example_test.go ├── makefile ├── table.go └── table_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.dll 4 | *.so 5 | *.dylib 6 | 7 | # Test binary, build with `go test -c` 8 | *.test 9 | 10 | # Output of the go coverage tool, specifically when used with LiteIDE 11 | *.out 12 | 13 | # Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 14 | .glide/ 15 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | sudo: false 3 | 4 | go: 5 | - 1.2.x 6 | - 1.3.x 7 | - 1.4.x 8 | - 1.5.x 9 | - 1.6.x 10 | - 1.7.x 11 | - 1.8.x 12 | - tip 13 | 14 | before_install: 15 | - go get golang.org/x/tools/cmd/cover 16 | - go get github.com/mattn/goveralls 17 | 18 | install: 19 | - go get -t ./... 20 | 21 | script: 22 | - go test ./... -v -short -covermode=count -coverprofile=cover.out 23 | - $HOME/gopath/bin/goveralls -service=travis-ci 24 | 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 mwn 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | table 2 | ===== 3 | 4 | [![Build Status](https://travis-ci.org/modood/table.png)](https://travis-ci.org/modood/table) 5 | [![Coverage Status](https://coveralls.io/repos/github/modood/table/badge.svg?branch=master)](https://coveralls.io/github/modood/table?branch=master) 6 | [![GoDoc](https://godoc.org/github.com/modood/table?status.svg)](http://godoc.org/github.com/modood/table) 7 | 8 | Produces a string that represents slice of structs data in a text table, inspired by gajus/table. 9 | 10 | **Features:** 11 | 12 | - No dependency. 13 | - Cell content aligned. 14 | - Column width self-adaptation 15 | - Support type of struct field: int, float, string, bool, slice, struct, map, time.Time and everything. 16 | - Support custom table header by declaring optional tag: `table`.(Thanks @skyfireitdiy) 17 | 18 | Installation 19 | ------------ 20 | 21 | ``` 22 | $ go get github.com/modood/table 23 | ``` 24 | 25 | Quick start 26 | ----------- 27 | 28 | ```go 29 | package main 30 | 31 | import ( 32 | "github.com/modood/table" 33 | ) 34 | 35 | type House struct { 36 | Name string `table:"Name"` 37 | Sigil string 38 | Motto string 39 | } 40 | 41 | func main() { 42 | hs := []House{ 43 | {"Stark", "direwolf", "Winter is coming"}, 44 | {"Targaryen", "dragon", "Fire and Blood"}, 45 | {"Lannister", "lion", "Hear Me Roar"}, 46 | } 47 | 48 | // Output to stdout 49 | table.Output(hs) 50 | 51 | // Or just return table string and then do something 52 | s := table.Table(hs) 53 | _ = s 54 | } 55 | ``` 56 | 57 | output: 58 | ``` 59 | ┌───────────┬──────────┬──────────────────┐ 60 | │ Name │ Sigil │ Motto │ 61 | ├───────────┼──────────┼──────────────────┤ 62 | │ Stark │ direwolf │ Winter is coming │ 63 | │ Targaryen │ dragon │ Fire and Blood │ 64 | │ Lannister │ lion │ Hear Me Roar │ 65 | └───────────┴──────────┴──────────────────┘ 66 | ``` 67 | 68 | Document 69 | -------- 70 | 71 | - `func Output(slice interface{})` 72 | 73 | formats slice of structs data and writes to standard output.(Using box drawing characters) 74 | 75 | - `func OutputA(slice interface{})` 76 | 77 | formats slice of structs data and writes to standard output.(Using standard ascii characters) 78 | 79 | - `func Table(slice interface{}) string` 80 | 81 | formats slice of structs data and returns the resulting string.(Using box drawing characters) 82 | 83 | - `func AsciiTable(slice interface{}) string` 84 | 85 | formats slice of structs data and returns the resulting string.(Using standard ascii characters) 86 | 87 | - compare [box drawing characters](http://unicode.org/charts/PDF/U2500.pdf) with [standard ascii characters](https://ascii.cl/) 88 | 89 | box drawing: 90 | ``` 91 | ┌───────────┬──────────┬──────────────────┐ 92 | │ Name │ Sigil │ Motto │ 93 | ├───────────┼──────────┼──────────────────┤ 94 | │ Stark │ direwolf │ Winter is coming │ 95 | │ Targaryen │ dragon │ Fire and Blood │ 96 | │ Lannister │ lion │ Hear Me Roar │ 97 | └───────────┴──────────┴──────────────────┘ 98 | ``` 99 | 100 | standard ascii: 101 | 102 | ``` 103 | +-----------+----------+------------------+ 104 | | Name | Sigil | Motto | 105 | +-----------+----------+------------------+ 106 | | Stark | direwolf | Winter is coming | 107 | | Targaryen | dragon | Fire and Blood | 108 | | Lannister | lion | Hear Me Roar | 109 | +-----------+----------+------------------+ 110 | ``` 111 | 112 | 113 | Contributing 114 | ------------ 115 | 116 | 1. Fork it 117 | 2. Create your feature branch (`git checkout -b my-new-feature`) 118 | 3. Commit your changes (`git commit -am 'Add some feature'`) 119 | 4. Push to the branch (`git push origin my-new-feature`) 120 | 5. Create new Pull Request 121 | 122 | License 123 | ------- 124 | 125 | this repo is released under the [MIT License](http://www.opensource.org/licenses/MIT). 126 | -------------------------------------------------------------------------------- /example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 modood. All rights reserved. 2 | // license that can be found in the LICENSE file. 3 | 4 | package table_test 5 | 6 | import ( 7 | "github.com/modood/table" 8 | ) 9 | 10 | type House struct { 11 | Name string 12 | Sigil string 13 | Motto string 14 | } 15 | 16 | func Example() { 17 | hs := []House{ 18 | {"Stark", "direwolf", "Winter is coming"}, 19 | {"Targaryen", "dragon", "Fire and Blood"}, 20 | {"Lannister", "lion", "Hear Me Roar"}, 21 | } 22 | 23 | // Output to stdout 24 | table.Output(hs) 25 | 26 | // Or just return table string and then do something 27 | s := table.Table(hs) 28 | _ = s 29 | } 30 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | GO = go 2 | GO_GET = $(GO) get -v 3 | GO_TEST = $(GO) test -v 4 | 5 | 6 | all: test 7 | 8 | dep: 9 | -$(GO_GET) github.com/smartystreets/goconvey/convey 10 | 11 | test: dep 12 | $(GO_TEST) 13 | 14 | .PHONY: all dep test 15 | 16 | -------------------------------------------------------------------------------- /table.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 modood. All rights reserved. 2 | // license that can be found in the LICENSE file. 3 | 4 | // Package table produces a string that represents slice of structs data in a text table 5 | package table 6 | 7 | import ( 8 | "errors" 9 | "fmt" 10 | "reflect" 11 | ) 12 | 13 | type bd struct { 14 | H rune // BOX DRAWINGS HORIZONTAL 15 | V rune // BOX DRAWINGS VERTICAL 16 | VH rune // BOX DRAWINGS VERTICAL AND HORIZONTAL 17 | HU rune // BOX DRAWINGS HORIZONTAL AND UP 18 | HD rune // BOX DRAWINGS HORIZONTAL AND DOWN 19 | VL rune // BOX DRAWINGS VERTICAL AND LEFT 20 | VR rune // BOX DRAWINGS VERTICAL AND RIGHT 21 | DL rune // BOX DRAWINGS DOWN AND LEFT 22 | DR rune // BOX DRAWINGS DOWN AND RIGHT 23 | UL rune // BOX DRAWINGS UP AND LEFT 24 | UR rune // BOX DRAWINGS UP AND RIGHT 25 | } 26 | 27 | var m = map[string]bd{ 28 | "ascii": {'-', '|', '+', '+', '+', '+', '+', '+', '+', '+', '+'}, 29 | "box-drawing": {'─', '│', '┼', '┴', '┬', '┤', '├', '┐', '┌', '┘', '└'}, 30 | } 31 | 32 | // Output formats slice of structs data and writes to standard output.(Using box drawing characters) 33 | func Output(slice interface{}) { 34 | fmt.Println(Table(slice)) 35 | } 36 | 37 | // OutputA formats slice of structs data and writes to standard output.(Using standard ascii characters) 38 | func OutputA(slice interface{}) { 39 | fmt.Println(AsciiTable(slice)) 40 | } 41 | 42 | // Table formats slice of structs data and returns the resulting string.(Using box drawing characters) 43 | func Table(slice interface{}) string { 44 | coln, colw, rows, err := parse(slice) 45 | if err != nil { 46 | return err.Error() 47 | } 48 | table := table(coln, colw, rows, m["box-drawing"]) 49 | return table 50 | } 51 | 52 | // AsciiTable formats slice of structs data and returns the resulting string.(Using standard ascii characters) 53 | func AsciiTable(slice interface{}) string { 54 | coln, colw, rows, err := parse(slice) 55 | if err != nil { 56 | return err.Error() 57 | } 58 | table := table(coln, colw, rows, m["ascii"]) 59 | return table 60 | } 61 | 62 | func parse(slice interface{}) ( 63 | coln []string, // name of columns 64 | colw []int, // width of columns 65 | rows [][]string, // rows of content 66 | err error, 67 | ) { 68 | 69 | s, err := sliceconv(slice) 70 | if err != nil { 71 | return 72 | } 73 | for i, u := range s { 74 | v := reflect.ValueOf(u) 75 | t := reflect.TypeOf(u) 76 | if v.Kind() == reflect.Ptr { 77 | v = v.Elem() 78 | t = t.Elem() 79 | } 80 | if v.Kind() != reflect.Struct { 81 | err = errors.New("warning: table: items of slice should be on struct value") 82 | return 83 | } 84 | var row []string 85 | 86 | m := 0 // count of unexported field 87 | for n := 0; n < v.NumField(); n++ { 88 | if t.Field(n).PkgPath != "" { 89 | m++ 90 | continue 91 | } 92 | cn := t.Field(n).Name 93 | ct := t.Field(n).Tag.Get("table") 94 | if ct == "" { 95 | ct = cn 96 | } else if ct == "-" { 97 | m++ 98 | continue 99 | } 100 | cv := fmt.Sprintf("%+v", v.FieldByName(cn).Interface()) 101 | 102 | if i == 0 { 103 | coln = append(coln, ct) 104 | colw = append(colw, len(ct)) 105 | } 106 | if colw[n-m] < len(cv) { 107 | colw[n-m] = len(cv) 108 | } 109 | 110 | row = append(row, cv) 111 | } 112 | rows = append(rows, row) 113 | } 114 | return coln, colw, rows, nil 115 | } 116 | 117 | func table(coln []string, colw []int, rows [][]string, b bd) (table string) { 118 | if len(rows) == 0 { 119 | return "" 120 | } 121 | head := [][]rune{{b.DR}, {b.V}, {b.VR}} 122 | bttm := []rune{b.UR} 123 | for i, v := range colw { 124 | head[0] = append(head[0], []rune(repeat(v+2, b.H)+string(b.HD))...) 125 | head[1] = append(head[1], []rune(" "+coln[i]+repeat(v-StringLength([]rune(coln[i]))+1, ' ')+string(b.V))...) 126 | head[2] = append(head[2], []rune(repeat(v+2, b.H)+string(b.VH))...) 127 | bttm = append(bttm, []rune(repeat(v+2, b.H)+string(b.HU))...) 128 | } 129 | head[0][len(head[0])-1] = b.DL 130 | head[2][len(head[2])-1] = b.VL 131 | bttm[len(bttm)-1] = b.UL 132 | 133 | var body [][]rune 134 | for _, r := range rows { 135 | row := []rune{b.V} 136 | for i, v := range colw { 137 | // handle non-ascii character 138 | l := StringLength([]rune(r[i])) 139 | 140 | row = append(row, []rune(" "+r[i]+repeat(v-l+1, ' ')+string(b.V))...) 141 | } 142 | body = append(body, row) 143 | } 144 | 145 | for _, v := range head { 146 | table += string(v) + "\n" 147 | } 148 | for _, v := range body { 149 | table += string(v) + "\n" 150 | } 151 | table += string(bttm) 152 | return table 153 | } 154 | 155 | func sliceconv(slice interface{}) ([]interface{}, error) { 156 | v := reflect.ValueOf(slice) 157 | if v.Kind() != reflect.Slice { 158 | return nil, errors.New("warning: sliceconv: param \"slice\" should be on slice value") 159 | } 160 | 161 | l := v.Len() 162 | r := make([]interface{}, l) 163 | for i := 0; i < l; i++ { 164 | r[i] = v.Index(i).Interface() 165 | } 166 | return r, nil 167 | } 168 | 169 | func repeat(time int, char rune) string { 170 | var s = make([]rune, time) 171 | for i := range s { 172 | s[i] = char 173 | } 174 | return string(s) 175 | } 176 | 177 | // StringLength string display length 178 | func StringLength(r []rune) int { 179 | // CJK(Chinese, Japanese, Korean) 180 | type cjk struct { 181 | from rune 182 | to rune 183 | } 184 | 185 | // References: 186 | // - [Unicode Table](http://www.tamasoft.co.jp/en/general-info/unicode.html) 187 | // - [汉字 Unicode 编码范围](http://www.qqxiuzi.cn/zh/hanzi-unicode-bianma.php) 188 | 189 | var a = []cjk{ 190 | {0x2E80, 0x9FD0}, // Chinese, Hiragana, Katakana, ... 191 | {0xAC00, 0xD7A3}, // Hangul 192 | {0xF900, 0xFACE}, // Kanji 193 | {0xFE00, 0xFE6C}, // Fullwidth 194 | {0xFF00, 0xFF60}, // Fullwidth again 195 | {0x20000, 0x2FA1D}, // Extension 196 | // More? PRs are aways welcome here. 197 | } 198 | length := len(r) 199 | l: 200 | for _, v := range r { 201 | for _, c := range a { 202 | if v >= c.from && v <= c.to { 203 | length++ 204 | continue l 205 | } 206 | } 207 | } 208 | return length 209 | } 210 | -------------------------------------------------------------------------------- /table_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 modood. All rights reserved. 2 | // license that can be found in the LICENSE file. 3 | 4 | package table_test 5 | 6 | import ( 7 | "testing" 8 | "time" 9 | 10 | . "github.com/modood/table" 11 | . "github.com/smartystreets/goconvey/convey" 12 | ) 13 | 14 | func TestOutput(t *testing.T) { 15 | Convey("Output content without panic", t, func() { 16 | type Repo struct { 17 | Name string 18 | Author string 19 | URL string 20 | } 21 | 22 | s := []Repo{ 23 | {"table", "modood", "https://github.com/modood/table"}, 24 | } 25 | 26 | Println("\n\nBox Drawing:") 27 | Output(s) 28 | 29 | Println("\nPure Ascii:") 30 | OutputA(s) 31 | }) 32 | } 33 | 34 | func TestTable(t *testing.T) { 35 | Convey("Get table content", t, func() { 36 | Convey("non-slice, should warning", func() { 37 | So(Table(nil), ShouldEqual, "warning: sliceconv: param \"slice\" should be on slice value") 38 | 39 | So(AsciiTable(nil), ShouldEqual, "warning: sliceconv: param \"slice\" should be on slice value") 40 | }) 41 | 42 | Convey("slice of int, should warning", func() { 43 | s := []int{1, 2, 3} 44 | 45 | So(Table(s), ShouldEqual, "warning: table: items of slice should be on struct value") 46 | 47 | So(AsciiTable(s), ShouldEqual, "warning: table: items of slice should be on struct value") 48 | }) 49 | 50 | Convey("slice of struct pointers, should work correctly", func() { 51 | 52 | type S struct { 53 | Celsius string 54 | } 55 | 56 | s := []*S{{"39.5℃"}, {"37.34℃"}} 57 | 58 | var tb = []struct { 59 | content string 60 | expected string 61 | }{ 62 | {Table(s), ` 63 | ┌──────────┐ 64 | │ Celsius │ 65 | ├──────────┤ 66 | │ 39.5℃ │ 67 | │ 37.34℃ │ 68 | └──────────┘`}, 69 | {AsciiTable(s), ` 70 | +----------+ 71 | | Celsius | 72 | +----------+ 73 | | 39.5℃ | 74 | | 37.34℃ | 75 | +----------+`}, 76 | } 77 | 78 | for _, tt := range tb { 79 | So("\n"+tt.content, ShouldEqual, tt.expected) 80 | } 81 | }) 82 | 83 | Convey("special characters should work correctly", func() { 84 | type S struct { 85 | Celsius string 86 | } 87 | 88 | s := []S{{"39.5℃"}, {"37.34℃"}} 89 | 90 | var tb = []struct { 91 | content string 92 | expected string 93 | }{ 94 | {Table(s), ` 95 | ┌──────────┐ 96 | │ Celsius │ 97 | ├──────────┤ 98 | │ 39.5℃ │ 99 | │ 37.34℃ │ 100 | └──────────┘`}, 101 | {AsciiTable(s), ` 102 | +----------+ 103 | | Celsius | 104 | +----------+ 105 | | 39.5℃ | 106 | | 37.34℃ | 107 | +----------+`}, 108 | } 109 | 110 | for _, tt := range tb { 111 | So("\n"+tt.content, ShouldEqual, tt.expected) 112 | } 113 | }) 114 | 115 | Convey("unexported field should be ignored", func() { 116 | type Field struct { 117 | Exported float32 118 | unexported float32 119 | } 120 | 121 | s := []Field{{1.2, 2.4}, {4.8, 9.6}} 122 | 123 | var tb = []struct { 124 | content string 125 | expected string 126 | }{ 127 | {Table(s), ` 128 | ┌──────────┐ 129 | │ Exported │ 130 | ├──────────┤ 131 | │ 1.2 │ 132 | │ 4.8 │ 133 | └──────────┘`}, 134 | {AsciiTable(s), ` 135 | +----------+ 136 | | Exported | 137 | +----------+ 138 | | 1.2 | 139 | | 4.8 | 140 | +----------+`}, 141 | } 142 | 143 | for _, tt := range tb { 144 | So("\n"+tt.content, ShouldEqual, tt.expected) 145 | } 146 | }) 147 | 148 | Convey("Field: string(Chinese)", func() { 149 | type Poet struct { 150 | Name string 151 | Dynasty string 152 | Live string 153 | } 154 | 155 | s := []Poet{ 156 | {"李白", "唐朝", "701年-762年"}, 157 | {"李清照", "宋朝", "1084年-1155年"}, 158 | } 159 | 160 | var tb = []struct { 161 | content string 162 | expected string 163 | }{ 164 | {Table(s), ` 165 | ┌───────────┬─────────┬─────────────────┐ 166 | │ Name │ Dynasty │ Live │ 167 | ├───────────┼─────────┼─────────────────┤ 168 | │ 李白 │ 唐朝 │ 701年-762年 │ 169 | │ 李清照 │ 宋朝 │ 1084年-1155年 │ 170 | └───────────┴─────────┴─────────────────┘`}, 171 | {AsciiTable(s), ` 172 | +-----------+---------+-----------------+ 173 | | Name | Dynasty | Live | 174 | +-----------+---------+-----------------+ 175 | | 李白 | 唐朝 | 701年-762年 | 176 | | 李清照 | 宋朝 | 1084年-1155年 | 177 | +-----------+---------+-----------------+`}, 178 | } 179 | 180 | for _, tt := range tb { 181 | So("\n"+tt.content, ShouldEqual, tt.expected) 182 | } 183 | }) 184 | 185 | Convey("Field: string, int, bool, time.Time", func() { 186 | type User struct { 187 | ID string 188 | Name string 189 | Age int 190 | Deleted bool 191 | Created time.Time 192 | } 193 | 194 | s := []User{ 195 | {"8", "Captain Jack Sparrow", 31, false, time.Date(2017, time.November, 8, 23, 12, 43, 249437302, time.UTC)}, 196 | {"9", "William Turner", 18, false, time.Date(2009, time.February, 9, 2, 4, 25, 363779979, time.UTC)}, 197 | {"10", "Davy Jones", 120, true, time.Date(1965, time.October, 10, 31, 10, 26, 106273532, time.UTC)}, 198 | } 199 | 200 | var tb = []struct { 201 | content string 202 | expected string 203 | }{ 204 | {Table(s), ` 205 | ┌────┬──────────────────────┬─────┬─────────┬─────────────────────────────────────────┐ 206 | │ ID │ Name │ Age │ Deleted │ Created │ 207 | ├────┼──────────────────────┼─────┼─────────┼─────────────────────────────────────────┤ 208 | │ 8 │ Captain Jack Sparrow │ 31 │ false │ 2017-11-08 23:12:43.249437302 +0000 UTC │ 209 | │ 9 │ William Turner │ 18 │ false │ 2009-02-09 02:04:25.363779979 +0000 UTC │ 210 | │ 10 │ Davy Jones │ 120 │ true │ 1965-10-11 07:10:26.106273532 +0000 UTC │ 211 | └────┴──────────────────────┴─────┴─────────┴─────────────────────────────────────────┘`}, 212 | {AsciiTable(s), ` 213 | +----+----------------------+-----+---------+-----------------------------------------+ 214 | | ID | Name | Age | Deleted | Created | 215 | +----+----------------------+-----+---------+-----------------------------------------+ 216 | | 8 | Captain Jack Sparrow | 31 | false | 2017-11-08 23:12:43.249437302 +0000 UTC | 217 | | 9 | William Turner | 18 | false | 2009-02-09 02:04:25.363779979 +0000 UTC | 218 | | 10 | Davy Jones | 120 | true | 1965-10-11 07:10:26.106273532 +0000 UTC | 219 | +----+----------------------+-----+---------+-----------------------------------------+`}, 220 | } 221 | 222 | for _, tt := range tb { 223 | So("\n"+tt.content, ShouldEqual, tt.expected) 224 | } 225 | }) 226 | 227 | Convey("Field: float, slice, struct, map", func() { 228 | type Mixed struct { 229 | String string 230 | Int int 231 | Float float32 232 | Bool bool 233 | Slice []string 234 | Struct struct { 235 | ID string 236 | Name string 237 | } 238 | Map map[string]string 239 | } 240 | 241 | s := []Mixed{ 242 | {"House Stark", 100210, 3.14159, false, []string{"Arya", "Bran"}, struct { 243 | ID string 244 | Name string 245 | }{"10010", "Jon Snow"}, map[string]string{"10086": "Sansa Stark"}}, 246 | {"House Lannister", 2131, 1.234234, true, []string{"Tywin", "Tyrion"}, struct { 247 | ID string 248 | Name string 249 | }{"23019", "Queen Cersei"}, map[string]string{"33489": "Ser Jaime"}}, 250 | } 251 | 252 | var tb = []struct { 253 | content string 254 | expected string 255 | }{ 256 | {Table(s), ` 257 | ┌─────────────────┬────────┬──────────┬───────┬────────────────┬──────────────────────────────┬────────────────────────┐ 258 | │ String │ Int │ Float │ Bool │ Slice │ Struct │ Map │ 259 | ├─────────────────┼────────┼──────────┼───────┼────────────────┼──────────────────────────────┼────────────────────────┤ 260 | │ House Stark │ 100210 │ 3.14159 │ false │ [Arya Bran] │ {ID:10010 Name:Jon Snow} │ map[10086:Sansa Stark] │ 261 | │ House Lannister │ 2131 │ 1.234234 │ true │ [Tywin Tyrion] │ {ID:23019 Name:Queen Cersei} │ map[33489:Ser Jaime] │ 262 | └─────────────────┴────────┴──────────┴───────┴────────────────┴──────────────────────────────┴────────────────────────┘`}, 263 | {AsciiTable(s), ` 264 | +-----------------+--------+----------+-------+----------------+------------------------------+------------------------+ 265 | | String | Int | Float | Bool | Slice | Struct | Map | 266 | +-----------------+--------+----------+-------+----------------+------------------------------+------------------------+ 267 | | House Stark | 100210 | 3.14159 | false | [Arya Bran] | {ID:10010 Name:Jon Snow} | map[10086:Sansa Stark] | 268 | | House Lannister | 2131 | 1.234234 | true | [Tywin Tyrion] | {ID:23019 Name:Queen Cersei} | map[33489:Ser Jaime] | 269 | +-----------------+--------+----------+-------+----------------+------------------------------+------------------------+`}, 270 | } 271 | 272 | for _, tt := range tb { 273 | So("\n"+tt.content, ShouldEqual, tt.expected) 274 | } 275 | }) 276 | }) 277 | } 278 | --------------------------------------------------------------------------------