├── .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", 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 | --------------------------------------------------------------------------------