ascii-chat
Binary v0.15.83-53-gb711d8720
Library lib/v0.3.2-701-gb711d8720
Video chat in your terminal
Loading...
Searching...
No Matches
schema.h File Reference

Schema metadata for config file options. More...

Go to the source code of this file.

Data Structures

 Option metadata for config file parsing. More...

Typedefs

typedef bool(* builder_validate_fn_t) (const void *options_struct)
 Validation function from options builder.

Enumerations

enum  option_context_t { OPTION_CONTEXT_CLI , OPTION_CONTEXT_CONFIG , OPTION_CONTEXT_BOTH }
 Context where an option can appear. More...

Functions

const config_option_metadata_t * config_schema_get_by_toml_key (const char *toml_key)
 Get option metadata by TOML key.
const config_option_metadata_t ** config_schema_get_by_category (const char *category, size_t *count)
 Get all option metadata for a category.
const config_option_metadata_t * config_schema_get_all (size_t *count)
 Get all option metadata.
asciichat_error_t config_schema_build_from_configs (const options_config_t **configs, size_t num_configs)
 Build schema dynamically from options builder configs.
void config_schema_destroy (void)
 Clean up dynamically allocated schema resources.

Detailed Description

Schema metadata for config file options.

This module provides declarative metadata for all configurable options that can appear in TOML configuration files. The schema drives the generic config parser, eliminating duplicate validation code.

Note
This is separate from builder.h's option_descriptor_t which is for CLI parsing. This schema is specifically for TOML config files.

Definition in file schema.h.

Typedef Documentation

◆ builder_validate_fn_t

typedef bool(* builder_validate_fn_t) (const void *options_struct)

Validation function from options builder.

This is the same validation function signature used by the options builder. It receives the full options struct and can perform cross-field validation.

Parameters
options_structFull options struct (cast to void* for generic use)
Returns
true if valid, false after setting error context with SET_ERRNO() if invalid

Definition at line 40 of file schema.h.

Enumeration Type Documentation

◆ option_context_t

Context where an option can appear.

Enumerator
OPTION_CONTEXT_CLI 

CLI-only option (cannot appear in config).

OPTION_CONTEXT_CONFIG 

Config-only option (never on CLI).

OPTION_CONTEXT_BOTH 

Can appear in both CLI and config.

Definition at line 25 of file schema.h.

