ascii-chat 0.8.38
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
schema.c File Reference

Schema metadata registry for config file options. More...

Go to the source code of this file.

Functions

asciichat_error_t config_schema_build_from_configs (const options_config_t **configs, size_t num_configs)
 
const config_option_metadata_t * config_schema_get_by_toml_key (const char *toml_key)
 
const config_option_metadata_t ** config_schema_get_by_category (const char *category, size_t *count)
 
const config_option_metadata_t * config_schema_get_all (size_t *count)
 
void config_schema_destroy (void)
 

Detailed Description

Schema metadata registry for config file options.

Definition in file schema.c.

Function Documentation

◆ config_schema_build_from_configs()

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

Definition at line 242 of file schema.c.

242 {
243 // Free existing dynamic schema if any
244 if (g_dynamic_schema) {
245 SAFE_FREE(g_dynamic_schema);
246 g_dynamic_schema = NULL;
247 g_dynamic_schema_count = 0;
248 }
249
250 // Free existing string storage
251 if (g_dynamic_strings) {
252 for (size_t i = 0; i < g_dynamic_strings_count; i++) {
253 SAFE_FREE(g_dynamic_strings[i]);
254 }
255 SAFE_FREE(g_dynamic_strings);
256 g_dynamic_strings = NULL;
257 g_dynamic_strings_count = 0;
258 g_dynamic_strings_capacity = 0;
259 }
260
261 // Count total unique descriptors (by offset) across all configs
262 // Use a simple approach: collect all descriptors, then deduplicate by offset
263 const option_descriptor_t *all_descriptors[256] = {0}; // Max expected options
264 size_t descriptor_count = 0;
265
266 // Collect descriptors from all configs
267 for (size_t cfg_idx = 0; cfg_idx < num_configs; cfg_idx++) {
268 const options_config_t *config = configs[cfg_idx];
269 if (!config) {
270 continue;
271 }
272
273 for (size_t i = 0; i < config->num_descriptors && descriptor_count < 256; i++) {
274 const option_descriptor_t *desc = &config->descriptors[i];
275 if (should_add_descriptor(desc, all_descriptors, descriptor_count)) {
276 all_descriptors[descriptor_count++] = desc;
277 }
278 }
279 }
280
281 // Allocate schema array
282 g_dynamic_schema = SAFE_MALLOC(descriptor_count * sizeof(config_option_metadata_t), config_option_metadata_t *);
283 if (!g_dynamic_schema) {
284 return SET_ERRNO(ERROR_MEMORY, "Failed to allocate dynamic schema");
285 }
286
287 // Build schema entries
288 g_dynamic_schema_count = 0;
289 char toml_key_buffer[BUFFER_SIZE_SMALL];
290 char cli_flag_buffer[256];
291 char category_buffer[64];
292
293 for (size_t i = 0; i < descriptor_count; i++) {
294 const option_descriptor_t *desc = all_descriptors[i];
295 if (!desc || !desc->long_name || !desc->group) {
296 continue;
297 }
298
299 config_option_metadata_t *meta = &g_dynamic_schema[g_dynamic_schema_count++];
300 // Zero-initialize the metadata struct to ensure all fields are initialized
301 memset(meta, 0, sizeof(*meta));
302
303 // Use builder's type directly
304 meta->type = desc->type;
305
306 // Generate category: lowercase the group name from builder
307 SAFE_STRNCPY(category_buffer, desc->group, sizeof(category_buffer) - 1);
308 category_buffer[sizeof(category_buffer) - 1] = '\0';
309 str_tolower(category_buffer);
310
311 // Generate TOML key: "category.field_name" (category is lowercase group, field_name is long_name with
312 // dashes->underscores)
313 if (!generate_toml_key(category_buffer, desc->long_name, toml_key_buffer, sizeof(toml_key_buffer))) {
314 g_dynamic_schema_count--; // Skip this entry
315 continue;
316 }
317
318 // Store generated strings (allocate and store)
319 meta->toml_key = store_dynamic_string(toml_key_buffer);
320 meta->category = store_dynamic_string(category_buffer);
321 if (!meta->toml_key || !meta->category) {
322 // Failed to allocate strings, skip this entry
323 g_dynamic_schema_count--;
324 continue;
325 }
326
327 // Generate CLI flag: "--long-name"
328 if (generate_cli_flag(desc->long_name, cli_flag_buffer, sizeof(cli_flag_buffer))) {
329 meta->cli_flag = store_dynamic_string(cli_flag_buffer);
330 if (!meta->cli_flag) {
331 // Failed to allocate, but continue (cli_flag can be NULL)
332 }
333 } else {
334 meta->cli_flag = NULL;
335 }
336
337 // Set other fields
338 meta->context = OPTION_CONTEXT_BOTH; // Most options can appear in both CLI and config
339 meta->field_offset = desc->offset;
340 meta->field_size = get_field_size(meta->type, desc->offset);
341 // Use builder's validate function directly - it receives the full options struct
342 meta->validate_fn = desc->validate;
343 // Use builder's parse function for CALLBACK types
344 meta->parse_fn = desc->parse_fn;
345 meta->description = desc->help_text;
346
347 // Set mode_bitmask from option descriptor
348 // The descriptor should have mode_bitmask set from the registry
349 meta->mode_bitmask = desc->mode_bitmask;
350
351 // Copy mode_default_getter function pointer
352 meta->mode_default_getter = desc->mode_default_getter;
353
354 // Copy constraints from descriptor's metadata
355 // For integer types, copy numeric_range to int_range (always copy, even if min is 0)
356 memset(&meta->constraints, 0, sizeof(meta->constraints));
357 // Always copy numeric_range if descriptor has it (check max or min)
358 if (desc && desc->metadata.numeric_range.max > 0) {
359 meta->constraints.int_range.min = desc->metadata.numeric_range.min;
360 meta->constraints.int_range.max = desc->metadata.numeric_range.max;
361 }
362 }
363
364 g_schema_built = true;
365 return ASCIICHAT_OK;
366}

