├── .gitignore
├── LICENSE
├── README.md
├── cell
├── cell.go
├── column.go
└── data.go
├── color
└── color.go
├── doc
├── api.md
├── demo.md
├── errors.md
├── safe_table_demo.md
└── simple_table_demo.md
├── exception
├── base.go
├── columns.go
├── file.go
└── row.go
├── go.mod
├── go.sum
├── gotable.go
├── gotable_test.go
├── table
├── base.go
├── kind.go
├── print.go
├── safe.go
├── set.go
└── table.go
├── test_csv.csv
└── util
├── drop.go
├── file.go
└── string.go
/.gitignore:
--------------------------------------------------------------------------------
1 | # idea
2 | .idea
3 |
4 | # cmd
5 | cmd
6 |
7 | # Mac OS X
8 | .DS_Store
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GOTABLE License
2 |
3 | Copyright (c) 2021 TCatTime
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 | # gotable 5: Safe
2 | Generate beautiful ASCII tables.
3 |
4 | ```go
5 | package main
6 |
7 | import (
8 | "fmt"
9 | "github.com/liushuochen/gotable"
10 | )
11 |
12 | func main() {
13 | table, err := gotable.Create("version", "description")
14 | if err != nil {
15 | fmt.Println("Create table failed: ", err.Error())
16 | return
17 | }
18 |
19 | table.AddRow([]string{"gotable 5", "Safe: New table type to enhance concurrency security"})
20 | table.AddRow([]string{"gotable 4", "Colored: Print colored column"})
21 | table.AddRow([]string{"gotable 3", "Storage: Store the table data as a file"})
22 | table.AddRow([]string{"gotable 2", "Simple: Use simpler APIs to control table"})
23 | table.AddRow([]string{"gotable 1", "Gotable: Print a beautiful ASCII table"})
24 |
25 | fmt.Println(table)
26 | }
27 |
28 | ```
29 |
30 | ```text
31 | +-----------+------------------------------------------------------+
32 | | version | description |
33 | +-----------+------------------------------------------------------+
34 | | gotable 5 | Safe: New table type to enhance concurrency security |
35 | | gotable 4 | Colored: Print colored column |
36 | | gotable 3 | Storage: Store the table data as a file |
37 | | gotable 2 | Simple: Use simpler APIs to control table |
38 | | gotable 1 | Gotable: Print a beautiful ASCII table |
39 | +-----------+------------------------------------------------------+
40 |
41 | ```
42 |
43 |
44 | ## Reference
45 | Please refer to guide:
46 | [gotable guide](https://blog.csdn.net/TCatTime/article/details/103068260#%E8%8E%B7%E5%8F%96gotable)
47 |
48 |
49 | ## Supported character set
50 | * ASCII
51 | * Chinese characters
52 |
53 |
54 | ## API
55 | Please refer to '[gotable APIs](doc/api.md)' for more gotable API information.
56 |
57 |
58 | ## Demo
59 | Please refer to [gotable demo page](doc/demo.md) for more demos code.
60 |
61 |
62 | ## Error type
63 | Please refer to this guide '[error type](doc/errors.md)' for more gotable error information.
64 |
--------------------------------------------------------------------------------
/cell/cell.go:
--------------------------------------------------------------------------------
1 | package cell
2 |
3 | type Cell interface {
4 | String() string
5 | Length() int
6 | Original() string
7 | }
8 |
--------------------------------------------------------------------------------
/cell/column.go:
--------------------------------------------------------------------------------
1 | package cell
2 |
3 | import (
4 | "github.com/liushuochen/gotable/color"
5 | "github.com/liushuochen/gotable/util"
6 | )
7 |
8 | const (
9 | AlignCenter = iota
10 | AlignLeft
11 | AlignRight
12 | )
13 |
14 | type Column struct {
15 | name string
16 | coloredName string
17 | defaultValue string
18 | align int
19 | length int
20 | }
21 |
22 | func CreateColumn(name string) *Column {
23 | h := &Column{
24 | name: name,
25 | coloredName: name,
26 | defaultValue: "",
27 | align: AlignCenter,
28 | length: util.Length(name),
29 | }
30 | return h
31 | }
32 |
33 | func (h *Column) String() string {
34 | return h.coloredName
35 | }
36 |
37 | func (h *Column) Original() string {
38 | return h.name
39 | }
40 |
41 | func (h *Column) Length() int {
42 | return h.length
43 | }
44 |
45 | func (h *Column) Default() string {
46 | return h.defaultValue
47 | }
48 |
49 | func (h *Column) SetDefault(value string) {
50 | h.defaultValue = value
51 | }
52 |
53 | func (h *Column) Align() int {
54 | return h.align
55 | }
56 |
57 | func (h *Column) AlignString() string {
58 | switch h.align {
59 | case AlignCenter:
60 | return "center"
61 | case AlignLeft:
62 | return "left"
63 | case AlignRight:
64 | return "right"
65 | default:
66 | return "unknown"
67 | }
68 | }
69 |
70 | func (h *Column) SetAlign(mode int) {
71 | switch mode {
72 | case AlignLeft:
73 | h.align = AlignLeft
74 | case AlignRight:
75 | h.align = AlignRight
76 | default:
77 | h.align = AlignCenter
78 | }
79 | }
80 |
81 | func (h *Column) Equal(other *Column) bool {
82 | functions := []func(o *Column) bool{
83 | h.nameEqual,
84 | h.lengthEqual,
85 | h.defaultEqual,
86 | h.alignEqual,
87 | }
88 |
89 | for _, function := range functions {
90 | if !function(other) {
91 | return false
92 | }
93 | }
94 | return true
95 | }
96 |
97 | func (h *Column) SetColor(displayType, font, background int) {
98 | c := new(color.Color)
99 | c.Display = displayType
100 | c.Font = font
101 | c.Background = background
102 | h.coloredName = c.Combine(h.Original())
103 | return
104 | }
105 |
106 | func (h *Column) Colorful() bool {
107 | return h.String() != h.Original()
108 | }
109 |
110 | func (h *Column) nameEqual(other *Column) bool {
111 | return h.Original() == other.Original()
112 | }
113 |
114 | func (h *Column) lengthEqual(other *Column) bool {
115 | return h.Length() == other.Length()
116 | }
117 |
118 | func (h *Column) defaultEqual(other *Column) bool {
119 | return h.Default() == other.Default()
120 | }
121 |
122 | func (h *Column) alignEqual(other *Column) bool {
123 | return h.Align() == other.Align()
124 | }
125 |
--------------------------------------------------------------------------------
/cell/data.go:
--------------------------------------------------------------------------------
1 | package cell
2 |
3 | import (
4 | "github.com/liushuochen/gotable/util"
5 | )
6 |
7 | type Data struct {
8 | value string
9 | length int
10 | }
11 |
12 | func CreateData(value string) *Data {
13 | d := new(Data)
14 | d.value = value
15 | d.length = util.Length(value)
16 | return d
17 | }
18 |
19 | func CreateEmptyData() *Data {
20 | return CreateData("")
21 | }
22 |
23 | func (d *Data) String() string {
24 | return d.value
25 | }
26 |
27 | func (d *Data) Length() int {
28 | return d.length
29 | }
30 |
31 | func (d *Data) Original() string {
32 | return d.String()
33 | }
34 |
--------------------------------------------------------------------------------
/color/color.go:
--------------------------------------------------------------------------------
1 | package color
2 |
3 | import "fmt"
4 |
5 | type Color struct {
6 | Display int
7 | Font int
8 | Background int
9 | }
10 |
11 | // Combine into a terminal escape sequence
12 | func (c *Color) Combine(message string) string {
13 | value := ""
14 | if c.Background == 0 {
15 | value = fmt.Sprintf("\033[%d;%dm%s\033[0m", c.Display, c.Font, message)
16 | } else {
17 | value = fmt.Sprintf("\033[%d;%d;%dm%s\033[0m", c.Display, c.Font, c.Background, message)
18 | }
19 | return value
20 | }
21 |
--------------------------------------------------------------------------------
/doc/api.md:
--------------------------------------------------------------------------------
1 | # API
2 | This section describes the gotable APIs.
3 |
4 | [Return to the home page](../README.md)
5 |
6 |
7 |
8 |
9 |
10 | ## github.com/liushuochen/gotable
11 |
12 | ### Create a simple table
13 | ```go
14 | func Create(columns ...string) (*table.Table, error)
15 | ```
16 |
17 |
18 |
19 | ### Create a simple table from struct
20 |
21 | ```go
22 | func CreateByStruct(v interface{}) (*table.Table, error)
23 | ```
24 |
25 |
26 |
27 | ### Create a safe table
28 |
29 | ```go
30 | func CreateSafeTable(columns ...string) (*table.SafeTable, error)
31 | ```
32 |
33 |
34 |
35 | ### Get version
36 |
37 | ```go
38 | func Version() string
39 | ```
40 |
41 |
42 |
43 | ### Get version list
44 |
45 | ```go
46 | func Versions() []string
47 | ```
48 |
49 |
50 |
51 | ### Specify default value
52 |
53 | The ```gotable.Default``` constant replaces the default value stored in column. Refer to Set Default Values in the Demo
54 | section for more information.
55 |
56 | ```go
57 | gotable.Default
58 | ```
59 |
60 |
61 |
62 | ### Load data from file
63 |
64 | Currently,csv and json file are supported.
65 | ```go
66 | func Read(path string) (*table.Table, error)
67 | ```
68 |
69 |
70 |
71 | ### Color control
72 |
73 | The following constants are used in conjunction with the ```*table.SetColumnColor``` method to change the column color.
74 | #### display type
75 | Default display
76 | ```go
77 | gotable.TerminalDefault
78 | ```
79 | Fonts are highlighted
80 | ```go
81 | gotable.Highlight
82 | ```
83 | Underline
84 | ```go
85 | gotable.Underline
86 | ```
87 | Font flash
88 | ```go
89 | gotable.Flash
90 | ```
91 | #### color
92 | ```go
93 | gotable.Black
94 | gotable.Red
95 | gotable.Green
96 | gotable.Yellow
97 | gotable.Blue
98 | gotable.Purple
99 | gotable.Cyan
100 | gotable.Write
101 | ```
102 |
103 | Do not set the background color
104 | ```go
105 | gotable.NoneBackground
106 | ```
107 |
108 |
109 |
110 |
111 |
112 | ## APIs for simple table type(*table.Table)
113 |
114 | ### Clear data
115 | The clear method is used to clear all data in the table, include columns and rows.
116 | ```go
117 | func (tb *Table) Clear()
118 | ```
119 |
120 |
121 |
122 | ### Get table type
123 |
124 | The type method returns a type of table.
125 | ```go
126 | func (tb *Table) Type() string
127 | ```
128 |
129 |
130 |
131 | ### Add row
132 |
133 | Add a row to the table. Support Map and Slice. See the Demo section for more information.
134 | ```go
135 | func (tb *Table) AddRow(row interface{}) error
136 | ```
137 |
138 |
139 |
140 | ### Add a list of rows
141 |
142 | Method ```AddRows``` add a list of rows. It returns a slice that consists of adding failed rows.
143 |
144 | ```go
145 | func (tb *Table) AddRows(rows []map[string]string) []map[string]string
146 | ```
147 |
148 |
149 |
150 | ### Add column
151 |
152 | ```go
153 | func (tb *Table) AddColumn(column string) error
154 | ```
155 |
156 |
157 |
158 | ### Print table
159 |
160 | ```*Table``` implements ```fmt.Stringer``` interface, so you can use the ```fmt.Print```, ```fmt.Printf``` functions
161 | and so on to print the contents of the table instance.
162 |
163 | ```go
164 | func (tb *Table) String() string
165 | ```
166 |
167 |
168 |
169 | ### Set default value
170 |
171 | By default, the default value for all columns is an empty string.
172 |
173 | ```go
174 | func (b *base) SetDefault(column string, defaultValue string)
175 | ```
176 |
177 |
178 |
179 | ### Drop default value
180 |
181 | ```go
182 | func (b *base) DropDefault(column string)
183 | ```
184 |
185 |
186 |
187 | ### Get default value
188 |
189 | Use table method ```GetDefault``` to get default value of column. If column does not exist in the table, the method returns an empty string.
190 |
191 | ```go
192 | func (b *base) GetDefault(column string) string
193 | ```
194 |
195 |
196 |
197 | ### Get default map
198 |
199 | Use table method ```GetDefaults``` to get default map of head.
200 |
201 | ```go
202 | func (b *base) GetDefaults() map[string]string
203 | ```
204 |
205 |
206 |
207 | ### Arrange: center, align left or align right
208 |
209 |
By default, the table is centered. You can set a header to be left
210 | aligned or right aligned. See the next section for more details on how
211 | to use it.
212 |
213 | ```go
214 | func (tb *Table) Align(column string, mode int)
215 | ```
216 |
217 |
218 |
219 | ### Check empty
220 |
221 | Use table method ```Empty``` to check if the table is empty.
222 |
223 | ```go
224 | func (tb *Table) Empty() bool
225 | ```
226 |
227 |
228 |
229 | ### Get list of columns
230 |
231 | Use table method ```GetColumns``` to get a list of columns.
232 |
233 | ```go
234 | func (b *base) GetColumns() []string
235 | ```
236 |
237 |
238 |
239 | ### Get values map
240 |
241 | Use table method ```GetValues``` to get the map that save values.
242 | ```go
243 | func (tb *Table) GetValues() []map[string]string
244 | ```
245 |
246 |
247 |
248 | ### Check value exists
249 |
250 | ```go
251 | func (tb *Table) Exist(value map[string]string) bool
252 | ```
253 |
254 |
255 |
256 | ### Get table length
257 |
258 | ```go
259 | func (tb *Table) Length() int
260 | ```
261 |
262 |
263 |
264 | ### To JSON string
265 |
266 | Use table method ```JSON``` to convert the table to JSON format.
267 | The argument ```indent``` indicates the number of indents.
268 | If the argument ```indent``` is less than or equal to 0, then the ```JSON``` method unindents.
269 | ```go
270 | func (tb *Table) JSON(indent int) (string, error)
271 | ```
272 |
273 |
274 |
275 | ### To XML string
276 |
277 | Use table method ```XML``` to convert the table to XML format.
278 | The argument ```indent``` indicates the number of indents.
279 | If the argument ```indent``` is less than or equal to 0, then the ```XML``` method unindents.
280 | ```go
281 | func (tb *Table) XML(indent int) string
282 | ```
283 |
284 |
285 |
286 | ### Save the table data to a JSON file
287 |
288 | Use table method ```ToJsonFile``` to save the table data to a JSON file.
289 | ```go
290 | func (tb *Table) ToJsonFile(path string, indent int) error
291 | ```
292 |
293 |
294 |
295 | ### Save the table data to a CSV file
296 |
297 | Use table method ```ToCSVFile``` to save the table data to a CSV file.
298 | ```go
299 | func (tb *Table) ToCSVFile(path string) error
300 | ```
301 |
302 |
303 |
304 | ### Close border
305 |
306 | Use table method ```CloseBorder``` to close table border.
307 | ```go
308 | func (tb *Table) CloseBorder()
309 | ```
310 |
311 |
312 |
313 | ### Open border
314 |
315 | Use table method ```OpenBorder``` to open table border. By default, the border property is turned on.
316 | ```go
317 | func (tb *Table) OpenBorder()
318 | ```
319 |
320 |
321 |
322 | ### Has column
323 |
324 | Table method ```HasColumn``` determine whether the column is included.
325 | ```go
326 | func (tb *Table) HasColumn(column string) bool
327 | ```
328 |
329 |
330 |
331 | ### Check whether the columns of the two tables are the same
332 |
333 | Table method ```EqualColumns``` is used to check whether the columns of two tables are the same. This method returns
334 | true if the columns of the two tables are identical (length, name, order, alignment, default), and false otherwise.
335 | ```go
336 | func (tb *Table) EqualColumns(other *Table) bool
337 | ```
338 |
339 |
340 |
341 | ### Set column color
342 |
343 | Table method ```SetColumnColor``` is used to set the color of a specific column. The first parameter specifies the name
344 | of the column to be modified. The second parameter indicates the type of font to display. Refer to the Color control
345 | section in this document for more information. The third and fourth parameters specify the font and background color.
346 | ```go
347 | func (tb *Table) SetColumnColor(columnName string, display, fount, background int)
348 | ```
349 |
350 |
351 |
352 | ### Custom ending string
353 |
354 | By default, a new blank line will print after table printing. You can designate your ending string by reset
355 | ```table.End```.
356 |
357 |
358 |
359 | ### Is simple table
360 |
361 | Method IsSimpleTable is used to check whether the table type is simple table.
362 |
363 | ```go
364 | func (b *base) IsSimpleTable() bool
365 | ```
366 |
367 |
368 |
369 | ### Is safe table
370 |
371 | Method IsSafeTable is used to check whether the table type is safe table.
372 |
373 | ```go
374 | func(b *base) IsSafeTable() bool
375 | ```
376 |
377 |
378 |
379 | ## APIs for safe table type(*table.SafeTable)
380 |
381 | ### Get table type
382 | The type method returns a type of table.
383 | ```go
384 | func (tb *Table) Type() string
385 | ```
386 |
387 |
388 |
389 | ### Is simple table
390 |
391 | Method IsSimpleTable is used to check whether the table type is simple table.
392 |
393 | ```go
394 | func (b *base) IsSimpleTable() bool
395 | ```
396 |
397 |
398 |
399 | ### Is safe table
400 |
401 | Method IsSafeTable is used to check whether the table type is safe table.
402 |
403 | ```go
404 | func(b *base) IsSafeTable() bool
405 | ```
406 |
407 |
408 |
409 | ### Add row
410 |
411 | Add a row to the safe table. Only support Map. See the Demo section for more information.
412 | ```go
413 | func (s *SafeTable) AddRow(row interface{}) error
414 | ```
415 |
416 |
417 |
418 | ### Add a list of rows
419 |
420 | Method ```AddRows``` add a list of rows. It returns a slice that consists of adding failed rows.
421 |
422 | ```go
423 | func (s *SafeTable) AddRows(rows []map[string]string) []map[string]string
424 | ```
425 |
426 |
427 |
428 |
429 |
430 | ### Custom ending string
431 |
432 | By default, a new blank line will print after table printing. You can designate your ending string by reset
433 | ```table.End```.
434 |
435 |
436 |
437 | ### Add column
438 |
439 | ```go
440 | func (s *SafeTable) AddColumn(column string) error
441 | ```
442 |
443 |
444 |
445 | ### Set default value
446 |
447 | By default, the default value for all columns is an empty string.
448 |
449 | ```go
450 | func (b *base) SetDefault(column string, defaultValue string)
451 | ```
452 |
453 |
454 |
455 | ### Drop default value
456 |
457 | ```go
458 | func (b *base) DropDefault(column string)
459 | ```
460 |
461 |
462 |
463 | ### Get default map
464 |
465 | Use table method ```GetDefaults``` to get default map of head.
466 |
467 | ```go
468 | func (b *base) GetDefaults() map[string]string
469 | ```
470 |
471 |
472 |
473 | ### Get table length
474 |
475 | ```go
476 | func (s *SafeTable) Length() int
477 | ```
478 |
479 |
480 |
481 | ### Print table
482 |
483 | ```*SafeTable``` implements ```fmt.Stringer``` interface, so you can use the ```fmt.Print```, ```fmt.Printf``` functions
484 | and so on to print the contents of the table instance.
485 |
486 | ```go
487 | func (st *SafeTable) String() string
488 | ```
489 |
490 |
491 |
492 | ### Get list of columns
493 |
494 | Use table method ```GetColumns``` to get a list of columns.
495 |
496 | ```go
497 | func (b *base) GetColumns() []string
498 | ```
499 |
500 |
--------------------------------------------------------------------------------
/doc/demo.md:
--------------------------------------------------------------------------------
1 | # Gotable Demo
2 | In this section, we have written some demos for your reference.
3 | Click [here](../README.md) to return to home page.
4 |
5 | # Demo content
6 | * [simple table](simple_table_demo.md)
7 | * [safe table](safe_table_demo.md)
8 |
--------------------------------------------------------------------------------
/doc/errors.md:
--------------------------------------------------------------------------------
1 | # Gotable error types
2 |
3 | In this section, we introduce the error types defined in gotable. By default, we will still return the original
4 | ```Error``` interface. The following code demonstrates how to properly handle errors returned by ```gotable```.
5 |
6 | ```go
7 | package main
8 |
9 | import (
10 | "fmt"
11 | "github.com/liushuochen/gotable"
12 | "github.com/liushuochen/gotable/exception"
13 | )
14 |
15 | func main() {
16 | table, err := gotable.ReadFromJSONFile("cmd/fun.csv")
17 | if err != nil {
18 | switch err.(type) {
19 | case *exception.FileDoNotExistError:
20 | exp, _ := err.(*exception.FileDoNotExistError)
21 | fmt.Printf("file %s dot exit: %s", exp.Filename(), err.Error())
22 | default:
23 | fmt.Println(err.Error())
24 | }
25 | return
26 | }
27 | fmt.Println(table)
28 | }
29 |
30 | ```
31 |
32 | [Return to the home page](../README.md)
33 |
34 | ## FileDoNotExistError
35 | This error type indicates that the filename was not found in the server. It has a public method
36 | ```*FileDoNotExistError.Filename() string``` that returns the wrong filename.
37 |
38 | ## NotARegularCSVFileError
39 | This error type indicates that the given filename is not a valid csv. It has a public method
40 | ```*NotARegularCSVFileError.Filename() string``` that returns the wrong CSV filename.
41 |
42 | ## NotARegularJSONFileError
43 | This error type indicates that the given filename is not a valid JSON. It has a public method
44 | ```*NotARegularJSONFileError.Filename() string``` that returns the wrong JSON filename.
45 |
46 | ## NotGotableJSONFormatError
47 | This error type indicates that the data format stored in the JSON file can not be parsed as a table.
48 | It has a public method ```*NotGotableJSONFormatError.Filename() string``` that returns the wrong JSON filename.
49 |
50 | ## UnsupportedRowTypeError
51 | This error type indicates that the row data structure is not support. It has a public method
52 | ```*UnsupportedRowTypeError.Type() string``` that returns the wrong type name.
53 |
54 | ## ColumnsLengthError
55 | This error type indicates that column's length not greater than 0.
56 |
57 | ## ColumnDoNotExistError
58 | A nonexistent column was found while adding a row. It has a public method ```*ColumnDoNotExistError.Name() string```
59 | that returns the nonexistent column name.
60 |
61 | ## RowLengthNotEqualColumnsError
62 | This error is raised when adding a row from a Slice when the length of the Slice is not equal with the length of the
63 | table column.
64 |
65 | ## UnSupportedFileTypeError
66 | When the file type read is not supported. It has a public mnethod ```*UnSupportedFileTypeError.Filename() string```
67 | that returns the wrong filename.
68 |
--------------------------------------------------------------------------------
/doc/safe_table_demo.md:
--------------------------------------------------------------------------------
1 | # Safe table demos
2 | In this section, we have written some demos about safe table type for your reference.
3 | Click [here](demo.md) to return to demo main page.
4 |
5 |
6 |
7 | ## Create a safe table
8 |
9 | Use ```gotable.CreateSafeTable``` function with a column(string slice or strings) to create a safe table.
10 | It returns a pointer of ```table.SafeTable``` struct and an error.
11 |
12 | ```go
13 | package main
14 |
15 | import (
16 | "fmt"
17 | "github.com/liushuochen/gotable"
18 | )
19 |
20 | func main() {
21 | table, err := gotable.CreateSafeTable("China", "US", "UK")
22 | if err != nil {
23 | fmt.Println("Create table failed: ", err.Error())
24 | return
25 | }
26 | }
27 |
28 | ```
29 |
30 |
31 |
32 | ## Print table
33 |
34 | You can print the contents of the `SafeTable` instance to STDOUT using the print function in the ```fmt``` standard library.
35 | For example ```fmt.Println```, ```fmt.Print``` and so on.
36 |
37 | ```go
38 | package main
39 |
40 | import (
41 | "fmt"
42 | "github.com/liushuochen/gotable"
43 | )
44 |
45 | func main() {
46 | table, _ := gotable.CreateSafeTable("ID", "Name")
47 | row := []string{"001", "gotable"}
48 | _ = table.AddRow(row)
49 | fmt.Println(table)
50 | // outputs:
51 | // +-----+---------+
52 | // | ID | Name |
53 | // +-----+---------+
54 | // | 001 | gotable |
55 | // +-----+---------+
56 | }
57 |
58 | ```
59 |
60 |
61 |
62 | ## Add row
63 |
64 | Use safe table method ```AddRow``` to add a new row to the table. Method ```AddRow``` supports Map and Slice.
65 | argument:
66 | * For Map argument, you must put the data from each row into a Map and use column-data as key-value pairs. If the Map
67 | does not contain a column, the table sets it to the default value(see more information in 'Set Default' section). If
68 | the Map contains a column that does not exist, the ```AddRow``` method returns an error.
69 | * For Slice argument, you must ensure that the slice length is equal to the column length. Method will automatically
70 | mapping values in Slice and columns. The default value cannot be omitted and must use gotable.Default constant.
71 |
72 | ```go
73 | package main
74 |
75 | import (
76 | "fmt"
77 | "github.com/liushuochen/gotable"
78 | )
79 |
80 | func main() {
81 | table, err := gotable.CreateSafeTable("China", "US", "French")
82 | if err != nil {
83 | fmt.Println("Create table failed: ", err.Error())
84 | return
85 | }
86 |
87 | // Use map
88 | row := make(map[string]string)
89 | row["China"] = "Beijing"
90 | row["US"] = "Washington, D.C."
91 | row["French"] = "Paris"
92 | err = table.AddRow(row)
93 | if err != nil {
94 | fmt.Println("Add value to table failed: ", err.Error())
95 | return
96 | }
97 |
98 | // Use Slice
99 | row2 := []string{"Yinchuan", "Los Angeles", "Orleans"}
100 | err = table.AddRow(row2)
101 | if err != nil {
102 | fmt.Println("Add value to table failed: ", err.Error())
103 | return
104 | }
105 |
106 | fmt.Println(table)
107 | // outputs:
108 | // +----------+------------------+---------+
109 | // | China | US | French |
110 | // +----------+------------------+---------+
111 | // | Beijing | Washington, D.C. | Paris |
112 | // | Yinchuan | Los Angeles | Orleans |
113 | // +----------+------------------+---------+
114 | }
115 |
116 | ```
117 |
118 |
119 |
120 | ## Add a new column to a table
121 |
122 | ```go
123 | package main
124 |
125 | import (
126 | "fmt"
127 | "github.com/liushuochen/gotable"
128 | )
129 |
130 | func main() {
131 | table, err := gotable.CreateSafeTable("China", "US", "French")
132 | if err != nil {
133 | fmt.Println("Create table failed: ", err.Error())
134 | return
135 | }
136 |
137 | row := make(map[string]string)
138 | row["China"] = "Beijing"
139 | row["US"] = "Washington, D.C."
140 | row["French"] = "Paris"
141 | err = table.AddRow(row)
142 | if err != nil {
143 | fmt.Println("Add value to table failed: ", err.Error())
144 | return
145 | }
146 |
147 | err = table.AddColumn("Japan")
148 | if err != nil {
149 | fmt.Println("Add column failed: ", err.Error())
150 | return
151 | }
152 |
153 | fmt.Println(table)
154 | // outputs:
155 | // +---------+------------------+--------+-------+
156 | // | China | US | French | Japan |
157 | // +---------+------------------+--------+-------+
158 | // | Beijing | Washington, D.C. | Paris | |
159 | // +---------+------------------+--------+-------+
160 | }
161 |
162 |
163 | ```
164 |
165 |
166 |
167 | ## Set default value
168 |
169 | You can use the ```SetDefault``` method to set a default value for a column. By default, the default value is an empty
170 | string. For Map structure data, when adding a row, omitting a column indicates that the value of column in the row is
171 | the default value. You can also use the ```gotable.Default``` constant to indicate that a column in the row is the
172 | default value. For Slice structure data, when adding a row, you must explicitly specify the ```gotable.Default```
173 | constant to indicate that the value for a column is the default value.
174 |
175 | ```go
176 | package main
177 |
178 | import (
179 | "fmt"
180 | "github.com/liushuochen/gotable"
181 | )
182 |
183 | func main() {
184 | tb, err := gotable.CreateSafeTable("China", "US", "UK")
185 | if err != nil {
186 | fmt.Println("Create table failed: ", err.Error())
187 | return
188 | }
189 |
190 | tb.SetDefault("UK", "---")
191 | row := make(map[string]string)
192 | row["China"] = "Beijing"
193 | row["US"] = "Washington, D.C."
194 | row["UK"] = "London"
195 | _ = tb.AddRow(row)
196 |
197 | row2 := make(map[string]string)
198 | row2["China"] = "Hangzhou"
199 | row2["US"] = "NewYork"
200 | _ = tb.AddRow(row2)
201 | }
202 |
203 | ```
204 |
205 |
206 |
207 | ## Check the table type is safe table
208 |
209 | ```go
210 | package main
211 |
212 | import (
213 | "fmt"
214 | "github.com/liushuochen/gotable"
215 | )
216 |
217 | func main() {
218 | table, err := gotable.CreateSafeTable("China", "US", "UK")
219 | if err != nil {
220 | fmt.Println("Create table failed: ", err.Error())
221 | return
222 | }
223 |
224 | fmt.Println(table.IsSafeTable())
225 | // output: true
226 | }
227 |
228 | ```
229 |
230 |
231 |
232 | ## Get default value
233 |
234 | ```go
235 | package main
236 |
237 | import (
238 | "fmt"
239 | "github.com/liushuochen/gotable"
240 | )
241 |
242 | func main() {
243 | table, err := gotable.CreateSafeTable("China", "US", "UK")
244 | if err != nil {
245 | fmt.Println("Create table failed: ", err.Error())
246 | return
247 | }
248 |
249 | table.SetDefault("China", "Beijing")
250 | table.SetDefault("China", "Hangzhou")
251 | fmt.Println(table.GetDefault("China"))
252 | // Hangzhou
253 | }
254 |
255 | ```
256 |
257 |
258 |
259 | ## Get default map
260 |
261 | ```go
262 | package main
263 |
264 | import (
265 | "fmt"
266 | "github.com/liushuochen/gotable"
267 | )
268 |
269 | func main() {
270 | table, err := gotable.CreateSafeTable("China", "US", "UK")
271 | if err != nil {
272 | fmt.Println("Create table failed: ", err.Error())
273 | return
274 | }
275 |
276 | table.SetDefault("UK", "London")
277 | fmt.Println(table.GetDefaults())
278 | // map[China: UK:London US:]
279 | table.DropDefault("UK")
280 | fmt.Println(table.GetDefaults())
281 | // map[China: UK: US:]
282 | }
283 |
284 | ```
285 |
286 |
287 |
288 | ## Drop default value
289 |
290 | ```go
291 | package main
292 |
293 | import (
294 | "fmt"
295 | "github.com/liushuochen/gotable"
296 | )
297 |
298 | func main() {
299 | table, err := gotable.CreateSafeTable("China", "US", "UK")
300 | if err != nil {
301 | fmt.Println("Create table failed: ", err.Error())
302 | return
303 | }
304 |
305 | table.SetDefault("UK", "London")
306 | fmt.Println(table.GetDefaults())
307 | // map[China: UK:London US:]
308 | table.DropDefault("UK")
309 | fmt.Println(table.GetDefaults())
310 | // map[China: UK: US:]
311 | }
312 | ```
313 |
314 |
315 |
316 | ## Get table length
317 |
318 | ```go
319 | package main
320 |
321 | import (
322 | "fmt"
323 | "github.com/liushuochen/gotable"
324 | )
325 |
326 | func main() {
327 | tb, err := gotable.CreateSafeTable("Name", "ID", "salary")
328 | if err != nil {
329 | fmt.Println("Create table failed: ", err.Error())
330 | return
331 | }
332 |
333 | length := tb.Length()
334 | fmt.Printf("Before insert values, the value of length is: %d\n", length)
335 | // Before insert values, the value of length is: 0
336 |
337 | rows := make([]map[string]string, 0)
338 | for i := 0; i < 3; i++ {
339 | row := make(map[string]string)
340 | row["Name"] = fmt.Sprintf("employee-%d", i)
341 | row["ID"] = fmt.Sprintf("00%d", i)
342 | row["salary"] = "60000"
343 | rows = append(rows, row)
344 | }
345 |
346 | tb.AddRows(rows)
347 |
348 | length = tb.Length()
349 | fmt.Printf("After insert values, the value of length is: %d\n", length)
350 | // After insert values, the value of length is: 3
351 | }
352 |
353 | ```
354 |
355 |
356 |
357 | ## Check table empty
358 |
359 | ```go
360 | package main
361 |
362 | import (
363 | "fmt"
364 | "github.com/liushuochen/gotable"
365 | )
366 |
367 | func main() {
368 | table, err := gotable.CreateSafeTable("China", "US", "UK")
369 | if err != nil {
370 | fmt.Println("Create table failed: ", err.Error())
371 | return
372 | }
373 |
374 | if table.Empty() {
375 | fmt.Println("table is empty.")
376 | }
377 | }
378 |
379 | ```
380 |
381 |
382 |
383 | ## Get list of columns
384 |
385 | ```go
386 | package main
387 |
388 | import (
389 | "fmt"
390 | "github.com/liushuochen/gotable"
391 | )
392 |
393 | func main() {
394 | tb, err := gotable.CreateSafeTable("China", "US", "UK")
395 | if err != nil {
396 | fmt.Println("Create table failed: ", err.Error())
397 | return
398 | }
399 |
400 | fmt.Println(tb.GetColumns())
401 | // [China US UK]
402 | }
403 |
404 | ```
405 |
406 |
--------------------------------------------------------------------------------
/doc/simple_table_demo.md:
--------------------------------------------------------------------------------
1 | # Simple table demos
2 | In this section, we have written some demos about simple table type for your reference.
3 | Click [here](demo.md) to return to demo main page.
4 |
5 |
6 |
7 | ## Create a table
8 | Use ```gotable.Create``` function with a column(string slice or strings) to create a table.
9 | It returns a pointer of ```table.Table``` struct and an error.
10 |
11 | ```go
12 | package main
13 |
14 | import (
15 | "fmt"
16 | "github.com/liushuochen/gotable"
17 | )
18 |
19 | func main() {
20 | table, err := gotable.Create("China", "US", "UK")
21 | if err != nil {
22 | fmt.Println("Create table failed: ", err.Error())
23 | return
24 | }
25 | }
26 |
27 | ```
28 |
29 |
30 |
31 | ## Create a table from struct
32 |
33 | ```go
34 | package main
35 |
36 | import (
37 | "fmt"
38 | "github.com/liushuochen/gotable"
39 | )
40 |
41 | type Student struct {
42 | Id string `gotable:"id"` // Specify the column name of the table through the struct tag `gotable`
43 | Name string
44 | }
45 |
46 | func main() {
47 | table, err := gotable.CreateByStruct(&Student{})
48 | if err != nil {
49 | fmt.Println("Create table failed: ", err.Error())
50 | return
51 | }
52 | }
53 |
54 | ```
55 |
56 |
57 |
58 | ## Load data from file
59 |
60 | Currently, csv and json file are supported.
61 | ```go
62 | package main
63 |
64 | import (
65 | "fmt"
66 | "github.com/liushuochen/gotable"
67 | )
68 |
69 | func main() {
70 | table, err := gotable.Read("cmd/demo.csv")
71 | if err != nil {
72 | fmt.Println("Create table failed: ", err.Error())
73 | return
74 | }
75 |
76 | }
77 |
78 | ```
79 |
80 |
81 |
82 | ## Add row
83 |
84 | Use table method ```AddRow``` to add a new row to the table. Method ```AddRow``` supports Map and Slice argument:
85 | * For Map argument, you must put the data from each row into a Map and use column-data as key-value pairs. If the Map
86 | does not contain a column, the table sets it to the default value(see more information in 'Set Default' section). If
87 | the Map contains a column that does not exist, the ```AddRow``` method returns an error.
88 |
89 | * For Slice argument, you must ensure that the slice length is equal to the column length. The ```AddRow``` method
90 | automatically mapping values in Slice and columns. The default value cannot be omitted and must use
91 | ```gotable.Default``` constant.
92 |
93 | ```go
94 | package main
95 |
96 | import (
97 | "fmt"
98 | "github.com/liushuochen/gotable"
99 | )
100 |
101 | func main() {
102 | table, err := gotable.Create("China", "US", "French")
103 | if err != nil {
104 | fmt.Println("Create table failed: ", err.Error())
105 | return
106 | }
107 |
108 | // Use map
109 | row := make(map[string]string)
110 | row["China"] = "Beijing"
111 | row["US"] = "Washington, D.C."
112 | row["French"] = "Paris"
113 | err = table.AddRow(row)
114 | if err != nil {
115 | fmt.Println("Add value to table failed: ", err.Error())
116 | return
117 | }
118 |
119 | // Use Slice
120 | row2 := []string{"Yinchuan", "Los Angeles", "Orleans"}
121 | err = table.AddRow(row2)
122 | if err != nil {
123 | fmt.Println("Add value to table failed: ", err.Error())
124 | return
125 | }
126 |
127 | fmt.Println(table)
128 | // outputs:
129 | // +----------+------------------+---------+
130 | // | China | US | French |
131 | // +----------+------------------+---------+
132 | // | Beijing | Washington, D.C. | Paris |
133 | // | Yinchuan | Los Angeles | Orleans |
134 | // +----------+------------------+---------+
135 | }
136 |
137 | ```
138 |
139 |
140 |
141 | ## Add rows
142 |
143 | Method ```AddRows``` add a list of rows. It returns a slice that
144 | consists of adding failed rows.
145 |
146 | ```go
147 | package main
148 |
149 | import (
150 | "fmt"
151 | "github.com/liushuochen/gotable"
152 | )
153 |
154 | func main() {
155 | table, err := gotable.Create("Name", "ID", "salary")
156 | if err != nil {
157 | fmt.Println("Create table failed: ", err.Error())
158 | return
159 | }
160 |
161 | rows := make([]map[string]string, 0)
162 | for i := 0; i < 3; i++ {
163 | row := make(map[string]string)
164 | row["Name"] = fmt.Sprintf("employee-%d", i)
165 | row["ID"] = fmt.Sprintf("00%d", i)
166 | row["salary"] = "60000"
167 | rows = append(rows, row)
168 | }
169 |
170 | table.AddRows(rows)
171 | fmt.Println(table)
172 | // outputs:
173 | // +------------+-----+--------+
174 | // | Name | ID | salary |
175 | // +------------+-----+--------+
176 | // | employee-0 | 000 | 60000 |
177 | // | employee-1 | 001 | 60000 |
178 | // | employee-2 | 002 | 60000 |
179 | // +------------+-----+--------+
180 | }
181 | ```
182 |
183 |
184 |
185 | ## Print table
186 |
187 | You can print the contents of the table instance to STDOUT using the print function in the ```fmt``` standard library.
188 | For example ```fmt.Println```, ```fmt.Print``` and so on.
189 |
190 | ```go
191 | package main
192 |
193 | import (
194 | "fmt"
195 | "github.com/liushuochen/gotable"
196 | )
197 |
198 | func main() {
199 | table, err := gotable.Create("China", "US", "UK")
200 | if err != nil {
201 | fmt.Println("Create table failed: ", err.Error())
202 | return
203 | }
204 |
205 | row := make(map[string]string)
206 | row["China"] = "Beijing"
207 | row["US"] = "Washington, D.C."
208 | row["UK"] = "London"
209 | table.AddRow(row)
210 |
211 | fmt.Println(table)
212 | // outputs:
213 | // +---------+------------------+--------+
214 | // | China | US | UK |
215 | // +---------+------------------+--------+
216 | // | Beijing | Washington, D.C. | London |
217 | // +---------+------------------+--------+
218 |
219 | fmt.Printf("%s", table)
220 | // outputs:
221 | // +---------+------------------+--------+
222 | // | China | US | UK |
223 | // +---------+------------------+--------+
224 | // | Beijing | Washington, D.C. | London |
225 | // +---------+------------------+--------+
226 |
227 | fmt.Print(table)
228 | // outputs:
229 | // +---------+------------------+--------+
230 | // | China | US | UK |
231 | // +---------+------------------+--------+
232 | // | Beijing | Washington, D.C. | London |
233 | // +---------+------------------+--------+
234 | }
235 |
236 | ```
237 |
238 |
239 |
240 | ## Clear data
241 |
242 | ```go
243 | package main
244 |
245 | import (
246 | "fmt"
247 | "github.com/liushuochen/gotable"
248 | )
249 |
250 | func main() {
251 | table, err := gotable.Create("Name", "ID", "salary")
252 | if err != nil {
253 | fmt.Println("Create table failed: ", err.Error())
254 | return
255 | }
256 |
257 | length := table.Length()
258 | fmt.Printf("Before insert values, the value of length is: %d\n", length)
259 | // Before insert values, the value of length is: 0
260 |
261 | rows := make([]map[string]string, 0)
262 | for i := 0; i < 3; i++ {
263 | row := make(map[string]string)
264 | row["Name"] = fmt.Sprintf("employee-%d", i)
265 | row["ID"] = fmt.Sprintf("00%d", i)
266 | row["salary"] = "60000"
267 | rows = append(rows, row)
268 | }
269 |
270 | table.AddRows(rows)
271 | fmt.Println(table)
272 | // outputs:
273 | // +------------+-----+--------+
274 | // | Name | ID | salary |
275 | // +------------+-----+--------+
276 | // | employee-0 | 000 | 60000 |
277 | // | employee-1 | 001 | 60000 |
278 | // | employee-2 | 002 | 60000 |
279 | // +------------+-----+--------+
280 |
281 | table.Clear()
282 | fmt.Println("After the table data is cleared...")
283 | fmt.Println(table)
284 | // output: After the table data is cleared...
285 | }
286 |
287 | ```
288 |
289 |
290 |
291 | ## Set default value
292 |
293 | You can use the ```SetDefault``` method to set a default value for a column. By default, the default value is an empty
294 | string. For Map structure data, when adding a row, omitting a column indicates that the value of column in the row is
295 | the default value. You can also use the ```gotable.Default``` constant to indicate that a column in the row is the
296 | default value. For Slice structure data, when adding a row, you must explicitly specify the ```gotable.Default```
297 | constant to indicate that the value for a column is the default value.
298 |
299 |
300 | ```go
301 | package main
302 |
303 | import (
304 | "fmt"
305 | "github.com/liushuochen/gotable"
306 | )
307 |
308 | func main() {
309 | table, err := gotable.Create("China", "US", "UK")
310 | if err != nil {
311 | fmt.Println("Create table failed: ", err.Error())
312 | return
313 | }
314 |
315 | table.SetDefault("China", "Xi'AN")
316 | table.SetDefault("US", "Los Angeles")
317 |
318 | row := make(map[string]string)
319 | row["China"] = "Beijing"
320 | row["US"] = "Washington D.C."
321 | row["UK"] = "London"
322 | table.AddRow(row)
323 |
324 | // China is omitted in Map, the value of China column in the row is changed to the default value(Xi'AN).
325 | row2 := make(map[string]string)
326 | row2["US"] = "NewYork"
327 | row2["UK"] = "Manchester"
328 | table.AddRow(row2)
329 |
330 | // Use the gotable.Default constant to indicate that the value of US is the default(Los Angeles)
331 | row3 := make(map[string]string)
332 | row3["China"] = "Hangzhou"
333 | row3["US"] = gotable.Default
334 | row3["UK"] = "Manchester"
335 | table.AddRow(row3)
336 |
337 | // Use gotable.Default in Slice.
338 | // Because the value of row4[1] is gotable.Default constant, the value for column[1](US) is the default value(Los Angeles)
339 | row4 := []string{"Qingdao", gotable.Default, "Oxford"}
340 | table.AddRow(row4)
341 |
342 | fmt.Println(table)
343 | // outputs:
344 | // +----------+-----------------+------------+
345 | // | China | US | UK |
346 | // +----------+-----------------+------------+
347 | // | Beijing | Washington D.C. | London |
348 | // | Xi'AN | NewYork | Manchester |
349 | // | Hangzhou | Los Angeles | Manchester |
350 | // | Qingdao | Los Angeles | Oxford |
351 | // +----------+-----------------+------------+
352 | }
353 |
354 | ```
355 |
356 |
357 |
358 | ## Drop default value
359 |
360 | ```go
361 | package main
362 |
363 | import (
364 | "fmt"
365 | "github.com/liushuochen/gotable"
366 | )
367 |
368 | func main() {
369 | table, err := gotable.Create("China", "US", "UK")
370 | if err != nil {
371 | fmt.Println("Create table failed: ", err.Error())
372 | return
373 | }
374 |
375 | table.SetDefault("UK", "London")
376 | fmt.Println(table.GetDefaults())
377 | // map[China: UK:London US:]
378 | table.DropDefault("UK")
379 | fmt.Println(table.GetDefaults())
380 | // map[China: UK: US:]
381 | }
382 |
383 | ```
384 |
385 |
386 |
387 | ## Get default value
388 |
389 | ```go
390 | package main
391 |
392 | import (
393 | "fmt"
394 | "github.com/liushuochen/gotable"
395 | )
396 |
397 | func main() {
398 | table, err := gotable.Create("China", "US", "UK")
399 | if err != nil {
400 | fmt.Println("Create table failed: ", err.Error())
401 | return
402 | }
403 |
404 | table.SetDefault("China", "Beijing")
405 | table.SetDefault("China", "Hangzhou")
406 | fmt.Println(table.GetDefault("China"))
407 | // Hangzhou
408 | }
409 |
410 | ```
411 |
412 |
413 |
414 | ## Get default map
415 |
416 | ```go
417 | package main
418 |
419 | import (
420 | "fmt"
421 | "github.com/liushuochen/gotable"
422 | )
423 |
424 | func main() {
425 | tb, err := gotable.Create("China", "US", "UK")
426 | if err != nil {
427 | fmt.Println("Create table failed: ", err.Error())
428 | return
429 | }
430 |
431 | tb.SetDefault("UK", "London")
432 | defaults := tb.GetDefaults()
433 | fmt.Println(defaults)
434 | // map[China: UK:London US:]
435 | }
436 |
437 | ```
438 |
439 |
440 |
441 | ## Add a new column to a table
442 |
443 | ```go
444 | package main
445 |
446 | import (
447 | "fmt"
448 | "github.com/liushuochen/gotable"
449 | )
450 |
451 | func main() {
452 | table, err := gotable.Create("China", "US", "UK")
453 | if err != nil {
454 | fmt.Println("Create table failed: ", err.Error())
455 | return
456 | }
457 |
458 | row := make(map[string]string)
459 | row["China"] = "Beijing"
460 | row["US"] = "Washington D.C."
461 | row["UK"] = "London"
462 | table.AddRow(row)
463 | table.AddColumn("Japan")
464 |
465 | fmt.Println(table)
466 | // outputs:
467 | // +---------+-----------------+--------+-------+
468 | // | China | US | UK | Japan |
469 | // +---------+-----------------+--------+-------+
470 | // | Beijing | Washington D.C. | London | |
471 | // +---------+-----------------+--------+-------+
472 | }
473 |
474 | ```
475 |
476 |
477 |
478 | ## Arrange: center, align left or align right
479 |
480 | To change the arrangement, there are three constants
481 | (```gotable.Center```, ```gotable.Left```, ```gotable.Right```) to
482 | choose from. By default, all arrangements is ```gotable.Center```.
483 |
484 | ```go
485 | package main
486 |
487 | import (
488 | "fmt"
489 | "github.com/liushuochen/gotable"
490 | )
491 |
492 | func main() {
493 | table, err := gotable.Create("China", "US", "UK")
494 | if err != nil {
495 | fmt.Println("Create table failed: ", err.Error())
496 | return
497 | }
498 |
499 | row := make(map[string]string)
500 | row["China"] = "Beijing"
501 | row["US"] = "Washington D.C."
502 | row["UK"] = "London"
503 | table.AddRow(row)
504 | table.Align("UK", gotable.Left)
505 |
506 | row2 := make(map[string]string)
507 | row2["US"] = "NewYork"
508 | row2["UK"] = "Manchester"
509 | table.AddRow(row2)
510 |
511 | fmt.Println(table)
512 | // outputs:
513 | // +---------+-----------------+------------+
514 | // | China | US |UK |
515 | // +---------+-----------------+------------+
516 | // | Beijing | Washington D.C. |London |
517 | // | | NewYork |Manchester |
518 | // +---------+-----------------+------------+
519 | }
520 |
521 | ```
522 |
523 |
524 |
525 | ## Check empty
526 |
527 | ```go
528 | package main
529 |
530 | import (
531 | "fmt"
532 | "github.com/liushuochen/gotable"
533 | )
534 |
535 | func main() {
536 | table, err := gotable.Create("China", "US", "UK")
537 | if err != nil {
538 | fmt.Println("Create table failed: ", err.Error())
539 | return
540 | }
541 |
542 | if table.Empty() {
543 | fmt.Println("table is empty.")
544 | }
545 | }
546 | ```
547 |
548 |
549 |
550 | ## Get list of columns
551 |
552 | ```go
553 | package main
554 |
555 | import (
556 | "fmt"
557 | "github.com/liushuochen/gotable"
558 | )
559 |
560 | func main() {
561 | tb, err := gotable.Create("China", "US", "UK")
562 | if err != nil {
563 | fmt.Println("Create table failed: ", err.Error())
564 | return
565 | }
566 |
567 | fmt.Println(tb.GetColumns())
568 | // [China US UK]
569 | }
570 |
571 | ```
572 |
573 |
574 |
575 | ## Get values map
576 |
577 | ```go
578 | package main
579 |
580 | import (
581 | "fmt"
582 | "github.com/liushuochen/gotable"
583 | )
584 |
585 | func main() {
586 | tb, err := gotable.Create("China", "US", "UK")
587 | if err != nil {
588 | fmt.Println("Create table failed: ", err.Error())
589 | return
590 | }
591 |
592 | tb.SetDefault("UK", "---")
593 | row := make(map[string]string)
594 | row["China"] = "Beijing"
595 | row["US"] = "Washington, D.C."
596 | row["UK"] = "London"
597 | _ = tb.AddRow(row)
598 |
599 | row2 := make(map[string]string)
600 | row2["China"] = "Hangzhou"
601 | row2["US"] = "NewYork"
602 | _ = tb.AddRow(row2)
603 | fmt.Println(tb.GetValues())
604 | // [map[China:Beijing UK:London US:Washington, D.C.] map[China:Hangzhou UK:--- US:NewYork]]
605 | }
606 |
607 | ```
608 |
609 |
610 |
611 | ## Check value exists
612 |
613 | ```go
614 | package main
615 |
616 | import (
617 | "fmt"
618 | "github.com/liushuochen/gotable"
619 | )
620 |
621 | func main() {
622 | tb, err := gotable.Create("Name", "ID", "salary")
623 | if err != nil {
624 | fmt.Println("Create table failed: ", err.Error())
625 | return
626 | }
627 |
628 | rows := make([]map[string]string, 0)
629 | for i := 0; i < 3; i++ {
630 | row := make(map[string]string)
631 | row["Name"] = fmt.Sprintf("employee-%d", i)
632 | row["ID"] = fmt.Sprintf("00%d", i)
633 | row["salary"] = "60000"
634 | rows = append(rows, row)
635 | }
636 |
637 | tb.AddRows(rows)
638 |
639 | row := make(map[string]string)
640 | row["salary"] = "60000"
641 | // check salary="60000" exists: true
642 | fmt.Println(tb.Exist(row))
643 |
644 | row["Name"] = "employee-5"
645 | // check salary="60000" && Name="employee-5" exists: false
646 | // The value of "employee-5" in Name do not exist
647 | fmt.Println(tb.Exist(row))
648 |
649 | row2 := make(map[string]string)
650 | row2["s"] = "60000"
651 | // check s="60000" exists: false
652 | // The table do not has a key named 's'
653 | fmt.Println(tb.Exist(row2))
654 | }
655 | ```
656 |
657 |
658 |
659 | ## Get table length
660 |
661 | ```go
662 | package main
663 |
664 | import (
665 | "fmt"
666 | "github.com/liushuochen/gotable"
667 | )
668 |
669 | func main() {
670 | tb, err := gotable.Create("Name", "ID", "salary")
671 | if err != nil {
672 | fmt.Println("Create table failed: ", err.Error())
673 | return
674 | }
675 |
676 | length := tb.Length()
677 | fmt.Printf("Before insert values, the value of length is: %d\n", length)
678 | // Before insert values, the value of length is: 0
679 |
680 | rows := make([]map[string]string, 0)
681 | for i := 0; i < 3; i++ {
682 | row := make(map[string]string)
683 | row["Name"] = fmt.Sprintf("employee-%d", i)
684 | row["ID"] = fmt.Sprintf("00%d", i)
685 | row["salary"] = "60000"
686 | rows = append(rows, row)
687 | }
688 |
689 | tb.AddRows(rows)
690 |
691 | length = tb.Length()
692 | fmt.Printf("After insert values, the value of length is: %d\n", length)
693 | // After insert values, the value of length is: 3
694 | }
695 |
696 | ```
697 |
698 |
699 |
700 | ## To JSON string
701 |
702 | ```go
703 | package main
704 |
705 | import (
706 | "fmt"
707 | "github.com/liushuochen/gotable"
708 | )
709 |
710 | func main() {
711 | tb, err := gotable.Create("Name", "ID", "salary")
712 | if err != nil {
713 | fmt.Println("Create table failed: ", err.Error())
714 | return
715 | }
716 |
717 | rows := make([]map[string]string, 0)
718 | for i := 0; i < 3; i++ {
719 | row := make(map[string]string)
720 | row["Name"] = fmt.Sprintf("employee-%d", i)
721 | row["ID"] = fmt.Sprintf("00%d", i)
722 | row["salary"] = "60000"
723 | rows = append(rows, row)
724 | }
725 |
726 | jsonString, err := tb.JSON(4)
727 | if err != nil {
728 | fmt.Println("ERROR: ", err.Error())
729 | return
730 | }
731 | fmt.Println(jsonString)
732 | // output: []
733 |
734 | tb.AddRows(rows)
735 |
736 | jsonString, err = tb.JSON(4)
737 | if err != nil {
738 | fmt.Println("ERROR: ", err.Error())
739 | return
740 | }
741 | fmt.Println(jsonString)
742 | // output:
743 | // [
744 | // {
745 | // "ID": "000",
746 | // "Name": "employee-0",
747 | // "salary": "60000"
748 | // },
749 | // {
750 | // "ID": "001",
751 | // "Name": "employee-1",
752 | // "salary": "60000"
753 | //
754 | //
755 | // "ID": "002",
756 | // "Name": "employee-2",
757 | // "salary": "60000"
758 | // }
759 | //]
760 | }
761 | ```
762 |
763 |
764 |
765 | ## To XML string
766 |
767 | ```go
768 | package main
769 |
770 | import (
771 | "fmt"
772 | "github.com/liushuochen/gotable"
773 | )
774 |
775 | func main() {
776 | table, err := gotable.Create("Name", "ID", "salary")
777 | if err != nil {
778 | fmt.Println("Create table failed: ", err.Error())
779 | return
780 | }
781 |
782 | rows := make([]map[string]string, 0)
783 | for i := 0; i < 3; i++ {
784 | row := make(map[string]string)
785 | row["Name"] = fmt.Sprintf("employee-%d", i)
786 | row["ID"] = fmt.Sprintf("00%d", i)
787 | row["salary"] = "60000"
788 | rows = append(rows, row)
789 | }
790 |
791 | table.AddRows(rows)
792 |
793 | content := table.XML(2)
794 | fmt.Println(content)
795 | // outputs:
796 | //
797 | //
798 | //
799 | // employee-0
800 | // 000
801 | // 60000
802 | //
803 | //
804 | // employee-1
805 | // 001
806 | // 60000
807 | //
808 | //
809 | // employee-2
810 | // 002
811 | // 60000
812 | //
813 | //
814 | }
815 |
816 | ```
817 |
818 |
819 |
820 | ## Save the table data to a JSON file
821 |
822 | ```go
823 | package main
824 |
825 | import (
826 | "fmt"
827 | "github.com/liushuochen/gotable"
828 | )
829 |
830 | func main() {
831 | tb, err := gotable.Create("Name", "ID", "salary")
832 | if err != nil {
833 | fmt.Println("Create table failed: ", err.Error())
834 | return
835 | }
836 |
837 | rows := make([]map[string]string, 0)
838 | for i := 0; i < 3; i++ {
839 | row := make(map[string]string)
840 | row["Name"] = fmt.Sprintf("employee-%d", i)
841 | row["ID"] = fmt.Sprintf("00%d", i)
842 | row["salary"] = "60000"
843 | rows = append(rows, row)
844 | }
845 | tb.AddRows(rows)
846 | err = tb.ToJsonFile("cmd/demo.json", 4)
847 | if err != nil {
848 | fmt.Println("write json file error: ", err.Error())
849 | return
850 | }
851 | }
852 | ```
853 |
854 | cmd/demo.json:
855 | ```json
856 | [
857 | {
858 | "ID": "000",
859 | "Name": "employee-0",
860 | "salary": "60000"
861 | },
862 | {
863 | "ID": "001",
864 | "Name": "employee-1",
865 | "salary": "60000"
866 | },
867 | {
868 | "ID": "002",
869 | "Name": "employee-2",
870 | "salary": "60000"
871 | }
872 | ]
873 | ```
874 |
875 |
876 |
877 | ## Save the table data to a CSV file
878 |
879 | ```go
880 | package main
881 |
882 | import (
883 | "fmt"
884 | "github.com/liushuochen/gotable"
885 | )
886 |
887 | func main() {
888 | tb, err := gotable.Create("Name", "ID", "salary")
889 | if err != nil {
890 | fmt.Println("Create table failed: ", err.Error())
891 | return
892 | }
893 |
894 | rows := make([]map[string]string, 0)
895 | for i := 0; i < 3; i++ {
896 | row := make(map[string]string)
897 | row["Name"] = fmt.Sprintf("employee-%d", i)
898 | row["ID"] = fmt.Sprintf("00%d", i)
899 | row["salary"] = "60000"
900 | rows = append(rows, row)
901 | }
902 | tb.AddRows(rows)
903 | err = tb.ToCSVFile("cmd/demo.csv")
904 | if err != nil {
905 | fmt.Println("write csv file error: ", err.Error())
906 | return
907 | }
908 | }
909 |
910 | ```
911 |
912 |
913 |
914 | ## Close border
915 |
916 | ```go
917 | package main
918 |
919 | import (
920 | "fmt"
921 | "github.com/liushuochen/gotable"
922 | )
923 |
924 | func main() {
925 | table, err := gotable.Create("China", "US", "UK")
926 | if err != nil {
927 | fmt.Println("Create table failed: ", err.Error())
928 | return
929 | }
930 |
931 | table.SetDefault("US", "Los Angeles")
932 |
933 | row := make(map[string]string)
934 | row["China"] = "Beijing"
935 | row["US"] = "Washington D.C."
936 | row["UK"] = "London"
937 | table.AddRow(row)
938 |
939 | row2 := make(map[string]string)
940 | row2["China"] = "Xi'AN"
941 | row2["US"] = "NewYork"
942 | row2["UK"] = "Manchester"
943 | table.AddRow(row2)
944 |
945 | row3 := make(map[string]string)
946 | row3["China"] = "Hangzhou"
947 | row3["US"] = gotable.Default
948 | row3["UK"] = "Manchester"
949 | table.AddRow(row3)
950 |
951 | // close border
952 | table.CloseBorder()
953 | table.Align("China", gotable.Left)
954 | table.Align("US", gotable.Left)
955 | table.Align("UK", gotable.Left)
956 |
957 | fmt.Println(table)
958 | // outputs:
959 | // China US UK
960 | // Beijing Washington D.C. London
961 | // Xi'AN NewYork Manchester
962 | // Hangzhou Los Angeles Manchester
963 | }
964 |
965 | ```
966 |
967 |
968 |
969 | ## Open border
970 |
971 | ```go
972 | package main
973 |
974 | import (
975 | "fmt"
976 | "github.com/liushuochen/gotable"
977 | )
978 |
979 | func main() {
980 | table, err := gotable.Create("China", "US", "UK")
981 | if err != nil {
982 | fmt.Println("Create table failed: ", err.Error())
983 | return
984 | }
985 |
986 | table.SetDefault("US", "Los Angeles")
987 |
988 | row := make(map[string]string)
989 | row["China"] = "Beijing"
990 | row["US"] = "Washington D.C."
991 | row["UK"] = "London"
992 | table.AddRow(row)
993 |
994 | row2 := make(map[string]string)
995 | row2["China"] = "Xi'AN"
996 | row2["US"] = "NewYork"
997 | row2["UK"] = "Manchester"
998 | table.AddRow(row2)
999 |
1000 | row3 := make(map[string]string)
1001 | row3["China"] = "Hangzhou"
1002 | row3["US"] = gotable.Default
1003 | row3["UK"] = "Manchester"
1004 | table.AddRow(row3)
1005 |
1006 | // close border
1007 | table.CloseBorder()
1008 |
1009 | // open border again
1010 | table.OpenBorder()
1011 |
1012 | fmt.Println(table)
1013 | // outputs:
1014 | // +----------+-----------------+------------+
1015 | // | China | US | UK |
1016 | // +----------+-----------------+------------+
1017 | // | Beijing | Washington D.C. | London |
1018 | // | Xi'AN | NewYork | Manchester |
1019 | // | Hangzhou | Los Angeles | Manchester |
1020 | // +----------+-----------------+------------+
1021 | }
1022 |
1023 | ```
1024 |
1025 |
1026 |
1027 | ## Has column
1028 |
1029 | ```go
1030 | package main
1031 |
1032 | import (
1033 | "fmt"
1034 | "github.com/liushuochen/gotable"
1035 | )
1036 |
1037 | type Stu struct {
1038 | Name string `gotable:"name"`
1039 | Sex string `gotable:"sex"`
1040 | Age int `gotable:"age"`
1041 | }
1042 |
1043 | func main() {
1044 | tb, err := gotable.CreateByStruct(&Stu{})
1045 | if err != nil {
1046 | fmt.Println("[ERROR] ", err.Error())
1047 | return
1048 | }
1049 |
1050 |
1051 | if tb.HasColumn("age") {
1052 | fmt.Println("table has column age")
1053 | }
1054 | }
1055 |
1056 | ```
1057 |
1058 |
1059 |
1060 | ## Check whether the columns of the two tables are the same
1061 |
1062 | ```go
1063 | package main
1064 |
1065 | import (
1066 | "fmt"
1067 | "github.com/liushuochen/gotable"
1068 | )
1069 |
1070 | func main() {
1071 | tb1, _ := gotable.Create("id", "name")
1072 | tb2, _ := gotable.Create("name", "id")
1073 | fmt.Println(tb1.EqualColumns(tb2))
1074 | // output: false
1075 | // reason: tb1 and tb2 have different columns order
1076 |
1077 | tb1, _ = gotable.Create("id", "name")
1078 | tb2, _ = gotable.Create("id", "name", "sex")
1079 | fmt.Println(tb1.EqualColumns(tb2))
1080 | // output: false
1081 | // reason: tb1 and tb2 have different columns
1082 |
1083 | tb1, _ = gotable.Create("id", "name")
1084 | tb2, _ = gotable.Create("id", "name")
1085 | tb2.SetDefault("id", "001")
1086 | fmt.Println(tb1.EqualColumns(tb2))
1087 | // output: false
1088 | // reason: tb1 and tb2 have different column default. (tb1: "", ""; tb2: "001", "")
1089 |
1090 | tb1, _ = gotable.Create("id", "name")
1091 | tb2, _ = gotable.Create("id", "name")
1092 | tb2.Align("id", gotable.Left)
1093 | fmt.Println(tb1.EqualColumns(tb2))
1094 | // output: false
1095 | // reason: tb1 and tb2 have different column alignment.
1096 | // (tb1: gotable.Center, gotable.Center; tb2: gotable.Left, gotable.Center)
1097 |
1098 | tb1, _ = gotable.Create("id", "name")
1099 | tb2, _ = gotable.Create("id", "name")
1100 | fmt.Println(tb1.EqualColumns(tb2))
1101 | // output: true
1102 | }
1103 |
1104 | ```
1105 |
1106 |
1107 |
1108 | ## Set columns color
1109 |
1110 | ```go
1111 | package main
1112 |
1113 | import (
1114 | "fmt"
1115 | "github.com/liushuochen/gotable"
1116 | )
1117 |
1118 | func main() {
1119 | table, err := gotable.Create("Name", "ID", "salary")
1120 | if err != nil {
1121 | fmt.Println("Create table failed: ", err.Error())
1122 | return
1123 | }
1124 |
1125 | rows := make([]map[string]string, 0)
1126 | for i := 0; i < 3; i++ {
1127 | row := make(map[string]string)
1128 | row["Name"] = fmt.Sprintf("employee-%d", i)
1129 | row["ID"] = fmt.Sprintf("00%d", i)
1130 | row["salary"] = "60000"
1131 | rows = append(rows, row)
1132 | }
1133 |
1134 | table.AddRows(rows)
1135 |
1136 | // Underline the `salary` column with a white font and no background color
1137 | table.SetColumnColor("salary", gotable.Underline, gotable.Write, gotable.NoneBackground)
1138 | fmt.Println(table)
1139 | }
1140 |
1141 | ```
1142 |
1143 |
1144 |
1145 | ## Get table type
1146 |
1147 | ```go
1148 | package main
1149 |
1150 | import (
1151 | "fmt"
1152 | "github.com/liushuochen/gotable"
1153 | )
1154 |
1155 | func main() {
1156 | table, err := gotable.Create("version", "description")
1157 | if err != nil {
1158 | fmt.Println("Create table failed: ", err.Error())
1159 | return
1160 | }
1161 |
1162 | fmt.Println(table.Type())
1163 | // output: simple
1164 | }
1165 |
1166 | ```
1167 |
1168 |
1169 |
1170 | ## Custom ending string
1171 |
1172 | ```go
1173 | package main
1174 |
1175 | import (
1176 | "fmt"
1177 | "github.com/liushuochen/gotable"
1178 | )
1179 |
1180 | func main() {
1181 | table, err := gotable.Create("version", "description")
1182 | if err != nil {
1183 | fmt.Println("Create safe table failed: ", err.Error())
1184 | return
1185 | }
1186 |
1187 | row := make(map[string]string)
1188 | row["version"] = "v1.0"
1189 | row["description"] = "test"
1190 | table.AddRow(row)
1191 |
1192 | table.End = ""
1193 | fmt.Println(table)
1194 | fmt.Println("I am a new line after printing table.")
1195 | // outputs:
1196 | // +---------+-------------+
1197 | // | version | description |
1198 | // +---------+-------------+
1199 | // | v1.0 | test |
1200 | // +---------+-------------+
1201 | // I am a new line after printing table.
1202 | }
1203 |
1204 | ```
1205 |
1206 |
1207 |
1208 | ## Check the table type is simple table
1209 |
1210 | ```go
1211 | package main
1212 |
1213 | import (
1214 | "fmt"
1215 | "github.com/liushuochen/gotable"
1216 | )
1217 |
1218 | func main() {
1219 | table, err := gotable.Create("China", "US", "UK")
1220 | if err != nil {
1221 | fmt.Println("Create table failed: ", err.Error())
1222 | return
1223 | }
1224 |
1225 | fmt.Println(table.IsSimpleTable())
1226 | // output: true
1227 | }
1228 |
1229 | ```
1230 |
1231 |
--------------------------------------------------------------------------------
/exception/base.go:
--------------------------------------------------------------------------------
1 | package exception
2 |
3 | type baseError struct {
4 | message string
5 | }
6 |
7 | func createBaseError(message string) *baseError {
8 | err := new(baseError)
9 | err.message = message
10 | return err
11 | }
12 |
13 | func (e *baseError) Error() string {
14 | return e.message
15 | }
16 |
17 | func (e *baseError) String() string {
18 | return e.Error()
19 | }
20 |
--------------------------------------------------------------------------------
/exception/columns.go:
--------------------------------------------------------------------------------
1 | package exception
2 |
3 | import "fmt"
4 |
5 | type ColumnsLengthError struct {
6 | *baseError
7 | }
8 |
9 | func ColumnsLength() *ColumnsLengthError {
10 | err := &ColumnsLengthError{createBaseError("columns length must more than zero")}
11 | return err
12 | }
13 |
14 | type ColumnDoNotExistError struct {
15 | *baseError
16 | name string
17 | }
18 |
19 | func (e *ColumnDoNotExistError) Name() string {
20 | return e.name
21 | }
22 |
23 | func ColumnDoNotExist(name string) *ColumnDoNotExistError {
24 | message := fmt.Sprintf("column %s do not exist", name)
25 | err := &ColumnDoNotExistError{createBaseError(message), name}
26 | return err
27 | }
28 |
--------------------------------------------------------------------------------
/exception/file.go:
--------------------------------------------------------------------------------
1 | package exception
2 |
3 | import "fmt"
4 |
5 | type fileError struct {
6 | *baseError
7 | filename string
8 | }
9 |
10 | func createFileError(filename, message string) *fileError {
11 | err := &fileError{baseError: createBaseError(message), filename: filename}
12 | return err
13 | }
14 |
15 | func (err *fileError) Filename() string {
16 | return err.filename
17 | }
18 |
19 | type FileDoNotExistError struct {
20 | *fileError
21 | }
22 |
23 | func FileDoNotExist(path string) *FileDoNotExistError {
24 | message := fmt.Sprintf("file %s do not exist", path)
25 | err := &FileDoNotExistError{fileError: createFileError(path, message)}
26 | return err
27 | }
28 |
29 | type NotARegularCSVFileError struct {
30 | *fileError
31 | }
32 |
33 | func NotARegularCSVFile(path string) *NotARegularCSVFileError {
34 | message := fmt.Sprintf("not a regular csv file: %s", path)
35 | err := &NotARegularCSVFileError{fileError: createFileError(path, message)}
36 | return err
37 | }
38 |
39 | type NotARegularJSONFileError struct {
40 | *fileError
41 | }
42 |
43 | func NotARegularJSONFile(path string) *NotARegularJSONFileError {
44 | message := fmt.Sprintf("not a regular json file: %s", path)
45 | err := &NotARegularJSONFileError{fileError: createFileError(path, message)}
46 | return err
47 | }
48 |
49 | type UnSupportedFileTypeError struct {
50 | *fileError
51 | }
52 |
53 | func UnSupportedFileType(path string) *UnSupportedFileTypeError {
54 | message := fmt.Sprintf("Unsupported file type %s", path)
55 | err := &UnSupportedFileTypeError{
56 | fileError: createFileError(path, message),
57 | }
58 | return err
59 | }
60 |
61 | type NotGotableJSONFormatError struct {
62 | *fileError
63 | }
64 |
65 | func NotGotableJSONFormat(path string) *NotGotableJSONFormatError {
66 | message := fmt.Sprintf("json file %s is not a valid gotable json format", path)
67 | err := &NotGotableJSONFormatError{fileError: createFileError(path, message)}
68 | return err
69 | }
70 |
--------------------------------------------------------------------------------
/exception/row.go:
--------------------------------------------------------------------------------
1 | package exception
2 |
3 | import "fmt"
4 |
5 | type UnsupportedRowTypeError struct {
6 | *baseError
7 | t string
8 | }
9 |
10 | func UnsupportedRowType(t interface{}) *UnsupportedRowTypeError {
11 | rowType := fmt.Sprintf("%T", t)
12 | message := fmt.Sprintf("Unsupported row type: %s", rowType)
13 | err := &UnsupportedRowTypeError{
14 | baseError: createBaseError(message),
15 | t: rowType,
16 | }
17 | return err
18 | }
19 |
20 | func (e *UnsupportedRowTypeError) Type() string {
21 | return e.t
22 | }
23 |
24 | type RowLengthNotEqualColumnsError struct {
25 | *baseError
26 | rowLength int
27 | columnLength int
28 | }
29 |
30 | func RowLengthNotEqualColumns(rowLength, columnLength int) *RowLengthNotEqualColumnsError {
31 | message := fmt.Sprintf("The length of row(%d) does not equal the columns(%d)", rowLength, columnLength)
32 | err := &RowLengthNotEqualColumnsError{
33 | baseError: createBaseError(message),
34 | rowLength: rowLength,
35 | columnLength: columnLength,
36 | }
37 | return err
38 | }
39 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/liushuochen/gotable
2 |
3 | go 1.14
4 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/liushuochen/gotable/1113793e7092f6e0ea6293bd0582f801ec948ca7/go.sum
--------------------------------------------------------------------------------
/gotable.go:
--------------------------------------------------------------------------------
1 | package gotable
2 |
3 | import (
4 | "encoding/csv"
5 | "encoding/json"
6 | "github.com/liushuochen/gotable/exception"
7 | "github.com/liushuochen/gotable/table"
8 | "github.com/liushuochen/gotable/util"
9 | "io/ioutil"
10 | "os"
11 | "reflect"
12 | "strings"
13 | )
14 |
15 | const (
16 | Center = table.C
17 | Left = table.L
18 | Right = table.R
19 | Default = table.Default
20 | )
21 |
22 | // Colored display control
23 | const (
24 | TerminalDefault = 0
25 | Highlight = 1
26 | Underline = 4
27 | Flash = 5
28 | )
29 |
30 | // Colored control
31 | const (
32 | Black = 30
33 | Red = 31
34 | Green = 32
35 | Yellow = 33
36 | Blue = 34
37 | Purple = 35
38 | Cyan = 36
39 | Write = 37
40 | NoneBackground = 0
41 | )
42 |
43 | // Create an empty simple table. When duplicate values in columns, table creation fails.
44 | // It will return a table pointer and an error.
45 | // Error:
46 | // - If the length of columns is not greater than 0, an *exception.ColumnsLengthError error is returned.
47 | // - If columns contain duplicate values, an error is returned.
48 | // - Otherwise, the value of error is nil.
49 | func Create(columns ...string) (*table.Table, error) {
50 | set, err := table.CreateSetFromString(columns...)
51 | if err != nil {
52 | return nil, err
53 | }
54 | tb := table.CreateTable(set)
55 | return tb, nil
56 | }
57 |
58 | // CreateSafeTable function used to create an empty safe table. When duplicate values in columns, table creation fails.
59 | // It will return a table pointer and an error.
60 | // Error:
61 | // - If the length of columns is not greater than 0, an *exception.ColumnsLengthError error is returned.
62 | // - If columns contain duplicate values, an error is returned.
63 | // - Otherwise, the value of error is nil.
64 | func CreateSafeTable(columns ...string) (*table.SafeTable, error) {
65 | set, err := table.CreateSetFromString(columns...)
66 | if err != nil {
67 | return nil, err
68 | }
69 | tb := table.CreateSafeTable(set)
70 | return tb, nil
71 | }
72 |
73 | // CreateByStruct creates an empty table from struct. You can rename a field using struct tag: gotable
74 | // It will return a table pointer and an error.
75 | // Error:
76 | // - If the length of columns is not greater than 0, an *exception.ColumnsLengthError error is returned.
77 | // - If columns contain duplicate values, an error is returned.
78 | // - Otherwise, the value of error is nil.
79 | func CreateByStruct(v interface{}) (*table.Table, error) {
80 | set := &table.Set{}
81 | s := reflect.TypeOf(v).Elem()
82 | numField := s.NumField()
83 | if numField <= 0 {
84 | return nil, exception.ColumnsLength()
85 | }
86 |
87 | for i := 0; i < numField; i++ {
88 | field := s.Field(i)
89 | name := field.Tag.Get("gotable")
90 | if name == "" {
91 | name = field.Name
92 | }
93 |
94 | err := set.Add(name)
95 | if err != nil {
96 | return nil, err
97 | }
98 | }
99 | tb := table.CreateTable(set)
100 | return tb, nil
101 | }
102 |
103 | // Version
104 | // The version function returns a string representing the version information of the gotable.
105 | // e.g.
106 | //
107 | // gotable 3.4.0
108 | func Version() string {
109 | return "gotable " + strings.Join(getVersions(), ".")
110 | }
111 |
112 | // Versions returns a list of version numbers.
113 | func Versions() []string { return getVersions() }
114 |
115 | // getVersions 5.18.0
116 | func getVersions() []string {
117 | return []string{"5", "18", "0"}
118 | }
119 |
120 | // Read from a csv file to create a *table instance.
121 | // This function is a private function that only called from Read function. It will return a table pointer and an error.
122 | // Error:
123 | // - If the contents of the csv file are empty, an *exception.ColumnsLengthError is returned.
124 | // - If there are duplicate columns in the parse result, an error is returned.
125 | // - Otherwise the value if error is nil.
126 | func readFromCSVFile(file *os.File) (*table.Table, error) {
127 | reader := csv.NewReader(file)
128 | lines, err := reader.ReadAll()
129 | if err != nil {
130 | return nil, err
131 | }
132 | if len(lines) < 1 {
133 | return Create()
134 | }
135 |
136 | tb, err := Create(lines[0]...)
137 | if err != nil {
138 | return nil, err
139 | }
140 |
141 | rows := make([]map[string]string, 0)
142 | for _, line := range lines[1:] {
143 | row := make(map[string]string)
144 | for i := range line {
145 | row[lines[0][i]] = line[i]
146 | }
147 | rows = append(rows, row)
148 | }
149 | tb.AddRows(rows)
150 | return tb, nil
151 | }
152 |
153 | // Read from a json file to create a *table instance.
154 | // This function is a private function that only called from Read function. It will return a table pointer and an error.
155 | // Error:
156 | // - If the contents of the json file are not eligible table contents, an *exception.NotGotableJSONFormatError is
157 | // returned.
158 | // - If there are duplicate columns in the parse result, an error is returned.
159 | // - Otherwise the value if error is nil.
160 | func readFromJSONFile(file *os.File) (*table.Table, error) {
161 | byteValue, err := ioutil.ReadAll(file)
162 | if err != nil {
163 | return nil, err
164 | }
165 |
166 | rows := make([]map[string]string, 0)
167 | err = json.Unmarshal(byteValue, &rows)
168 | if err != nil {
169 | return nil, exception.NotGotableJSONFormat(file.Name())
170 | }
171 |
172 | columns := make([]string, 0)
173 | for column := range rows[0] {
174 | columns = append(columns, column)
175 | }
176 | tb, err := Create(columns...)
177 | if err != nil {
178 | return nil, err
179 | }
180 | tb.AddRows(rows)
181 | return tb, nil
182 | }
183 |
184 | // Read from file to create a *table instance.
185 | // Currently, support csv and json file. It will return a table pointer and an error.
186 | // Error:
187 | // - If path is not a file, or does not exist, an *exception.FileDoNotExistError is returned.
188 | // - If path is a JSON file, the contents of the file are not eligible table contents, an
189 | // *exception.NotGotableJSONFormatError is returned.
190 | // - If path is a CSV file, and the contents of the file are empty, an *exception.ColumnsLengthError is returned.
191 | // - If there are duplicate columns in the parse result, an error is returned.
192 | // - Otherwise the value if error is nil.
193 | func Read(path string) (*table.Table, error) {
194 | if !util.IsFile(path) {
195 | return nil, exception.FileDoNotExist(path)
196 | }
197 |
198 | file, err := os.Open(path)
199 | if err != nil {
200 | return nil, err
201 | }
202 | defer func(file *os.File) {
203 | _ = file.Close()
204 | }(file)
205 |
206 | if util.IsJsonFile(file.Name()) {
207 | return readFromJSONFile(file)
208 | }
209 |
210 | if util.IsCSVFile(file.Name()) {
211 | return readFromCSVFile(file)
212 | }
213 |
214 | return nil, exception.UnSupportedFileType(file.Name())
215 | }
216 |
--------------------------------------------------------------------------------
/gotable_test.go:
--------------------------------------------------------------------------------
1 | // Package gotable_test used to test package gotable
2 | package gotable_test
3 |
4 | import (
5 | "github.com/liushuochen/gotable/exception"
6 | "strings"
7 | "testing"
8 |
9 | "github.com/liushuochen/gotable"
10 | )
11 |
12 | // Check version string whether start with "gotable".
13 | func TestVersionPrefix(t *testing.T) {
14 | version := gotable.Version()
15 | if !strings.HasPrefix(version, "gotable") {
16 | t.Errorf("expected version start switch gotable, but %s got", version)
17 | }
18 | }
19 |
20 | // Test the value of gotable.TerminalDefault, gotable.Highlight, gotable.Underline and gotable.Flash.
21 | func TestValueOfColorDisplay(t *testing.T) {
22 | if gotable.TerminalDefault != 0 {
23 | t.Errorf("expected gotable.TerminalDefault is 0, but %d got", gotable.TerminalDefault)
24 | }
25 |
26 | if gotable.Highlight != 1 {
27 | t.Errorf("expected gotable.Highlight is 1, but %d got", gotable.Highlight)
28 | }
29 |
30 | if gotable.Underline != 4 {
31 | t.Errorf("expected gotable.Underline is 4, but %d got", gotable.Underline)
32 | }
33 |
34 | if gotable.Flash != 5 {
35 | t.Errorf("expected gotable.Flash is 5, but %d got", gotable.Flash)
36 | }
37 | }
38 |
39 | // The value of color control
40 | func TestValueOfColorControllers(t *testing.T) {
41 | if gotable.Black != 30 {
42 | t.Errorf("expected gotable.Black is 30, but %d got", gotable.Black)
43 | }
44 |
45 | if gotable.Red != 31 {
46 | t.Errorf("expected gotable.Red is 31, but %d got", gotable.Red)
47 | }
48 |
49 | if gotable.Green != 32 {
50 | t.Errorf("expected gotable.Green is 32, but %d got", gotable.Green)
51 | }
52 |
53 | if gotable.Yellow != 33 {
54 | t.Errorf("expected gotable.Yellow is 33, but %d got", gotable.Yellow)
55 | }
56 |
57 | if gotable.Blue != 34 {
58 | t.Errorf("expected gotable.Blue is 34, but %d got", gotable.Blue)
59 | }
60 |
61 | if gotable.Purple != 35 {
62 | t.Errorf("expected gotable.Purple is 35, but %d got", gotable.Purple)
63 | }
64 |
65 | if gotable.Cyan != 36 {
66 | t.Errorf("expected gotable.Cyan is 36, but %d got", gotable.Cyan)
67 | }
68 |
69 | if gotable.Write != 37 {
70 | t.Errorf("expected gotable.Write is 37, but %d got", gotable.Write)
71 | }
72 |
73 | if gotable.NoneBackground != 0 {
74 | t.Errorf("expected gotable.NoneBackground is 0, but %d got", gotable.NoneBackground)
75 | }
76 | }
77 |
78 | // Create a simple table.
79 | func TestCreateSimpleTable(t *testing.T) {
80 | columns := []string{"country", "capital"}
81 | _, err := gotable.Create(columns...)
82 | if err != nil {
83 | t.Errorf("expected err is nil, but %s got", err.Error())
84 | return
85 | }
86 | }
87 |
88 | // Create a simple table with duplicate columns.
89 | func TestCreateSimpleTableWithDuplicateColumn(t *testing.T) {
90 | columns := []string{"name", "name"}
91 | _, err := gotable.Create(columns...)
92 | if err == nil {
93 | t.Errorf("expected err is not nil, but nil got")
94 | }
95 | }
96 |
97 | // Create a simple table without column.
98 | func TestCreateSimpleTableWithoutColumn(t *testing.T) {
99 | _, err := gotable.Create()
100 | switch err.(type) {
101 | case *exception.ColumnsLengthError:
102 | default:
103 | t.Errorf("expected err is ColumnsLengthError, but %T got", err)
104 | }
105 | }
106 |
107 | // Create a safe table.
108 | func TestCreateSafeTable(t *testing.T) {
109 | columns := []string{"country", "capital"}
110 | _, err := gotable.CreateSafeTable(columns...)
111 | if err != nil {
112 | t.Errorf("expected err is nil, but %s got", err.Error())
113 | return
114 | }
115 | }
116 |
117 | // Create a safe table with duplicate columns.
118 | func TestCreateSafeTableWithDuplicateColumn(t *testing.T) {
119 | columns := []string{"name", "name"}
120 | _, err := gotable.CreateSafeTable(columns...)
121 | if err == nil {
122 | t.Errorf("expected err is not nil, but nil got")
123 | }
124 | }
125 |
126 | // Create a safe table without column.
127 | func TestCreateSafeTableWithoutColumn(t *testing.T) {
128 | _, err := gotable.CreateSafeTable()
129 | switch err.(type) {
130 | case *exception.ColumnsLengthError:
131 | default:
132 | t.Errorf("expected err is ColumnsLengthError, but %T got", err)
133 | }
134 | }
135 |
136 | // Create table using struct.
137 | func TestCreateTableByStruct(t *testing.T) {
138 | type Student struct {
139 | Name string `gotable:"name"`
140 | Age string `gotable:"age"`
141 | }
142 |
143 | stu := &Student{
144 | Name: "Bob",
145 | Age: "12",
146 | }
147 |
148 | _, err := gotable.CreateByStruct(stu)
149 | if err != nil {
150 | t.Errorf("expected err is nil, but %s got.", err.Error())
151 | }
152 | }
153 |
154 | // Create table using empty struct.
155 | func TestCreateTableByStruct2(t *testing.T) {
156 | type Student struct{}
157 |
158 | stu := new(Student)
159 | _, err := gotable.CreateByStruct(stu)
160 | switch err.(type) {
161 | case *exception.ColumnsLengthError:
162 | default:
163 | t.Errorf("expected err is ColumnsLengthError, but %T got", err)
164 | }
165 | }
166 |
167 | // Create table using struct which contains duplicate tags.
168 | func TestCreateTableByStruct3(t *testing.T) {
169 | type Student struct {
170 | Name string `gotable:"name"`
171 | Age string `gotable:"name"`
172 | }
173 |
174 | stu := &Student{
175 | Name: "Bob",
176 | Age: "12",
177 | }
178 |
179 | _, err := gotable.CreateByStruct(stu)
180 | if err == nil {
181 | t.Error("expected got an error, but nil got.")
182 | }
183 | }
184 |
185 | // Check the returned value for Versions function whether empty.
186 | func TestVersions(t *testing.T) {
187 | result := gotable.Versions()
188 | if len(result) == 0 {
189 | t.Error("Versions return an empty slice.")
190 | }
191 | }
192 |
193 | // Check create table from a CSV file.
194 | // - Check table length (expected is 3, defined in test_csv.csv).
195 | func TestReadCSVFile(t *testing.T) {
196 | table, err := gotable.Read("test_csv.csv")
197 | if err != nil {
198 | t.Errorf("expected err is nil, but %s got.", err.Error())
199 | }
200 |
201 | if table.Length() != 3 {
202 | t.Errorf("expected table length is 3, but %d got.", table.Length())
203 | }
204 | }
205 |
--------------------------------------------------------------------------------
/table/base.go:
--------------------------------------------------------------------------------
1 | // Package table define all table types methods.
2 | // base.go contains basic methods of table types.
3 | package table
4 |
5 | import (
6 | "fmt"
7 | "strings"
8 | )
9 |
10 | // base struct contains common attributes to the table.
11 | // Columns: Table columns
12 | // border: Control the table border display(true: print table border).
13 | // tableType: Use to record table types
14 | // End: Used to set the ending. The default is newline "\n".
15 | type base struct {
16 | Columns *Set
17 | border bool
18 | tableType string
19 | End string
20 | }
21 |
22 | func createTableBase(columns *Set, tableType string, border bool) *base {
23 | b := new(base)
24 | b.Columns = columns
25 | b.tableType = tableType
26 | b.border = border
27 | b.End = "\n"
28 | return b
29 | }
30 |
31 | // Type method returns a table type string.
32 | func (b *base) Type() string {
33 | return b.tableType
34 | }
35 |
36 | // SetDefault method used to set default value for a given column name.
37 | func (b *base) SetDefault(column string, defaultValue string) {
38 | for _, head := range b.Columns.base {
39 | if head.Original() == column {
40 | head.SetDefault(defaultValue)
41 | break
42 | }
43 | }
44 | }
45 |
46 | // IsSimpleTable method returns a bool value indicate the table type is simpleTableType.
47 | func (b *base) IsSimpleTable() bool {
48 | return b.tableType == simpleTableType
49 | }
50 |
51 | // IsSafeTable method returns a bool value indicate the table type is safeTableType.
52 | func (b *base) IsSafeTable() bool {
53 | return b.tableType == safeTableType
54 | }
55 |
56 | // GetDefault method returns default value with a designated column name.
57 | func (b *base) GetDefault(column string) string {
58 | for _, col := range b.Columns.base {
59 | if col.Original() == column {
60 | return col.Default()
61 | }
62 | }
63 | return ""
64 | }
65 |
66 | // DropDefault method used to delete default value for designated column.
67 | func (b *base) DropDefault(column string) {
68 | b.SetDefault(column, "")
69 | }
70 |
71 | // GetDefaults method return a map that contains all default value of each column.
72 | // * map[column name] = default value
73 | func (b *base) GetDefaults() map[string]string {
74 | defaults := make(map[string]string)
75 | for _, column := range b.Columns.base {
76 | defaults[column.Original()] = column.Default()
77 | }
78 | return defaults
79 | }
80 |
81 | func (b *base) end(content string) string {
82 | content = content[:len(content)-1]
83 | content += b.End
84 | return content
85 | }
86 |
87 | // GetColumns method return a list of string that contains all column names.
88 | func (b *base) GetColumns() []string {
89 | columns := make([]string, 0)
90 | for _, col := range b.Columns.base {
91 | columns = append(columns, col.Original())
92 | }
93 | return columns
94 | }
95 |
96 | func (b *base) header() []string {
97 | resultList := make([]string, 0)
98 | resultList = append(resultList, fmt.Sprintf("TableType:%s", b.tableType))
99 | resultList = append(resultList, fmt.Sprintf("Border:%v", b.border))
100 |
101 | columns := make([]string, 0)
102 | for _, column := range b.Columns.base {
103 | columns = append(columns, column.Original())
104 | }
105 | resultList = append(resultList, fmt.Sprintf("Column:[%s]", strings.Join(columns, ",")))
106 | return resultList
107 | }
108 |
--------------------------------------------------------------------------------
/table/kind.go:
--------------------------------------------------------------------------------
1 | // Package table define all table types methods.
2 | // kind.go used to define table types.
3 | package table
4 |
5 | const (
6 | simpleTableType = "simple"
7 | safeTableType = "safe"
8 | )
9 |
--------------------------------------------------------------------------------
/table/print.go:
--------------------------------------------------------------------------------
1 | // Package table define all table types methods.
2 | // print.go used to control table printing.
3 | package table
4 |
5 | import (
6 | "fmt"
7 | "github.com/liushuochen/gotable/cell"
8 | "sync"
9 | )
10 |
11 | // This method print part of table data in STDOUT. It will be called twice in *table.PrintTable method.
12 | // Arguments:
13 | // group: A map that storage column as key, data as value. Data is either "-" or row, if the value of data is
14 | // "-", the printGroup method will print the border of the table.
15 | // columnMaxLen: A map that storage column as key, max length of cell of column as value.
16 | func (tb *Table) printGroup(group []map[string]cell.Cell, columnMaxLen map[string]int) string {
17 | result := ""
18 | for _, item := range group {
19 | for index, column := range tb.Columns.base {
20 | itemLen := columnMaxLen[column.Original()]
21 | if tb.border {
22 | itemLen += 2
23 | }
24 | s := ""
25 | icon := "|"
26 | if item[column.String()].String() == "-" {
27 | if tb.border {
28 | s, _ = center(item[column.String()], itemLen, "-")
29 | icon = "+"
30 | } else {
31 | icon = " "
32 | }
33 | } else {
34 | switch column.Align() {
35 | case R:
36 | s, _ = right(item[column.String()], itemLen, " ")
37 | case L:
38 | s, _ = left(item[column.String()], itemLen, " ")
39 | default:
40 | s, _ = center(item[column.String()], itemLen, " ")
41 | }
42 | }
43 |
44 | if index == 0 {
45 | s = icon + s + icon
46 | } else {
47 | s = "" + s + icon
48 | }
49 | result += s
50 | }
51 | result += "\n"
52 | }
53 | return result
54 | }
55 |
56 | func (st *SafeTable) printGroup(group []*sync.Map, columnMaxLen *sync.Map) string {
57 | result := ""
58 | for _, item := range group {
59 | for index, column := range st.Columns.base {
60 | value, _ := columnMaxLen.Load(column.Original())
61 | itemLen := value.(int)
62 | if st.border {
63 | itemLen += 2
64 | }
65 | s := ""
66 | icon := "|"
67 | value, _ = item.Load(column.String())
68 | if value.(cell.Cell).String() == "-" {
69 | if st.border {
70 | s, _ = center(value.(cell.Cell), itemLen, "-")
71 | icon = "+"
72 | } else {
73 | icon = " "
74 | }
75 | } else {
76 | switch column.Align() {
77 | case R:
78 | s, _ = right(value.(cell.Cell), itemLen, " ")
79 | case L:
80 | s, _ = left(value.(cell.Cell), itemLen, " ")
81 | default:
82 | s, _ = center(value.(cell.Cell), itemLen, " ")
83 | }
84 | }
85 |
86 | if index == 0 {
87 | s = icon + s + icon
88 | } else {
89 | s = "" + s + icon
90 | }
91 | result += s
92 | }
93 | result += "\n"
94 | }
95 | return result
96 | }
97 |
98 | func max(x, y int) int {
99 | if x >= y {
100 | return x
101 | }
102 | return y
103 | }
104 |
105 | func center(c cell.Cell, length int, fillchar string) (string, error) {
106 | if len(fillchar) != 1 {
107 | err := fmt.Errorf("the fill character must be exactly one" +
108 | " character long")
109 | return "", err
110 | }
111 |
112 | if c.Length() >= length {
113 | return c.String(), nil
114 | }
115 |
116 | result := ""
117 | if isEvenNumber(length - c.Length()) {
118 | front := ""
119 | for i := 0; i < ((length - c.Length()) / 2); i++ {
120 | front = front + fillchar
121 | }
122 |
123 | result = front + c.String() + front
124 | } else {
125 | front := ""
126 | for i := 0; i < ((length - c.Length() - 1) / 2); i++ {
127 | front = front + fillchar
128 | }
129 |
130 | behind := front + fillchar
131 | result = front + c.String() + behind
132 | }
133 | return result, nil
134 | }
135 |
136 | func left(c cell.Cell, length int, fillchar string) (string, error) {
137 | if len(fillchar) != 1 {
138 | err := fmt.Errorf("the fill character must be exactly one" +
139 | " character long")
140 | return "", err
141 | }
142 |
143 | result := c.String() + block(length-c.Length())
144 | return result, nil
145 | }
146 |
147 | func right(c cell.Cell, length int, fillchar string) (string, error) {
148 | if len(fillchar) != 1 {
149 | err := fmt.Errorf("the fill character must be exactly one" +
150 | " character long")
151 | return "", err
152 | }
153 |
154 | result := block(length-c.Length()) + c.String()
155 | return result, nil
156 | }
157 |
158 | func block(length int) string {
159 | result := ""
160 | for i := 0; i < length; i++ {
161 | result += " "
162 | }
163 | return result
164 | }
165 |
166 | func isEvenNumber(number int) bool {
167 | if number%2 == 0 {
168 | return true
169 | }
170 | return false
171 | }
172 |
173 | func toRow(value map[string]string) map[string]cell.Cell {
174 | row := make(map[string]cell.Cell)
175 | for k, v := range value {
176 | row[k] = cell.CreateData(v)
177 | }
178 | return row
179 | }
180 |
181 | func toSafeRow(value map[string]string) sync.Map {
182 | var row sync.Map
183 | for k, v := range value {
184 | row.Store(k, cell.CreateData(v))
185 | }
186 | return row
187 | }
188 |
--------------------------------------------------------------------------------
/table/safe.go:
--------------------------------------------------------------------------------
1 | package table
2 |
3 | import (
4 | "github.com/liushuochen/gotable/cell"
5 | "github.com/liushuochen/gotable/exception"
6 | "sync"
7 | )
8 |
9 | type SafeTable struct {
10 | *base
11 | Row []sync.Map
12 | }
13 |
14 | // CreateSafeTable returns a pointer of SafeTable.
15 | func CreateSafeTable(set *Set) *SafeTable {
16 | return &SafeTable{
17 | base: createTableBase(set, safeTableType, true),
18 | Row: make([]sync.Map, 0),
19 | }
20 | }
21 |
22 | // Clear the table. The table is cleared of all data.
23 | func (st *SafeTable) Clear() {
24 | st.Columns.Clear()
25 | st.Row = make([]sync.Map, 0)
26 | }
27 |
28 | // AddColumn method used to add a new column for table. It returns an error when column has been existed.
29 | func (st *SafeTable) AddColumn(column string) error {
30 | err := st.Columns.Add(column)
31 | if err != nil {
32 | return err
33 | }
34 |
35 | for index := range st.Row {
36 | st.Row[index].Store(column, cell.CreateEmptyData())
37 | }
38 | return nil
39 | }
40 |
41 | // AddRow method support Map and Slice argument.
42 | // For Map argument, you must put the data from each row into a Map and use column-data as key-value pairs. If the Map
43 | // does not contain a column, the table sets it to the default value. If the Map contains a column that does not
44 | // exist, the AddRow method returns an error.
45 | // For Slice argument, you must ensure that the slice length is equal to the column length. Method will automatically
46 | // map values in Slice and columns. The default value cannot be omitted and must use gotable.Default constant.
47 | // Return error types:
48 | // - *exception.UnsupportedRowTypeError: It returned when the type of the argument is not supported.
49 | // - *exception.RowLengthNotEqualColumnsError: It returned if the argument is type of the Slice but the length is
50 | // different from the length of column.
51 | // - *exception.ColumnDoNotExistError: It returned if the argument is type of the Map but contains a nonexistent
52 | // column as a key.
53 | func (st *SafeTable) AddRow(row interface{}) error {
54 | switch v := row.(type) {
55 | case []string:
56 | return st.addRowFromSlice(v)
57 | case map[string]string:
58 | return st.addRowFromMap(v)
59 | default:
60 | return exception.UnsupportedRowType(v)
61 | }
62 | }
63 |
64 | // AddRows used to add a slice of rows map. It returns a slice of map which add failed.
65 | func (st *SafeTable) AddRows(rows []map[string]string) []map[string]string {
66 | failure := make([]map[string]string, 0)
67 | for _, row := range rows {
68 | err := st.AddRow(row)
69 | if err != nil {
70 | failure = append(failure, row)
71 | }
72 | }
73 | return failure
74 | }
75 |
76 | func (st *SafeTable) addRowFromMap(row map[string]string) error {
77 | for key := range row {
78 | if !st.Columns.Exist(key) {
79 | return exception.ColumnDoNotExist(key)
80 | }
81 |
82 | // add row by const `DEFAULT`
83 | if row[key] == Default {
84 | row[key] = st.Columns.Get(key).Default()
85 | }
86 | }
87 |
88 | // Add default value
89 | for _, col := range st.Columns.base {
90 | _, ok := row[col.Original()]
91 | if !ok {
92 | row[col.Original()] = col.Default()
93 | }
94 | }
95 |
96 | st.Row = append(st.Row, toSafeRow(row))
97 | return nil
98 | }
99 |
100 | func (st *SafeTable) addRowFromSlice(row []string) error {
101 | rowLength := len(row)
102 | if rowLength != st.Columns.Len() {
103 | return exception.RowLengthNotEqualColumns(rowLength, st.Columns.Len())
104 | }
105 |
106 | rowMap := make(map[string]string, 0)
107 | for i := 0; i < rowLength; i++ {
108 | if row[i] == Default {
109 | rowMap[st.Columns.base[i].Original()] = st.Columns.base[i].Default()
110 | } else {
111 | rowMap[st.Columns.base[i].Original()] = row[i]
112 | }
113 | }
114 |
115 | st.Row = append(st.Row, toSafeRow(rowMap))
116 | return nil
117 | }
118 |
119 | // Length method returns an integer indicates the length of the table row.
120 | func (st *SafeTable) Length() int {
121 | return len(st.Row)
122 | }
123 |
124 | // Empty method is used to determine whether the table is empty.
125 | func (st *SafeTable) Empty() bool {
126 | return st.Length() == 0
127 | }
128 |
129 | // String method used to implement fmt.Stringer.
130 | func (st *SafeTable) String() string {
131 | var (
132 | // Variable columnMaxLength original mode is map[string]int
133 | columnMaxLength sync.Map
134 |
135 | // Variable tag original mode is map[string]cell.Cell
136 | tag sync.Map
137 |
138 | // Variable taga original mode is []map[string]cell.Cell
139 | taga = make([]*sync.Map, 0)
140 | )
141 |
142 | for _, col := range st.Columns.base {
143 | columnMaxLength.Store(col.Original(), col.Length())
144 | tag.Store(col.String(), cell.CreateData("-"))
145 | }
146 |
147 | for index := range st.Row {
148 | for _, col := range st.Columns.base {
149 | value, _ := st.Row[index].Load(col.Original())
150 | maxLength := max(col.Length(), value.(cell.Cell).Length())
151 |
152 | value, _ = columnMaxLength.Load(col.Original())
153 | maxLength = max(maxLength, value.(int))
154 | columnMaxLength.Store(col.Original(), maxLength)
155 | }
156 | }
157 |
158 | content := ""
159 | icon := " "
160 | // Print first line.
161 | taga = append(taga, &tag)
162 | if st.border {
163 | content += st.printGroup(taga, &columnMaxLength)
164 | icon = "|"
165 | }
166 |
167 | // Print table column.
168 | for index, column := range st.Columns.base {
169 | value, _ := columnMaxLength.Load(column.Original())
170 | itemLen := value.(int)
171 | if st.border {
172 | itemLen += 2
173 | }
174 | s := ""
175 | switch column.Align() {
176 | case R:
177 | s, _ = right(column, itemLen, " ")
178 | case L:
179 | s, _ = left(column, itemLen, " ")
180 | default:
181 | s, _ = center(column, itemLen, " ")
182 | }
183 | if index == 0 {
184 | s = icon + s + icon
185 | } else {
186 | s = "" + s + icon
187 | }
188 |
189 | content += s
190 | }
191 |
192 | if st.border {
193 | content += "\n"
194 | }
195 |
196 | tableValue := taga
197 | if !st.Empty() {
198 | for index := range st.Row {
199 | // map[string]cell.Cell
200 | var cellMap sync.Map
201 | f := func(key, value interface{}) bool {
202 | column := st.Columns.Get(key.(string))
203 | cellMap.Store(column.String(), value.(cell.Cell))
204 | return true
205 | }
206 | st.Row[index].Range(f)
207 | tableValue = append(tableValue, &cellMap)
208 | }
209 | tableValue = append(tableValue, &tag)
210 | }
211 |
212 | content += st.printGroup(tableValue, &columnMaxLength)
213 | return st.end(content)
214 | }
215 |
--------------------------------------------------------------------------------
/table/set.go:
--------------------------------------------------------------------------------
1 | package table
2 |
3 | import (
4 | "fmt"
5 | "github.com/liushuochen/gotable/cell"
6 | "github.com/liushuochen/gotable/exception"
7 | )
8 |
9 | type Set struct {
10 | base []*cell.Column
11 | }
12 |
13 | func CreateSetFromString(columns ...string) (*Set, error) {
14 | if len(columns) <= 0 {
15 | return nil, exception.ColumnsLength()
16 | }
17 |
18 | set := &Set{base: make([]*cell.Column, 0)}
19 | for _, column := range columns {
20 | err := set.Add(column)
21 | if err != nil {
22 | return nil, err
23 | }
24 | }
25 |
26 | return set, nil
27 | }
28 |
29 | func (set *Set) Len() int {
30 | return len(set.base)
31 | }
32 |
33 | func (set *Set) Cap() int {
34 | return cap(set.base)
35 | }
36 |
37 | func (set *Set) Exist(element string) bool {
38 | return set.exist(element) != -1
39 | }
40 |
41 | func (set *Set) exist(element string) int {
42 | for index, data := range set.base {
43 | if data.Original() == element {
44 | return index
45 | }
46 | }
47 |
48 | return -1
49 | }
50 |
51 | func (set *Set) Clear() {
52 | set.base = make([]*cell.Column, 0)
53 | }
54 |
55 | func (set *Set) Add(element string) error {
56 | if set.Exist(element) {
57 | return fmt.Errorf("value %s has exit", element)
58 | }
59 |
60 | newHeader := cell.CreateColumn(element)
61 | set.base = append(set.base, newHeader)
62 | return nil
63 | }
64 |
65 | func (set *Set) Remove(element string) error {
66 | position := set.exist(element)
67 | if position == -1 {
68 | return fmt.Errorf("value %s has not exit", element)
69 | }
70 |
71 | set.base = append(set.base[:position], set.base[position+1:]...)
72 | return nil
73 | }
74 |
75 | func (set *Set) Get(name string) *cell.Column {
76 | for _, h := range set.base {
77 | if h.Original() == name {
78 | return h
79 | }
80 | }
81 | return nil
82 | }
83 |
84 | func (set *Set) Equal(other *Set) bool {
85 | if set.Len() != other.Len() {
86 | return false
87 | }
88 |
89 | c := make(chan bool)
90 | for index := range set.base {
91 | i := index
92 | go func(pos int) {
93 | if !set.base[pos].Equal(other.base[pos]) {
94 | c <- false
95 | } else {
96 | c <- true
97 | }
98 | }(i)
99 | }
100 |
101 | count := 0
102 | for {
103 | select {
104 | case equal := <-c:
105 | count += 1
106 | if !equal {
107 | return false
108 | }
109 |
110 | if count >= set.Len() {
111 | return true
112 | }
113 | }
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/table/table.go:
--------------------------------------------------------------------------------
1 | package table
2 |
3 | import (
4 | "encoding/csv"
5 | "encoding/json"
6 | "fmt"
7 | "github.com/liushuochen/gotable/cell"
8 | "github.com/liushuochen/gotable/exception"
9 | "github.com/liushuochen/gotable/util"
10 | "os"
11 | "strings"
12 | )
13 |
14 | const (
15 | C = cell.AlignCenter
16 | L = cell.AlignLeft
17 | R = cell.AlignRight
18 | Default = "__DEFAULT__"
19 | )
20 |
21 | // Table struct:
22 | // - Columns: Save the table columns.
23 | // - Row: Save the list of column and value mapping.
24 | // - border: A flag indicates whether to print table border. If the value is `true`, table will show its border.
25 | // Default is true(By CreateTable function).
26 | // - tableType: Type of table.
27 | type Table struct {
28 | *base
29 | Row []map[string]cell.Cell
30 | }
31 |
32 | // CreateTable function returns a pointer of Table.
33 | func CreateTable(set *Set) *Table {
34 | return &Table{
35 | base: createTableBase(set, simpleTableType, true),
36 | Row: make([]map[string]cell.Cell, 0),
37 | }
38 | }
39 |
40 | // Clear the table. The table is cleared of all data.
41 | func (tb *Table) Clear() {
42 | tb.Columns.Clear()
43 | tb.Row = make([]map[string]cell.Cell, 0)
44 | }
45 |
46 | // AddColumn method used to add a new column for table. It returns an error when column has been existed.
47 | func (tb *Table) AddColumn(column string) error {
48 | err := tb.Columns.Add(column)
49 | if err != nil {
50 | return err
51 | }
52 |
53 | // Modify exist value, add new column.
54 | for _, row := range tb.Row {
55 | row[column] = cell.CreateEmptyData()
56 | }
57 | return nil
58 | }
59 |
60 | // AddRow method support Map and Slice argument.
61 | // For Map argument, you must put the data from each row into a Map and use column-data as key-value pairs. If the Map
62 | // does not contain a column, the table sets it to the default value. If the Map contains a column that does not
63 | // exist, the AddRow method returns an error.
64 | // For Slice argument, you must ensure that the slice length is equal to the column length. Method will automatically
65 | // map values in Slice and columns. The default value cannot be omitted and must use gotable.Default constant.
66 | // Return error types:
67 | // - *exception.UnsupportedRowTypeError: It returned when the type of the argument is not supported.
68 | // - *exception.RowLengthNotEqualColumnsError: It returned if the argument is type of the Slice but the length is
69 | // different from the length of column.
70 | // - *exception.ColumnDoNotExistError: It returned if the argument is type of the Map but contains a nonexistent
71 | // column as a key.
72 | func (tb *Table) AddRow(row interface{}) error {
73 | switch v := row.(type) {
74 | case []string:
75 | return tb.addRowFromSlice(v)
76 | case map[string]string:
77 | return tb.addRowFromMap(v)
78 | default:
79 | return exception.UnsupportedRowType(v)
80 | }
81 | }
82 |
83 | func (tb *Table) addRowFromSlice(row []string) error {
84 | rowLength := len(row)
85 | if rowLength != tb.Columns.Len() {
86 | return exception.RowLengthNotEqualColumns(rowLength, tb.Columns.Len())
87 | }
88 |
89 | rowMap := make(map[string]string, 0)
90 | for i := 0; i < rowLength; i++ {
91 | if row[i] == Default {
92 | rowMap[tb.Columns.base[i].Original()] = tb.Columns.base[i].Default()
93 | } else {
94 | rowMap[tb.Columns.base[i].Original()] = row[i]
95 | }
96 | }
97 |
98 | tb.Row = append(tb.Row, toRow(rowMap))
99 | return nil
100 | }
101 |
102 | func (tb *Table) addRowFromMap(row map[string]string) error {
103 | for key := range row {
104 | if !tb.Columns.Exist(key) {
105 | return exception.ColumnDoNotExist(key)
106 | }
107 |
108 | // add row by const `DEFAULT`
109 | if row[key] == Default {
110 | row[key] = tb.Columns.Get(key).Default()
111 | }
112 | }
113 |
114 | // Add default value
115 | for _, col := range tb.Columns.base {
116 | _, ok := row[col.Original()]
117 | if !ok {
118 | row[col.Original()] = col.Default()
119 | }
120 | }
121 |
122 | tb.Row = append(tb.Row, toRow(row))
123 | return nil
124 | }
125 |
126 | // AddRows used to add a slice of rows maps. It returns a slice of map which add failed.
127 | func (tb *Table) AddRows(rows []map[string]string) []map[string]string {
128 | failure := make([]map[string]string, 0)
129 | for _, row := range rows {
130 | err := tb.AddRow(row)
131 | if err != nil {
132 | failure = append(failure, row)
133 | }
134 | }
135 | return failure
136 | }
137 |
138 | // String method used to implement fmt.Stringer.
139 | func (tb *Table) String() string {
140 | columnMaxLength := make(map[string]int)
141 | tag := make(map[string]cell.Cell)
142 | taga := make([]map[string]cell.Cell, 0)
143 | for _, h := range tb.Columns.base {
144 | columnMaxLength[h.Original()] = h.Length()
145 | tag[h.String()] = cell.CreateData("-")
146 | }
147 |
148 | for _, data := range tb.Row {
149 | for _, h := range tb.Columns.base {
150 | maxLength := max(h.Length(), data[h.Original()].Length())
151 | maxLength = max(maxLength, columnMaxLength[h.Original()])
152 | columnMaxLength[h.Original()] = maxLength
153 | }
154 | }
155 |
156 | content := ""
157 | icon := " "
158 | // Print first line.
159 | taga = append(taga, tag)
160 | if tb.border {
161 | content += tb.printGroup(taga, columnMaxLength)
162 | icon = "|"
163 | }
164 |
165 | // Print table column.
166 | for index, column := range tb.Columns.base {
167 | itemLen := columnMaxLength[column.Original()]
168 | if tb.border {
169 | itemLen += 2
170 | }
171 | s := ""
172 | switch column.Align() {
173 | case R:
174 | s, _ = right(column, itemLen, " ")
175 | case L:
176 | s, _ = left(column, itemLen, " ")
177 | default:
178 | s, _ = center(column, itemLen, " ")
179 | }
180 | if index == 0 {
181 | s = icon + s + icon
182 | } else {
183 | s = "" + s + icon
184 | }
185 |
186 | content += s
187 | }
188 |
189 | if tb.border {
190 | content += "\n"
191 | }
192 |
193 | tableValue := taga
194 | if !tb.Empty() {
195 | for _, row := range tb.Row {
196 | value := make(map[string]cell.Cell)
197 | for key := range row {
198 | col := tb.Columns.Get(key)
199 | value[col.String()] = row[key]
200 | }
201 | tableValue = append(tableValue, value)
202 | }
203 | tableValue = append(tableValue, tag)
204 | }
205 |
206 | content += tb.printGroup(tableValue, columnMaxLength)
207 | return tb.end(content)
208 | }
209 |
210 | // Empty method is used to determine whether the table is empty.
211 | func (tb *Table) Empty() bool {
212 | return tb.Length() == 0
213 | }
214 |
215 | // Length method returns an integer indicates the length of the table row.
216 | func (tb *Table) Length() int {
217 | return len(tb.Row)
218 | }
219 |
220 | func (tb *Table) GetValues() []map[string]string {
221 | values := make([]map[string]string, 0)
222 | for _, value := range tb.Row {
223 | ms := make(map[string]string)
224 | for k, v := range value {
225 | ms[k] = v.String()
226 | }
227 | values = append(values, ms)
228 | }
229 | return values
230 | }
231 |
232 | func (tb *Table) Exist(value map[string]string) bool {
233 | for _, row := range tb.Row {
234 | exist := true
235 | for key := range value {
236 | v, ok := row[key]
237 | if !ok || v.String() != value[key] {
238 | exist = false
239 | break
240 | }
241 | }
242 | if exist {
243 | return exist
244 | }
245 | }
246 | return false
247 | }
248 |
249 | func (tb *Table) json(indent int) ([]byte, error) {
250 | data := make([]map[string]string, 0)
251 | for _, row := range tb.Row {
252 | element := make(map[string]string)
253 | for col, value := range row {
254 | element[col] = value.String()
255 | }
256 | data = append(data, element)
257 | }
258 |
259 | if indent < 0 {
260 | indent = 0
261 | }
262 | elems := make([]string, 0)
263 | for i := 0; i < indent; i++ {
264 | elems = append(elems, " ")
265 | }
266 |
267 | return json.MarshalIndent(data, "", strings.Join(elems, " "))
268 | }
269 |
270 | // The JSON method returns the JSON string corresponding to the gotable. The indent argument represents the indent
271 | // value. If index is less than zero, the JSON method treats it as zero.
272 | func (tb *Table) JSON(indent int) (string, error) {
273 | bytes, err := tb.json(indent)
274 | if err != nil {
275 | return "", err
276 | }
277 | return string(bytes), nil
278 | }
279 |
280 | // The XML method returns the XML format string corresponding to the gotable. The indent argument represents the indent
281 | // value. If index is less than zero, the XML method treats it as zero.
282 | func (tb *Table) XML(indent int) string {
283 | if indent < 0 {
284 | indent = 0
285 | }
286 | indents := make([]string, indent)
287 | for i := 0; i < indent; i++ {
288 | indents[i] = " "
289 | }
290 | indentString := strings.Join(indents, "")
291 |
292 | contents := []string{"", ""}
293 | for _, row := range tb.Row {
294 | contents = append(contents, indentString+"")
295 | for name := range row {
296 | line := indentString + indentString + fmt.Sprintf("<%s>%s%s>", name, row[name], name)
297 | contents = append(contents, line)
298 | }
299 | contents = append(contents, indentString+"
")
300 | }
301 | contents = append(contents, "
")
302 | content := strings.Join(contents, "\n")
303 | return content
304 | }
305 |
306 | func (tb *Table) CloseBorder() {
307 | tb.border = false
308 | }
309 |
310 | func (tb *Table) OpenBorder() {
311 | tb.border = true
312 | }
313 |
314 | func (tb *Table) Align(column string, mode int) {
315 | for _, h := range tb.Columns.base {
316 | if h.Original() == column {
317 | h.SetAlign(mode)
318 | return
319 | }
320 | }
321 | }
322 |
323 | func (tb *Table) ToJsonFile(path string, indent int) error {
324 | if !util.IsJsonFile(path) {
325 | return fmt.Errorf("%s: not a regular json file", path)
326 | }
327 |
328 | bytes, err := tb.json(indent)
329 | if err != nil {
330 | return err
331 | }
332 |
333 | file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0666)
334 | if err != nil {
335 | return err
336 | }
337 | defer func(file *os.File) {
338 | _ = file.Close()
339 | }(file)
340 |
341 | _, err = file.Write(bytes)
342 | if err != nil {
343 | return err
344 | }
345 | return nil
346 | }
347 |
348 | func (tb *Table) ToCSVFile(path string) error {
349 | if !util.IsCSVFile(path) {
350 | return exception.NotARegularCSVFile(path)
351 | }
352 | file, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0666)
353 | if err != nil {
354 | return err
355 | }
356 | defer func(file *os.File) {
357 | _ = file.Close()
358 | }(file)
359 | writer := csv.NewWriter(file)
360 |
361 | contents := make([][]string, 0)
362 | columns := tb.GetColumns()
363 | contents = append(contents, columns)
364 | for _, value := range tb.GetValues() {
365 | content := make([]string, 0)
366 | for _, col := range columns {
367 | content = append(content, value[col])
368 | }
369 | contents = append(contents, content)
370 | }
371 |
372 | err = writer.WriteAll(contents)
373 | if err != nil {
374 | return err
375 | }
376 | writer.Flush()
377 | err = writer.Error()
378 | if err != nil {
379 | return err
380 | }
381 | return nil
382 | }
383 |
384 | func (tb *Table) HasColumn(column string) bool {
385 | for index := range tb.Columns.base {
386 | if tb.Columns.base[index].Original() == column {
387 | return true
388 | }
389 | }
390 | return false
391 | }
392 |
393 | func (tb *Table) EqualColumns(other *Table) bool {
394 | return tb.Columns.Equal(other.Columns)
395 | }
396 |
397 | func (tb *Table) SetColumnColor(columnName string, display, fount, background int) {
398 | background += 10
399 | for _, col := range tb.Columns.base {
400 | if col.Original() == columnName {
401 | col.SetColor(display, fount, background)
402 | break
403 | }
404 | }
405 | }
406 |
407 | // GoString method used to implement fmt.GoStringer.
408 | func (tb *Table) GoString() string {
409 | resultList := tb.header()
410 | values := make([]string, 0)
411 | for _, row := range tb.Row {
412 | value := make([]string, 0)
413 | for _, column := range tb.Columns.base {
414 | v, ok := row[column.Original()]
415 | if !ok {
416 | value = append(value, column.Default())
417 | } else {
418 | value = append(value, v.Original())
419 | }
420 | }
421 | values = append(values, strings.Join(value, ","))
422 | }
423 |
424 | resultList = append(resultList, fmt.Sprintf("Row:[%v]", strings.Join(values, "; ")))
425 | return fmt.Sprintf("{%s}", strings.Join(resultList, "; "))
426 | }
427 |
--------------------------------------------------------------------------------
/test_csv.csv:
--------------------------------------------------------------------------------
1 | Name,ID,salary
2 | employee-0,000,60000
3 | employee-1,001,60000
4 | employee-2,002,60000
5 |
6 |
--------------------------------------------------------------------------------
/util/drop.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | "fmt"
5 | )
6 |
7 | // DeprecatedTips
8 | // Arguments:
9 | // - deleteFunction: delete function name
10 | // - newFunction: new function name
11 | // - version: support version
12 | // - functionType: method or function
13 | func DeprecatedTips(
14 | deleteFunction,
15 | newFunction,
16 | version,
17 | functionType string) {
18 | fmt.Printf("%s `%s` will no longer supported."+
19 | " You can use the `%s` %s instead of `%s` %s."+
20 | " This %s will be removed in version %s.\n",
21 | Capitalize(functionType), deleteFunction, newFunction,
22 | functionType, deleteFunction, functionType, functionType, version,
23 | )
24 | }
25 |
--------------------------------------------------------------------------------
/util/file.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | "os"
5 | "strings"
6 | )
7 |
8 | func IsFile(path string) bool {
9 | stat, err := os.Stat(path)
10 | if err != nil {
11 | return false
12 | }
13 |
14 | return !stat.IsDir()
15 | }
16 |
17 | func isFormatFile(path, format string) bool {
18 | pathSlice := strings.Split(path, ".")
19 | return pathSlice[len(pathSlice)-1] == format
20 | }
21 |
22 | func IsJsonFile(path string) bool {
23 | return isFormatFile(path, "json")
24 | }
25 |
26 | func IsCSVFile(path string) bool {
27 | return isFormatFile(path, "csv")
28 | }
29 |
--------------------------------------------------------------------------------
/util/string.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | "strings"
5 | "unicode"
6 | )
7 |
8 | const (
9 | chineseSymbol = "!……(),。?、"
10 | )
11 |
12 | func Capitalize(s string) string {
13 | if len(s) < 1 {
14 | return s
15 | }
16 | firstLetter := s[0]
17 | if firstLetter < 65 || (firstLetter > 90 && firstLetter < 97) ||
18 | firstLetter > 122 {
19 | return s
20 | }
21 | return strings.ToUpper(string(s[0])) + s[1:]
22 | }
23 |
24 | func Length(s string) int {
25 | length := 0
26 | for _, c := range s {
27 | if isChinese(c) {
28 | length += 2
29 | } else {
30 | length += 1
31 | }
32 | }
33 | return length
34 | }
35 |
36 | func isChinese(c int32) bool {
37 | if unicode.Is(unicode.Han, c) {
38 | return true
39 | }
40 |
41 | for _, s := range chineseSymbol {
42 | if c == s {
43 | return true
44 | }
45 | }
46 | return false
47 | }
48 |
--------------------------------------------------------------------------------