ascii-chat 0.11.33
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

struct  config_option_metadata_t
 Option metadata for config file parsing. More...
 

Typedefs

typedef bool(* builder_validate_fn_t) (const void *options_struct, char **error_msg)
 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, char **error_msg)

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)
error_msgOutput: error message (allocated by validator, caller frees)
Returns
true if valid, false if invalid

Definition at line 41 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 240 of file schema.c.

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

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, option_metadata_t::max, config_option_metadata_t::max, option_descriptor_t::metadata, option_metadata_t::min, config_option_metadata_t::min, option_descriptor_t::mode_bitmask, config_option_metadata_t::mode_bitmask, option_descriptor_t::mode_default_getter, config_option_metadata_t::mode_default_getter, options_config_t::num_descriptors, option_metadata_t::numeric_range, option_descriptor_t::offset, OPTION_CONTEXT_BOTH, option_descriptor_t::parse_fn, config_option_metadata_t::parse_fn, SAFE_FREE, SAFE_MALLOC, SAFE_STRNCPY, SET_ERRNO, config_option_metadata_t::toml_key, option_descriptor_t::type, config_option_metadata_t::type, option_descriptor_t::validate, and config_option_metadata_t::validate_fn.

Referenced by action_create_config(), config_create_default(), and options_init().

◆ 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 454 of file schema.c.

454 {
455 if (!g_schema_built) {
456 return; // Nothing to clean up
457 }
458
459 // Free all dynamically allocated strings (toml_key, cli_flag, category)
460 if (g_dynamic_strings) {
461 for (size_t i = 0; i < g_dynamic_strings_count; i++) {
462 SAFE_FREE(g_dynamic_strings[i]);
463 }
464 SAFE_FREE(g_dynamic_strings);
465 g_dynamic_strings = NULL;
466 g_dynamic_strings_count = 0;
467 g_dynamic_strings_capacity = 0;
468 }
469
470 // Free the schema array itself
471 if (g_dynamic_schema) {
472 SAFE_FREE(g_dynamic_schema);
473 g_dynamic_schema = NULL;
474 g_dynamic_schema_count = 0;
475 }
476
477 g_schema_built = false;
478}

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 434 of file schema.c.

434 {
435 // Schema must be built before use
436 if (!g_schema_built || !g_dynamic_schema) {
437 if (count) {
438 *count = 0;
439 }
440 SET_ERRNO(ERROR_INVALID_STATE, "Schema not built");
441 return NULL;
442 }
443
444 if (count) {
445 *count = g_dynamic_schema_count;
446 }
447 return g_dynamic_schema;
448}
@ 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 401 of file schema.c.

401 {
402 static const config_option_metadata_t *results[64]; // Max options per category
403 size_t result_count = 0;
404
405 if (!category) {
406 if (count) {
407 *count = 0;
408 }
409 return NULL;
410 }
411
412 // Schema must be built before use
413 if (!g_schema_built || !g_dynamic_schema) {
414 if (count) {
415 *count = 0;
416 }
417 SET_ERRNO(ERROR_INVALID_STATE, "Schema not built");
418 return NULL;
419 }
420
421 for (size_t i = 0; i < g_dynamic_schema_count && result_count < 64; i++) {
422 if (g_dynamic_schema[i].category && strcmp(g_dynamic_schema[i].category, category) == 0) {
423 results[result_count++] = &g_dynamic_schema[i];
424 }
425 }
426
427 if (count) {
428 *count = result_count;
429 }
430
431 return (result_count > 0) ? results : NULL;
432}

References ERROR_INVALID_STATE, 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 380 of file schema.c.

380 {
381 if (!toml_key) {
382 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid arguments for config_schema_get_by_toml_key");
383 return NULL;
384 }
385
386 // Schema must be built before use
387 if (!g_schema_built || !g_dynamic_schema) {
388 SET_ERRNO(ERROR_INVALID_STATE, "Schema not built");
389 return NULL;
390 }
391
392 for (size_t i = 0; i < g_dynamic_schema_count; i++) {
393 if (g_dynamic_schema[i].toml_key && strcmp(g_dynamic_schema[i].toml_key, toml_key) == 0) {
394 return &g_dynamic_schema[i];
395 }
396 }
397
398 return NULL;
399}
@ ERROR_INVALID_PARAM

References ERROR_INVALID_PARAM, ERROR_INVALID_STATE, and SET_ERRNO.