Mstdlib-1.24.0
m_table.h
1/* The MIT License (MIT)
2 *
3 * Copyright (c) 2018 Monetra Technologies, LLC.
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
13 * all 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
21 * THE SOFTWARE.
22 */
23
24#ifndef __M_TABLE_H__
25#define __M_TABLE_H__
26
27/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
28
29#include <mstdlib/base/m_defs.h>
30#include <mstdlib/base/m_types.h>
31
32/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
33
34__BEGIN_DECLS
35
36/*! \addtogroup m_table Table
37 * \ingroup m_formats
38 *
39 * Generic table construction and manipulation.
40 *
41 * JSON input and output conform to [csv2json](https://www.w3.org/TR/csv2json/)
42 * Minimal Mode format.
43 *
44 * @{
45 */
46
47struct M_table;
48typedef struct M_table M_table_t;
49
50/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
51
52/*! Flags controlling behavior of insert by column name operations. */
53typedef enum {
54 M_TABLE_INSERT_NONE = 0, /*!< Fail the insert if the header, or index does not exist. */
55 M_TABLE_INSERT_COLADD = 1 << 0, /*!< Add a named column if it does not exist. */
56 M_TABLE_INSERT_COLIGNORE = 1 << 1 /*!< Ignore names if a corresponding named header does not exist. */
58
59
60/*! Flags controlling table construction. */
61typedef enum {
62 M_TABLE_NONE = 0, /*!< Default operation. */
63 M_TABLE_COLNAME_CASECMP = 1 << 0 /*!< Compare column names case insensitive. */
65
66
67/*! Flags controlling behavior of Markdown output. */
68typedef enum {
69 M_TABLE_MARKDOWN_NONE = 0, /*!< No special formatting. */
70 M_TABLE_MARKDOWN_PRETTYPRINT = 1 << 0, /*!< Pretty print output. */
71 M_TABLE_MARKDOWN_OUTERPIPE = 1 << 1, /*!< Write outer pipes around rows, framing characters. */
72 M_TABLE_MARKDOWN_LINEEND_UNX = 1 << 2, /*!< Use Unix line endings (\\n). */
73 M_TABLE_MARKDOWN_LINEEND_WIN = 1 << 3 /*!< Use Windows line endings (\\r\\n). */
75
76/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
77
78/*! Create a table.
79 *
80 * \param[in] flags M_table_flags_t controlling behavior of the table.
81 *
82 * return Table.
83 */
84M_API M_table_t *M_table_create(M_uint32 flags) M_MALLOC;
85
86
87/*! Destroy a table.
88 *
89 * \param[in] table Table.
90 */
91M_API void M_table_destroy(M_table_t *table) M_FREE(1);
92
93
94/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
95
96/*! Insert a new column into the table.
97 *
98 * \param[in] table Table.
99 * \param[in] colname Optional name associated with the column.
100 *
101 * \return M_TRUE when the column is successfully added. Otherwise, M_FALSE.
102 */
103M_API M_bool M_table_column_insert(M_table_t *table, const char *colname);
104
105
106/*! Insert a new column into the table at a specified index.
107 *
108 * \param[in] table Table.
109 * \param[in] idx Index to insert at. Cannot be larger than the number of columns (last idx+1).
110 * \param[in] colname Optional name associated with the column.
111 *
112 * \return M_TRUE when the column is successfully added. Otherwise, M_FALSE.
113 */
114M_API M_bool M_table_column_insert_at(M_table_t *table, size_t idx, const char *colname);
115
116
117/*! Get the name associated with a column.
118 *
119 * \param[in] table Table.
120 * \param[in] idx Column index.
121 *
122 * \return NULL if no name associated. Otherwise, name. Name can be an empty string is
123 * it was set to an empty string.
124 */
125M_API const char *M_table_column_name(const M_table_t *table, size_t idx);
126
127
128/*! Associate a name with a column.
129 *
130 * \param[in] table Table.
131 * \param[in] idx Column index.
132 * \param[in] colname Name.
133 *
134 * \return M_TRUE on success. Otherwise, M_FALSE. Can fail if a column with the given name
135 * already exists.
136 */
137M_API M_bool M_table_column_set_name(M_table_t *table, size_t idx, const char *colname);
138
139
140/*! Get the index for a column with a given name.
141 *
142 * \param[in] table Table.
143 * \param[in] colname Column name.
144 * \param[out] idx Index of column
145 *
146 * \return M_TRUE if the column exists. Otherwise, M_FALSE.
147 */
148M_API M_bool M_table_column_idx(const M_table_t *table, const char *colname, size_t *idx);
149
150
151/*! Sort rows based on data in a given column name.
152 *
153 * Supports secondary column sorting when values in the primary column are equivalent.
154 *
155 * \param[in] table Table.
156 * \param[in] colname Column name for primary sorting.
157 * \param[in] primary_sort Sort comparison function for `colname`.
158 * \param[in] secondary_colname Column name for secondary sorting. Only used when values from
159 * primary sort are equivalent.
160 * \param[in] secondary_sort Sort comparison function for `secondary_colname`.
161 * \param[in] thunk Thunk passed to comparison functions.
162 */
163M_API void M_table_column_sort_data(M_table_t *table, const char *colname, M_sort_compar_t primary_sort, const char *secondary_colname, M_sort_compar_t secondary_sort, void *thunk);
164
165
166/*! Sort rows based on data in a given column index.
167 *
168 * \param[in] table Table.
169 * \param[in] idx Column index used for sorting.
170 * \param[in] primary_sort Sort comparison function for `colname`.
171 * \param[in] secondary_idx Column index for secondary sorting. Only used when values from
172 * primary sort are equivalent.
173 * \param[in] secondary_sort Sort comparison function for `secondary_colname`.
174 * \param[in] thunk Thunk passed to comparison functions.
175 */
176M_API void M_table_column_sort_data_at(M_table_t *table, size_t idx, M_sort_compar_t primary_sort, size_t secondary_idx, M_sort_compar_t secondary_sort, void *thunk);
177
178
179/*! Sort column based on names.
180 *
181 * It is not required for all columns to be named. Unnamed columns will
182 * be passed to the sort function as an empty string ("").
183 *
184 * \param[in] table Table.
185 * \param[in] sort Sort comparison function.
186 * \param[in] thunk Thunk passed to comparison function.
187 */
188M_API void M_table_column_order(M_table_t *table, M_sort_compar_t sort, void *thunk);
189
190
191/*! Remove a column with a given name.
192 *
193 * \param[in] table Table.
194 * \param[in] colname Column name.
195 */
196M_API void M_table_column_remove(M_table_t *table, const char *colname);
197
198
199/*! Remove a column at a given index.
200 *
201 * \param[in] table Table.
202 * \param[in] idx Column index.
203 */
204M_API void M_table_column_remove_at(M_table_t *table, size_t idx);
205
206
207/*! Remove empty columns.
208 *
209 * A column is empty when no rows have data for that column.
210 *
211 * param[in] table Table.
212 *
213 * \return Number of columns removed.
214 */
216
217
218/*! Get the number of columns in the table.
219 *
220 * \param[in] table Table.
221 *
222 * \return Column count.
223 */
224M_API size_t M_table_column_count(const M_table_t *table);
225
226
227/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
228
229/*! Inset a row into the table.
230 *
231 * \param[in] table Table.
232 *
233 * \return Index the row was inserted at.
234 */
235M_API size_t M_table_row_insert(M_table_t *table);
236
237
238/*! Insert a row into the table at a given index.
239 *
240 * \param[in] table Table.
241 * \param[in] idx Index to insert at. Cannot be larger than the number of rows (last idx+1).
242 *
243 * \return M_TRUE if the row was inserted. Otherwise, M_FALSE.
244 */
245M_API M_bool M_table_row_insert_at(M_table_t *table, size_t idx);
246
247
248/*! Insert data from a dict into the table creating a new row.
249 *
250 * Dictionary key is the column name and the value is the cell value.
251 *
252 * \param[in] table Table.
253 * \param[in] data Data to insert.
254 * \param[in] flags M_table_insert_flags_t flags controlling insert behavior. Specifically
255 * handling of situations where the key in data is not a current column.
256 * \param[out] idx Index the row was inserted at. Will always be last idx+1 before insertion.
257 *
258 * \return M_TRUE if the row was inserted. Otherwise, M_FALSE.
259 */
260M_API M_bool M_table_row_insert_dict(M_table_t *table, const M_hash_dict_t *data, M_uint32 flags, size_t *idx);
261
262
263/*! Insert data from a dict into the table at a given idex.
264 *
265 * Dictionary key is the column name and the value is the cell value.
266 *
267 * \param[in] table Table.
268 * \param[in] idx Index to insert at. Cannot be larger than the number of rows (last idx+1).
269 * \param[in] data Data to insert.
270 * \param[in] flags M_table_insert_flags_t flags controlling insert behavior. Specifically
271 * handling of situations where the key in data is not a current column.
272 *
273 * \return M_TRUE if the row was inserted. Otherwise, M_FALSE.
274 */
275M_API M_bool M_table_row_insert_dict_at(M_table_t *table, size_t idx, const M_hash_dict_t *data, M_uint32 flags);
276
277
278/*! Remove a row.
279 *
280 * \param[in] table Table.
281 * \param[in] idx Row index.
282 */
283M_API void M_table_row_remove(M_table_t *table, size_t idx);
284
285
286/*! Remove all empty rows from the table.
287 *
288 * A row is considered empty if there is no data in any column.
289 * An empty string is considered data and the row will not be removed.
290 *
291 * \param[in] table Table.
292 *
293 * \return Number of rows removed.
294 */
296
297
298/*! Get the number of rows in the table.
299 *
300 * \param[in] table Table.
301 *
302 * \return Number of rows.
303 */
304M_API size_t M_table_row_count(const M_table_t *table);
305
306
307/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
308
309/*! Set data in a given cell by column name.
310 *
311 * \param[in] table Table.
312 * \param[in] row Row index.
313 * \param[in] colname Column name.
314 * \param[in] val Value to set. NULL will clear.
315 * \param[in] flags M_table_insert_flags_t flags controlling insert behavior. Specifically
316 * handling of situations where the `colname` is not a current column.
317 *
318 * \return M_TRUE if the value was set. Otherwise, M_FALSE.
319 */
320M_API M_bool M_table_cell_set(M_table_t *table, size_t row, const char *colname, const char *val, M_uint32 flags);
321
322
323/*! Set data in a given cell by index.
324 *
325 * \param[in] table Table.
326 * \param[in] row Row index.
327 * \param[in] col Column index.
328 * \param[in] val Value to set. NULL will clear.
329 *
330 * \return M_TRUE if the value was set. Otherwise, M_FALSE.
331 */
332M_API M_bool M_table_cell_set_at(M_table_t *table, size_t row, size_t col, const char *val);
333
334
335/*! Insert data from a dict into the table.
336 *
337 * Dictionary key is the column name and the value is the cell value.
338 *
339 * \param[in] table Table.
340 * \param[in] row Row index.
341 * \param[in] data Data to insert.
342 * \param[in] flags M_table_insert_flags_t flags controlling insert behavior. Specifically
343 * handling of situations where the key in data is not a current column.
344 *
345 * \return M_TRUE if the row was inserted. Otherwise, M_FALSE.
346 */
347M_API M_bool M_table_cell_set_dict(M_table_t *table, size_t row, const M_hash_dict_t *data, M_uint32 flags);
348
349
350/*! Clear the data from a cell by column name.
351 *
352 * This is the equivalent to calling `M_table_cell_set` with a NULL value.
353 *
354 * \param[in] table Table.
355 * \param[in] row Row index.
356 * \param[in] colname Column name.
357 *
358 * \return M_TRUE if the cell was cleared. Otherwise, M_FALSE. Clearing a column that does not
359 * exist is considered success.
360 */
361M_API M_bool M_table_cell_clear(M_table_t *table, size_t row, const char *colname);
362
363
364/*! Clear the data from a cell by column index.
365 *
366 * This is the equivalent to calling `M_table_cell_set` with a NULL value.
367 *
368 * \param[in] table Table.
369 * \param[in] row Row index.
370 * \param[in] col Column index.
371 *
372 * \return M_TRUE if the cell was cleared. Otherwise, M_FALSE.
373 */
374M_API M_bool M_table_cell_clear_at(M_table_t *table, size_t row, size_t col);
375
376
377/*! Get the data for a cell by column name.
378 *
379 * \param[in] table Table.
380 * \param[in] row Row index.
381 * \param[in] colname Column name.
382 *
383 * \return Cell value. NULL is returned if there is no cell value and on error.
384 */
385M_API const char *M_table_cell(const M_table_t *table, size_t row, const char *colname);
386
387
388/*! Get the data for a cell by column index.
389 *
390 * \param[in] table Table.
391 * \param[in] row Row index.
392 * \param[in] col Column index.
393 *
394 * \return Cell value. NULL is returned if there is no cell value and on error.
395 */
396M_API const char *M_table_cell_at(const M_table_t *table, size_t row, size_t col);
397
398
399/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
400
401/*! Merge two tables together.
402 *
403 * The second (src) table will be destroyed automatically upon completion of this function.
404 * Both tables must have fully named columns. The two tables do not have to have the same
405 * exact columns. They can have different overlapping or non-overlapping column names.
406 *
407 * \param[in,out] dest Pointer by reference to the table receiving the data.
408 * if dest is NULL, the src address will simply be copied to dest.
409 * \param[in,out] src Pointer to the table giving up its data.
410 *
411 * \return M_TRUE if the tables were merged and `src` is destroyed. Otherwise, M_FALSE.
412 * If M_FALSE, `src` is still valid and no data has been added to dest.
413 */
414M_API M_bool M_table_merge(M_table_t **dest, M_table_t *src) M_FREE(2);
415
416
417/*! Duplicate a table.
418 *
419 * \param[in] table Table.
420 *
421 * \return Duplicated table.
422 */
423M_API M_table_t *M_table_duplicate(const M_table_t *table) M_MALLOC;
424
425
426/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
427
428/*! Load CSV formatted data into the table.
429 *
430 * \param[in] table Table.
431 * \param[in] data CSV data.
432 * \param[in] len Length of data to load.
433 * \param[in] delim CSV delimiter character. Typically comma (",").
434 * \param[in] quote CSV quote character. Typically double quote (""").
435 * \param[in] flags M_CSV_FLAGS flags controlling parse behavior.
436 * \param[in] have_header Whether the CSV data has a header.
437 *
438 * \return M_TRUE if the data was loaded. Otherwise, M_FALSE.
439 */
440M_API M_bool M_table_load_csv(M_table_t *table, const char *data, size_t len, char delim, char quote, M_uint32 flags, M_bool have_header);
441
442
443/*! Write the table as CSV.
444 *
445 * \param[in] table Table.
446 * \param[in] delim CSV delimiter character. Typically comma (",").
447 * \param[in] quote CSV quote character. Typically double quote (""").
448 * \param[in] write_header Whether the column names should be written as the CSV header.
449 * All columns should be named if writing a header. However, it is not an
450 * error if there are unnamed columns.
451 *
452 * \return CSV data.
453 */
454M_API char *M_table_write_csv(const M_table_t *table, char delim, char quote, M_bool write_header);
455
456/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
457
458/*! Load JSON formatted data into the table.
459 *
460 * Should be in the form list of a objects who's keys are headers and value are cell value
461 * for the row at the given list index.
462 *
463 * E.g.:
464 *
465 * [
466 * { header1: "value", header2: "value" },
467 * { header1: "value", header2: "value" }
468 * ]
469 *
470 * \param[in] table Table.
471 * \param[in] data JSON string data.
472 * \param[in] len Length of data to load.
473 *
474 * \return M_TRUE if the data was loaded. Otherwise, M_FALSE.
475 */
476M_API M_bool M_table_load_json(M_table_t *table, const char *data, size_t len);
477
478
479/*! Write the table as a JSON node object.
480 *
481 * A NULL table poiner will return NULL.
482 * A table with 0 rows will output a json node as an empty array.
483 *
484 * All columns must be named!
485 *
486 * \param[in] table Table.
487 *
488 * \return JSON node object.
489 */
491
492
493/*! Write the table as a JSON buffer.
494 *
495 * A NULL table poiner will return NULL.
496 * A table with 0 rows will output an empty JSON array (`[]`).
497 *
498 * All columns must be named!
499 *
500 * \param[in] table Table.
501 * \param[in] flags M_json_writer_flags_t flags controlling writing.
502 *
503 * \return JSON data.
504 */
505M_API char *M_table_write_json(const M_table_t *table, M_uint32 flags);
506
507
508/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
509
510/*! Load Markdown formatted data into the table.
511 *
512 * Column justification information will be lost.
513 *
514 * \param[in] table Table.
515 * \param[in] data CSV data.
516 * \param[in] len Length of data to load.
517 *
518 * \return M_TRUE if the data was loaded. Otherwise, M_FALSE.
519 */
520M_API M_bool M_table_load_markdown(M_table_t *table, const char *data, size_t len);
521
522
523/*! Write the table as Markdown.
524 *
525 * \param[in] table Table.
526 * \param[in] flags M_table_markdown_flags_t flags controlling write behavior.
527 *
528 * \return CSV data.
529 */
530M_API char *M_table_write_markdown(const M_table_t *table, M_uint32 flags);
531
532/*! @} */
533
534__END_DECLS
535
536/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
537
538#endif /* __M_TABLE_H__ */
struct M_hash_dict M_hash_dict_t
Definition: m_hash_dict.h:52
struct M_json_node M_json_node_t
Definition: m_json.h:95
int(* M_sort_compar_t)(const void *arg1, const void *arg2, void *thunk)
Definition: m_sort.h:78
void M_table_destroy(M_table_t *table) M_FREE(1)
char * M_table_write_markdown(const M_table_t *table, M_uint32 flags)
M_bool M_table_cell_clear(M_table_t *table, size_t row, const char *colname)
void M_table_row_remove(M_table_t *table, size_t idx)
const char * M_table_column_name(const M_table_t *table, size_t idx)
M_bool M_table_load_json(M_table_t *table, const char *data, size_t len)
M_bool M_table_merge(M_table_t **dest, M_table_t *src) M_FREE(2)
M_bool M_table_row_insert_dict_at(M_table_t *table, size_t idx, const M_hash_dict_t *data, M_uint32 flags)
M_table_t * M_table_duplicate(const M_table_t *table) M_MALLOC
char * M_table_write_json(const M_table_t *table, M_uint32 flags)
M_bool M_table_column_set_name(M_table_t *table, size_t idx, const char *colname)
size_t M_table_column_remove_empty_columns(M_table_t *table)
M_table_t * M_table_create(M_uint32 flags) M_MALLOC
M_bool M_table_column_insert(M_table_t *table, const char *colname)
M_table_flags_t
Definition: m_table.h:61
M_bool M_table_cell_clear_at(M_table_t *table, size_t row, size_t col)
size_t M_table_row_insert(M_table_t *table)
M_bool M_table_cell_set(M_table_t *table, size_t row, const char *colname, const char *val, M_uint32 flags)
void M_table_column_remove_at(M_table_t *table, size_t idx)
void M_table_column_remove(M_table_t *table, const char *colname)
M_table_markdown_flags_t
Definition: m_table.h:68
size_t M_table_row_remove_empty_rows(M_table_t *table)
M_bool M_table_cell_set_at(M_table_t *table, size_t row, size_t col, const char *val)
void M_table_column_sort_data(M_table_t *table, const char *colname, M_sort_compar_t primary_sort, const char *secondary_colname, M_sort_compar_t secondary_sort, void *thunk)
M_bool M_table_cell_set_dict(M_table_t *table, size_t row, const M_hash_dict_t *data, M_uint32 flags)
struct M_table M_table_t
Definition: m_table.h:48
char * M_table_write_csv(const M_table_t *table, char delim, char quote, M_bool write_header)
size_t M_table_column_count(const M_table_t *table)
void M_table_column_sort_data_at(M_table_t *table, size_t idx, M_sort_compar_t primary_sort, size_t secondary_idx, M_sort_compar_t secondary_sort, void *thunk)
const char * M_table_cell_at(const M_table_t *table, size_t row, size_t col)
M_bool M_table_column_idx(const M_table_t *table, const char *colname, size_t *idx)
M_json_node_t * M_table_create_json(const M_table_t *table)
M_bool M_table_row_insert_dict(M_table_t *table, const M_hash_dict_t *data, M_uint32 flags, size_t *idx)
M_table_insert_flags_t
Definition: m_table.h:53
M_bool M_table_load_markdown(M_table_t *table, const char *data, size_t len)
void M_table_column_order(M_table_t *table, M_sort_compar_t sort, void *thunk)
const char * M_table_cell(const M_table_t *table, size_t row, const char *colname)
M_bool M_table_column_insert_at(M_table_t *table, size_t idx, const char *colname)
M_bool M_table_row_insert_at(M_table_t *table, size_t idx)
size_t M_table_row_count(const M_table_t *table)
M_bool M_table_load_csv(M_table_t *table, const char *data, size_t len, char delim, char quote, M_uint32 flags, M_bool have_header)
@ M_TABLE_NONE
Definition: m_table.h:62
@ M_TABLE_COLNAME_CASECMP
Definition: m_table.h:63
@ M_TABLE_MARKDOWN_NONE
Definition: m_table.h:69
@ M_TABLE_MARKDOWN_OUTERPIPE
Definition: m_table.h:71
@ M_TABLE_MARKDOWN_LINEEND_WIN
Definition: m_table.h:73
@ M_TABLE_MARKDOWN_LINEEND_UNX
Definition: m_table.h:72
@ M_TABLE_MARKDOWN_PRETTYPRINT
Definition: m_table.h:70
@ M_TABLE_INSERT_NONE
Definition: m_table.h:54
@ M_TABLE_INSERT_COLADD
Definition: m_table.h:55
@ M_TABLE_INSERT_COLIGNORE
Definition: m_table.h:56