DisplayTable renders a table as a string. The input argument can be: - a struct slice. - an interface slice, where the first element is a struct, and all other elements are of the same type, or a TableSeparator. At least one field in the struct must have a `table:""` tag containing the name of the
(out any, sort string, filterColumns []string)
| 99 | // If sort is empty, the input order will be used. If filterColumns is empty or |
| 100 | // nil, all available columns are included. |
| 101 | func DisplayTable(out any, sort string, filterColumns []string) (string, error) { |
| 102 | v := reflect.Indirect(reflect.ValueOf(out)) |
| 103 | |
| 104 | if v.Kind() != reflect.Slice { |
| 105 | return "", xerrors.New("DisplayTable called with a non-slice type") |
| 106 | } |
| 107 | var tableType reflect.Type |
| 108 | if v.Type().Elem().Kind() == reflect.Interface { |
| 109 | if v.Len() == 0 { |
| 110 | return "", xerrors.New("DisplayTable called with empty interface slice") |
| 111 | } |
| 112 | tableType = reflect.Indirect(reflect.ValueOf(v.Index(0).Interface())).Type() |
| 113 | } else { |
| 114 | tableType = v.Type().Elem() |
| 115 | } |
| 116 | |
| 117 | // Get the list of table column headers. |
| 118 | headersRaw, defaultSort, err := typeToTableHeaders(tableType, true) |
| 119 | if err != nil { |
| 120 | return "", xerrors.Errorf("get table headers recursively for type %q: %w", v.Type().Elem().String(), err) |
| 121 | } |
| 122 | if len(headersRaw) == 0 { |
| 123 | return "", xerrors.New(`no table headers found on the input type, make sure there is at least one "table" struct tag`) |
| 124 | } |
| 125 | if sort == "" { |
| 126 | sort = defaultSort |
| 127 | } |
| 128 | headers := make(table.Row, len(headersRaw)) |
| 129 | for i, header := range headersRaw { |
| 130 | headers[i] = strings.ReplaceAll(header, "_", " ") |
| 131 | } |
| 132 | // Verify that the given sort column and filter columns are valid. |
| 133 | if sort != "" || len(filterColumns) != 0 { |
| 134 | headersMap := make(map[string]string, len(headersRaw)) |
| 135 | for _, header := range headersRaw { |
| 136 | headersMap[strings.ToLower(header)] = header |
| 137 | } |
| 138 | |
| 139 | if sort != "" { |
| 140 | sort = strings.ToLower(strings.ReplaceAll(sort, "_", " ")) |
| 141 | h, ok := headersMap[sort] |
| 142 | if !ok { |
| 143 | return "", xerrors.Errorf(`specified sort column %q not found in table headers, available columns are "%v"`, sort, strings.Join(headersRaw, `", "`)) |
| 144 | } |
| 145 | |
| 146 | // Autocorrect |
| 147 | sort = h |
| 148 | } |
| 149 | |
| 150 | for i, column := range filterColumns { |
| 151 | column := strings.ToLower(strings.ReplaceAll(column, "_", " ")) |
| 152 | h, ok := headersMap[column] |
| 153 | if !ok { |
| 154 | return "", xerrors.Errorf(`specified filter column %q not found in table headers, available columns are "%v"`, column, strings.Join(headersRaw, `", "`)) |
| 155 | } |
| 156 | |
| 157 | // Autocorrect |
| 158 | filterColumns[i] = h |