Referenced by action_create_config(), and options_init().

◆ config_schema_destroy()

void config_schema_destroy ( void  )

Definition at line 446 of file schema.c.

446 {
447 if (!g_schema_built) {
448 return; // Nothing to clean up
449 }
450
451 // Free all dynamically allocated strings (toml_key, cli_flag, category)
452 if (g_dynamic_strings) {
453 for (size_t i = 0; i < g_dynamic_strings_count; i++) {
454 SAFE_FREE(g_dynamic_strings[i]);
455 }
456 SAFE_FREE(g_dynamic_strings);
457 g_dynamic_strings = NULL;
458 g_dynamic_strings_count = 0;
459 g_dynamic_strings_capacity = 0;
460 }
461
462 // Free the schema array itself
463 if (g_dynamic_schema) {
464 SAFE_FREE(g_dynamic_schema);
465 g_dynamic_schema = NULL;
466 g_dynamic_schema_count = 0;
467 }
468
469 g_schema_built = false;
470}

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)

Definition at line 426 of file schema.c.

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 if (count) {
437 *count = g_dynamic_schema_count;
438 }
439 return g_dynamic_schema;
440}

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 
)

Definition at line 393 of file schema.c.

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

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)

Definition at line 372 of file schema.c.

372 {
373 if (!toml_key) {
374 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid arguments for config_schema_get_by_toml_key");
375 return NULL;
376 }
377
378 // Schema must be built before use
379 if (!g_schema_built || !g_dynamic_schema) {
380 SET_ERRNO(ERROR_INVALID_STATE, "Schema not built");
381 return NULL;
382 }
383
384 for (size_t i = 0; i < g_dynamic_schema_count; i++) {
385 if (g_dynamic_schema[i].toml_key && strcmp(g_dynamic_schema[i].toml_key, toml_key) == 0) {
386 return &g_dynamic_schema[i];
387 }
388 }
389
390 return NULL;
391}