1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
|
/*
* util_fmt - Format structured key-value data as JSON, text pairs, or CSV
*
* Copyright IBM Corp. 2024
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
*
* This module provides helper functions for converting structured key-value
* data into different output formats.
*
* Benefits:
* - Output format can be dynamically configured at run-time
* - Callers do not need to add extra code for each output format
* - Some format-specific requirements such as quoting, indentation, and
* comma-placement are automated
*
* Basic API calling sequence:
*
* util_fmt_init() => Select output format
* util_fmt_obj_start() => Start a new object or list
* util_fmt_pair() => Emit a key-value pair
* util_fmt_obj_end() => End the latest object or list
* util_fmt_exit() => Cleanup
*
* Note:
* - Supported data elements are objects, lists and key-value pairs (mappings)
* - Scalars are only supported as part of a mapping
* - For CSV output and key filtering, mapping keys must be unique - this can
* be achieved either by choosing unique key names or by including object
* names via the FMT_PREFIX flag
* - For CSV output, at least one object or list with the FMT_ROW flag must be
* emitted
* - Common tool-specific meta-information such as API-level, tool version,
* etc. is automatically added to the output
*/
#ifndef LIB_UTIL_FMT_H
#define LIB_UTIL_FMT_H
#include <stdbool.h>
#include <stdio.h>
/* Flag value for default behavior (all flag types). */
#define FMT_DEFAULT 0
/* Names of supported output format types. */
#define FMT_TYPE_NAMES "json json-seq pairs csv"
/**
* enum util_fmt_t - Output format types.
* @FMT_JSON: JavaScript Object Notation output data structure
* @FMT_JSONSEQ: Sequence of JSON data structures according to RFC7464
* @FMT_PAIRS: Textual key=value pairs
* @FMT_CSV: Comma-separated-values output
*
* Use these types with util_fmt_init() to control the output format.
*/
enum util_fmt_t {
FMT_JSON,
FMT_JSONSEQ,
FMT_PAIRS,
FMT_CSV,
};
/**
* enum util_fmt_flags_t - Format control flags.
* @FMT_NOPREFIX: (pairs) Remove object hierarchy prefix from keys
* @FMT_KEEPINVAL: (all) Print mappings even if value is marked as invalid
* Values will be replaced with null (JSON) or an empty
* string
* @FMT_QUOTEALL: (all) Add quotes to all mapping values
* @FMT_FILTER: (all) Ignore keys not announced via util_fmt_add_key()
* @FMT_HANDLEINT: (json) Ensure correct JSON closure when interrupted
* @FMT_NOMETA: (all) Do not emit tool meta-data
* @FMT_WARN: (all) Warn about incorrect API usage
*
* Use these flags with util_fmt_init() to control generic aspects.
*/
enum util_fmt_flags_t {
FMT_NOPREFIX = (1 << 0),
FMT_KEEPINVAL = (1 << 1),
FMT_QUOTEALL = (1 << 2),
FMT_FILTER = (1 << 3),
FMT_HANDLEINT = (1 << 4),
FMT_NOMETA = (1 << 5),
FMT_WARN = (1 << 6),
};
/**
* enum util_fmt_oflags_t - Object flags.
* @FMT_LIST: (all) Object is a list
* @FMT_ROW: (csv) Start a new CSV row with this object
* @FMT_PREFIX: (all) Include object name in key prefix for CSV headings
* and filter keys
*
* Use these flags with util_fmt_obj_start() to control object related
* aspects.
*/
enum util_fmt_oflags_t {
FMT_LIST = (1 << 0),
FMT_ROW = (1 << 1),
FMT_PREFIX = (1 << 2),
};
/**
* enum util_fmt_mflags_t - Mapping flags.
* @FMT_QUOTE: (all) Quote value
* @FMT_INVAL: (all) Mark value as invalid
* @FMT_PERSIST: (csv) Keep value across CSV rows until overwritten
*
* Use these flags with util_fmt_pair() to control mapping related aspects.
*/
enum util_fmt_mflags_t {
FMT_QUOTE = (1 << 0),
FMT_INVAL = (1 << 1),
FMT_PERSIST = (1 << 2),
};
/**
* util_fmt_init() - Initialize output formatter.
* @fd : Output file descriptor
* @type : Output format type
* @flags: Formatting parameters
* @api_level: Output format level indicator
*
* Prepare for writing formatted output with the given @type to @fd. Additional
* @flags can be specified to control certain output aspects (see &enum
* util_fmt_flags_t).
*
* @api_level represents an application-specific output format version number:
* this number starts at 1 and must be increased whenever an incompatible format
* change is introduced, e.g. when a non-optional object or mapping is removed
* or used for different data.
*/
void util_fmt_init(FILE *fd, enum util_fmt_t type, unsigned int flags,
int api_level);
/**
* util_fmt_exit() - Release resources used by output formatter.
*
* Release all resources currently in use by the output formatter.
*/
void util_fmt_exit(void);
/**
* util_fmt_name_to_type() - Convert format name to type identifier.
* @name: Format name
* @type: Pointer to resulting format type identifier
*
* Search supported output format types for a type with associated @name. If
* found, store resulting type identifier in @type.
*
* Return: %true if type is found, %false otherwise.
*/
bool util_fmt_name_to_type(const char *name, enum util_fmt_t *type);
/**
* util_fmt_set_indent() - Set indentation parameters.
* @base : Base indentation level to apply to all output lines (default 0)
* @width : Number of indentation characters per intendation level (default 2)
* @ind_char: Indentation characters to use (default space).
*/
void util_fmt_set_indent(unsigned int base, unsigned int width, char ind_char);
/**
* util_fmt_add_key() - Register expected mapping keys.
* @fmt: Format string to generate key
*
* Register a mapping key before the associated key-value pair is emitted.
*
* Use this function together with format control flag @FMT_FILTER to ignore all
* key-value pairs for which the key has not been registered. This can be
* useful to allow for dynamically configured filtering of output based on
* a static list of emitted mappings.
*
* When creating CSV output, use this function to register all column keys
* in advance to enable a stable column list in case of rows that do not
* provide data for all columns.
*/
void util_fmt_add_key(const char *fmt, ...);
/**
* util_fmt_obj_start() - Start a new data object.
* @oflags: Flags controlling aspects of this object.
* @fmt : Format string for generating an object name or %NULL.
*
* Use this function to start a new object in output data. Depending on
* @oflags, the new object represents either a normal object or a list. @oflags
* can also be used to indicated that an object corresponds to a new row of
* CSV data. If @fmt is non-%NULL, the resulting name is used in a format
* type specified way:
*
* Pairs:
* - Object names are reflected as dot-separated component in the mapping
* prefix, e.g. 'a.b.key=value'
* - An index is generated for mappings and objects that are part of list,
* e.g. 'a.b[1].key=value'
* JSON:
* - Object names are reflected as key-object mappings, e.g.
* <name>: { }
* - Required commas between objects and mappings are automatically generated
* CSV:
* - Object names and the list type flag have no effect
* - When flag @FMT_ROW is specified, a CSV row will be emitted when
* util_fmt_obj_end() is called for the associated object
*/
void util_fmt_obj_start(unsigned int oflags, const char *fmt, ...);
/**
* util_fmt_obj_end() - Announce the end of the latest data object started.
*
* Each object started with util_fmt_obj_start() must be ended with an
* associated util_fmt_obj_end() call.
*/
void util_fmt_obj_end(void);
/**
* util_fmt_pair() - Emit a key-value pair.
* @mflags: Flags controlling this pair.
* @key : Key for this pair, excluding prefix.
* @fmt : Format string used to generated the pair value.
*
* Emit a key-value pair with the specified @key and the value that results
* from format string @fmt.
*
* Notes:
* - For JSON, a mapping can only occur after util_fmt_obj_start()
* - For CSV, each @key must be unique, either by choosing unique key names
* or by including object names as prefix via the use of FMT_PREFIX in
* parent objects
*/
void util_fmt_pair(unsigned int mflags, const char *key, const char *fmt, ...);
#endif /* LIB_UTIL_FMT_H */
|