25 {
option_context_t
Context where an option can appear.
Definition schema.h:25
@ OPTION_CONTEXT_BOTH
Can appear in both CLI and config.
Definition schema.h:28
@ OPTION_CONTEXT_CONFIG
Config-only option (never on CLI).
Definition schema.h:27
@ OPTION_CONTEXT_CLI
CLI-only option (cannot appear in config).
Definition schema.h:26

Function Documentation

◆ config_schema_build_from_configs()

asciichat_error_t config_schema_build_from_configs ( const options_config_t ** configs,
size_t num_configs )

Build schema dynamically from options builder configs.

This function builds the config schema by merging all mode configs (server, client, mirror, etc.). It generates TOML keys, CLI flags, categories, and types from the builder's option descriptors.

Parameters
configsArray of option configs (can contain NULL entries)
num_configsNumber of configs in the array
Returns
ASCIICHAT_OK on success, error code on failure
Note
This should be called once during initialization before any config parsing

Definition at line 252 of file schema.c.

252 {
253 // Free existing dynamic schema if any
254 if (g_dynamic_schema) {
255 SAFE_FREE(g_dynamic_schema);
256 g_dynamic_schema = NULL;
257 g_dynamic_schema_count = 0;
258 }
259
260 // Free existing string storage
261 if (g_dynamic_strings) {
262 for (size_t i = 0; i < g_dynamic_strings_count; i++) {
263 SAFE_FREE(g_dynamic_strings[i]);
264 }
265 SAFE_FREE(g_dynamic_strings);
266 g_dynamic_strings = NULL;
267 g_dynamic_strings_count = 0;
268 g_dynamic_strings_capacity = 0;
269 }
270
271 // Count total unique descriptors (by offset) across all configs
272 // Use a simple approach: collect all descriptors, then deduplicate by offset
273 const option_descriptor_t *all_descriptors[SCHEMA_OPTION_CAPACITY] = {0}; // Max expected options
274 size_t descriptor_count = 0;
275
276 // Collect descriptors from all configs
277 for (size_t cfg_idx = 0; cfg_idx < num_configs; cfg_idx++) {
278 const options_config_t *config = configs[cfg_idx];
279 if (!config) {
280 log_debug("Config %zu is NULL, skipping", cfg_idx);
281 continue;
282 }
283
284 if (!config->descriptors || config->num_descriptors == 0) {
285 log_debug("Config %zu has no descriptors", cfg_idx);
286 continue;
287 }
288
289 for (size_t i = 0; i < config->num_descriptors && descriptor_count < SCHEMA_OPTION_CAPACITY; i++) {
290 const option_descriptor_t *desc = &config->descriptors[i];
291 if (!desc) {
292 log_warn("Descriptor at index %zu in config %zu is NULL", i, cfg_idx);
293 continue;
294 }
295 if (should_add_descriptor(desc, all_descriptors, descriptor_count)) {
296 all_descriptors[descriptor_count++] = desc;
297 }
298 }
299 }
300
301 // Allocate schema array
302 g_dynamic_schema = SAFE_MALLOC(descriptor_count * sizeof(config_option_metadata_t), config_option_metadata_t *);
303 if (!g_dynamic_schema) {
304 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate dynamic schema");
305 }
306
307 // Build schema entries
308 g_dynamic_schema_count = 0;
309 char toml_key_buffer[BUFFER_SIZE_SMALL];
310 char cli_flag_buffer[OPTIONS_CONFIG_SCHEMA_CLI_FLAG_BUFFER_SIZE];
311 char category_buffer[OPTIONS_CONFIG_SCHEMA_CATEGORY_BUFFER_SIZE];
312
313 for (size_t i = 0; i < descriptor_count; i++) {
314 const option_descriptor_t *desc = all_descriptors[i];
315 if (!desc || !desc->long_name || !desc->group) {
316 continue;
317 }
318
319 config_option_metadata_t *meta = &g_dynamic_schema[g_dynamic_schema_count++];
320 // Zero-initialize the metadata struct to ensure all fields are initialized
321 memset(meta, 0, sizeof(*meta));
322
323 // Use builder's type directly
324 meta->type = desc->type;
325
326 // Generate category: lowercase the group name from builder
327 SAFE_STRNCPY(category_buffer, desc->group, sizeof(category_buffer) - 1);
328 category_buffer[sizeof(category_buffer) - 1] = '\0';
329 str_tolower(category_buffer);
330
331 // Generate TOML key: "category.field_name" (category is lowercase group, field_name is long_name with
332 // dashes->underscores)
333 const char *field_name = desc->long_name;
334 if (strcmp(category_buffer, "otel") == 0 && strncmp(field_name, "otel-", 5) == 0)
335 field_name += 5;
336 if (!generate_toml_key(category_buffer, field_name, toml_key_buffer, sizeof(toml_key_buffer))) {
337 g_dynamic_schema_count--; // Skip this entry
338 continue;
339 }
340
341 // Store generated strings (allocate and store)
342 meta->toml_key = store_dynamic_string(toml_key_buffer);
343 meta->category = store_dynamic_string(category_buffer);
344 if (!meta->toml_key || !meta->category) {
345 // Failed to allocate strings, skip this entry
346 g_dynamic_schema_count--;
347 continue;
348 }
349
350 // Generate CLI flag: "--long-name"
351 if (generate_cli_flag(desc->long_name, cli_flag_buffer, sizeof(cli_flag_buffer))) {
352 meta->cli_flag = store_dynamic_string(cli_flag_buffer);
353 if (!meta->cli_flag) {
354 // Failed to allocate, but continue (cli_flag can be NULL)
355 }
356 } else {
357 meta->cli_flag = NULL;
358 }
359
360 // Set other fields
361 meta->context = OPTION_CONTEXT_BOTH; // Most options can appear in both CLI and config
362 meta->field_offset = desc->offset;
363 meta->field_size = get_field_size(meta->type, desc->offset);
364 // Use builder's validate function directly - it receives the full options struct
365 meta->validate_fn = desc->validate;
366 // Use builder's parse function for CALLBACK types
367 meta->parse_fn = desc->parse_fn;
368 meta->description = desc->help_text;
369
370 // Set mode_bitmask from option descriptor
371 // The descriptor should have mode_bitmask set from the registry
372 meta->mode_bitmask = desc->mode_bitmask;
373
374 // Copy mode_default_getter function pointer
376
377 // Copy constraints from descriptor's metadata
378 // For integer types, copy numeric_range to int_range (always copy, even if min is 0)
379 memset(&meta->constraints, 0, sizeof(meta->constraints));
380 // Always copy numeric_range if descriptor has it (check max or min)
381 if (desc && desc->metadata.numeric_range.max > 0) {
384 }
385 }
386
387 g_schema_built = true;
388 return ASCIICHAT_OK;
389}
#define SET_ERRNO(code,...)
#define BUFFER_SIZE_SMALL
Small buffer size (256 bytes).
#define SAFE_STRNCPY(dst, src, size)
Definition common.h:423
#define SAFE_FREE(ptr)
Definition common.h:377
#define SAFE_MALLOC(size, cast)
Definition common.h:265
@ ERROR_MEMORY
Definition error_codes.h:51
@ ASCIICHAT_OK
Definition error_codes.h:46
#define log_warn(...)
Log a WARN message.
Definition log/log.h:621
#define log_debug(...)
Log a DEBUG message.
Definition log/log.h:595
#define OPTIONS_CONFIG_SCHEMA_CLI_FLAG_BUFFER_SIZE
Definition schema.c:8
#define OPTIONS_CONFIG_SCHEMA_CATEGORY_BUFFER_SIZE
Definition schema.c:9
#define SCHEMA_OPTION_CAPACITY
Definition schema.c:12
Option metadata for config file parsing.
Definition schema.h:48
option_context_t context
Where this option can appear.
Definition schema.h:52
option_type_t type
Value type (from builder).
Definition schema.h:51
size_t field_size
Size of field in options_t.
Definition schema.h:55
bool(* parse_fn)(const char *arg, void *dest, char **error_msg)
Custom parser for callbacks (or NULL).
Definition schema.h:61
struct config_option_metadata_t::@047245164140222033316365214353336112047172131121::@271224077242344302200333333005050320172070077212 int_range
option_mode_bitmask_t mode_bitmask
Which modes this option applies to.
Definition schema.h:64
const void *(* mode_default_getter)(asciichat_mode_t mode)
Get mode-specific default value (NULL if not mode-aware).
Definition schema.h:82
size_t field_offset
offsetof(options_t, field) - where to store value
Definition schema.h:54
const char * description
Description for docs/help generation.
Definition schema.h:67
const char * category
Category name (e.g., "network", "client", "audio").
Definition schema.h:53
builder_validate_fn_t validate_fn
Builder's validation function (can be NULL for simple types).
Definition schema.h:58
const char * toml_key
TOML key path (e.g., "network.port", "client.address").
Definition schema.h:49
const char * cli_flag
CLI flag name (e.g., "--port") or NULL if no CLI flag.
Definition schema.h:50
union config_option_metadata_t::@047245164140222033316365214353336112047172131121 constraints
The complete registry entry for one command-line/configuration option.
Definition builder.h:249
option_mode_bitmask_t mode_bitmask
Which modes this option applies to.
Definition builder.h:286
const char * help_text
Description for –help.
Definition builder.h:259
size_t offset
offsetof(struct, field) - where to store value
Definition builder.h:256
const char * group
Group name for help sections (e.g., "NETWORK OPTIONS").
Definition builder.h:260
bool(* parse_fn)(const char *arg, void *dest, char **error_msg)
Definition builder.h:274
bool(* validate)(const void *options_struct)
Definition builder.h:271
const char * long_name
Long option name (e.g., "port").
Definition builder.h:251
const void *(* mode_default_getter)(asciichat_mode_t mode)
Get mode-specific default value (NULL if not mode-aware).
Definition builder.h:292
option_metadata_t metadata
Metadata for shell completions (enums, ranges, examples, etc.).
Definition builder.h:289
option_type_t type
Value type.
Definition builder.h:255
int max
Maximum value (or 0 if no limit).
Definition builder.h:208
int min
Minimum value (or 0 if no limit).
Definition builder.h:207
struct option_metadata_t::@061372117173071240020206164036331043262170062306 numeric_range
Immutable parsing tables produced from an options builder.
Definition builder.h:414
option_descriptor_t * descriptors
Array of option descriptors.
Definition builder.h:415
size_t num_descriptors
Number of descriptors.
Definition builder.h:416

References ASCIICHAT_OK, BUFFER_SIZE_SMALL, config_option_metadata_t::category, config_option_metadata_t::cli_flag, config_option_metadata_t::constraints, config_option_metadata_t::context, config_option_metadata_t::description, options_config_t::descriptors, ERROR_MEMORY, config_option_metadata_t::field_offset, config_option_metadata_t::field_size, option_descriptor_t::group, option_descriptor_t::help_text, config_option_metadata_t::int_range, log_debug, log_warn, option_descriptor_t::long_name, config_option_metadata_t::max, option_metadata_t::max, option_descriptor_t::metadata, config_option_metadata_t::min, option_metadata_t::min, config_option_metadata_t::mode_bitmask, option_descriptor_t::mode_bitmask, config_option_metadata_t::mode_default_getter, option_descriptor_t::mode_default_getter, options_config_t::num_descriptors, option_metadata_t::numeric_range, option_descriptor_t::offset, OPTION_CONTEXT_BOTH, OPTIONS_CONFIG_SCHEMA_CATEGORY_BUFFER_SIZE, OPTIONS_CONFIG_SCHEMA_CLI_FLAG_BUFFER_SIZE, config_option_metadata_t::parse_fn, option_descriptor_t::parse_fn, SAFE_FREE, SAFE_MALLOC, SAFE_STRNCPY, SCHEMA_OPTION_CAPACITY, SET_ERRNO, config_option_metadata_t::toml_key, config_option_metadata_t::type, option_descriptor_t::type, option_descriptor_t::validate, and config_option_metadata_t::validate_fn.

Referenced by action_create_config(), and config_create_default().

◆ config_schema_destroy()

void config_schema_destroy ( void )

Clean up dynamically allocated schema resources.

Frees all memory allocated by config_schema_build_from_configs(), including the dynamic schema array and all associated strings. Safe to call multiple times or if schema was never built.

Note
This should be called during shutdown to prevent memory leaks

Definition at line 469 of file schema.c.

469 {
470 if (!g_schema_built) {
471 return; // Nothing to clean up
472 }
473
474 // Free all dynamically allocated strings (toml_key, cli_flag, category)
475 if (g_dynamic_strings) {
476 for (size_t i = 0; i < g_dynamic_strings_count; i++) {
477 SAFE_FREE(g_dynamic_strings[i]);
478 }
479 SAFE_FREE(g_dynamic_strings);
480 g_dynamic_strings = NULL;
481 g_dynamic_strings_count = 0;
482 g_dynamic_strings_capacity = 0;
483 }
484
485 // Free the schema array itself
486 if (g_dynamic_schema) {
487 SAFE_FREE(g_dynamic_schema);
488 g_dynamic_schema = NULL;
489 g_dynamic_schema_count = 0;
490 }
491
492 g_schema_built = false;
493}

References SAFE_FREE.

Referenced by options_cleanup_schema(), and options_state_destroy().

◆ config_schema_get_all()

const config_option_metadata_t * config_schema_get_all ( size_t * count)

Get all option metadata.

Parameters
countOutput: total number of options
Returns
Array of all metadata (NULL-terminated)

Definition at line 449 of file schema.c.

449 {
450 // Schema must be built before use
451 if (!g_schema_built || !g_dynamic_schema) {
452 if (count) {
453 *count = 0;
454 }
455 SET_ERRNO(ERROR_INVALID_STATE, "Schema not built");
456 return NULL;
457 }
458
459 if (count) {
460 *count = g_dynamic_schema_count;
461 }
462 return g_dynamic_schema;
463}
@ ERROR_INVALID_STATE

References ERROR_INVALID_STATE, and SET_ERRNO.

Referenced by config_create_default().

◆ config_schema_get_by_category()

const config_option_metadata_t ** config_schema_get_by_category ( const char * category,
size_t * count )

Get all option metadata for a category.

Parameters
categoryCategory name (e.g., "network")
countOutput: number of options in category
Returns
Array of metadata pointers (NULL-terminated)

Definition at line 416 of file schema.c.

416 {
417 static const config_option_metadata_t *results[SCHEMA_CATEGORY_OPTION_CAPACITY]; // Max options per category
418 size_t result_count = 0;
419
420 if (!category) {
421 if (count) {
422 *count = 0;
423 }
424 return NULL;
425 }
426
427 // Schema must be built before use
428 if (!g_schema_built || !g_dynamic_schema) {
429 if (count) {
430 *count = 0;
431 }
432 SET_ERRNO(ERROR_INVALID_STATE, "Schema not built");
433 return NULL;
434 }
435
436 for (size_t i = 0; i < g_dynamic_schema_count && result_count < SCHEMA_CATEGORY_OPTION_CAPACITY; i++) {
437 if (g_dynamic_schema[i].category && strcmp(g_dynamic_schema[i].category, category) == 0) {
438 results[result_count++] = &g_dynamic_schema[i];
439 }
440 }
441
442 if (count) {
443 *count = result_count;
444 }
445
446 return (result_count > 0) ? results : NULL;
447}
#define SCHEMA_CATEGORY_OPTION_CAPACITY
Definition schema.c:13

References ERROR_INVALID_STATE, SCHEMA_CATEGORY_OPTION_CAPACITY, and SET_ERRNO.

Referenced by config_create_default().

◆ config_schema_get_by_toml_key()

const config_option_metadata_t * config_schema_get_by_toml_key ( const char * toml_key)

Get option metadata by TOML key.

Parameters
toml_keyTOML key path (e.g., "network.port")
Returns
Pointer to metadata or NULL if not found

Definition at line 395 of file schema.c.

395 {
396 if (!toml_key) {
397 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid arguments");
398 return NULL;
399 }
400
401 // Schema must be built before use
402 if (!g_schema_built || !g_dynamic_schema) {
403 SET_ERRNO(ERROR_INVALID_STATE, "Schema not built");
404 return NULL;
405 }
406
407 for (size_t i = 0; i < g_dynamic_schema_count; i++) {
408 if (g_dynamic_schema[i].toml_key && strcmp(g_dynamic_schema[i].toml_key, toml_key) == 0) {
409 return &g_dynamic_schema[i];
410 }
411 }
412
413 return NULL;
414}
@ ERROR_INVALID_PARAM

References ERROR_INVALID_PARAM, ERROR_INVALID_STATE, and SET_ERRNO.