ascii-chat 0.8.38
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
public_api.c
Go to the documentation of this file.
1
14#include <ascii-chat/options/registry/common.h>
15#include <ascii-chat/options/registry/core.h>
16#include <ascii-chat/options/registry.h>
17#include <ascii-chat/options/actions.h>
18#include <string.h>
19
20asciichat_error_t options_registry_add_all_to_builder(options_builder_t *builder) {
21 if (!builder) {
22 return SET_ERRNO(ERROR_INVALID_PARAM, "Builder is NULL");
23 }
24
26
27 for (size_t i = 0; i < g_registry_size; i++) {
28 const registry_entry_t *entry = &g_options_registry[i];
29 if (!entry->long_name) {
30 continue;
31 }
32 // Silent - no debug logging needed
33
34 switch (entry->type) {
35 case OPTION_TYPE_STRING:
36 options_builder_add_string(builder, entry->long_name, entry->short_name, entry->offset,
37 entry->default_value ? (const char *)entry->default_value : "", entry->help_text,
38 entry->group, entry->required, entry->env_var_name, entry->validate_fn);
39 break;
40 case OPTION_TYPE_INT:
41 // Use metadata-aware function if metadata is present
42 if (entry->metadata.numeric_range.max != 0 ||
43 (entry->metadata.enum_values && entry->metadata.enum_values[0] != NULL)) {
44 options_builder_add_int_with_metadata(builder, entry->long_name, entry->short_name, entry->offset,
45 entry->default_value ? *(const int *)entry->default_value : 0,
46 entry->help_text, entry->group, entry->required, entry->env_var_name,
47 entry->validate_fn, &entry->metadata);
48 } else {
49 options_builder_add_int(builder, entry->long_name, entry->short_name, entry->offset,
50 entry->default_value ? *(const int *)entry->default_value : 0, entry->help_text,
51 entry->group, entry->required, entry->env_var_name, entry->validate_fn);
52 }
53 break;
54 case OPTION_TYPE_BOOL:
55 options_builder_add_bool(builder, entry->long_name, entry->short_name, entry->offset,
56 entry->default_value ? *(const bool *)entry->default_value : false, entry->help_text,
57 entry->group, entry->required, entry->env_var_name);
58 break;
59 case OPTION_TYPE_DOUBLE:
60 // Use metadata-aware function if metadata is present (has numeric range)
61 if (entry->metadata.numeric_range.max != 0) {
62 options_builder_add_double_with_metadata(builder, entry->long_name, entry->short_name, entry->offset,
63 entry->default_value ? *(const double *)entry->default_value : 0.0,
64 entry->help_text, entry->group, entry->required, entry->env_var_name,
65 entry->validate_fn, &entry->metadata);
66 } else {
67 options_builder_add_double(builder, entry->long_name, entry->short_name, entry->offset,
68 entry->default_value ? *(const double *)entry->default_value : 0.0, entry->help_text,
69 entry->group, entry->required, entry->env_var_name, entry->validate_fn);
70 }
71 break;
72 case OPTION_TYPE_CALLBACK:
73 // Always use metadata-aware function to preserve enum/metadata information
74 options_builder_add_callback_with_metadata(builder, entry->long_name, entry->short_name, entry->offset,
75 entry->default_value, entry->default_value_size, entry->parse_fn,
76 entry->help_text, entry->group, entry->required, entry->env_var_name,
77 entry->optional_arg, &entry->metadata);
78 break;
79 case OPTION_TYPE_ACTION:
80 // Actions are now registered as options with help text
81 // Look up the corresponding action function based on option name
82 if (strcmp(entry->long_name, "list-webcams") == 0) {
83 options_builder_add_action(builder, entry->long_name, entry->short_name, action_list_webcams, entry->help_text,
84 entry->group);
85 } else if (strcmp(entry->long_name, "list-microphones") == 0) {
86 options_builder_add_action(builder, entry->long_name, entry->short_name, action_list_microphones,
87 entry->help_text, entry->group);
88 } else if (strcmp(entry->long_name, "list-speakers") == 0) {
89 options_builder_add_action(builder, entry->long_name, entry->short_name, action_list_speakers, entry->help_text,
90 entry->group);
91 } else if (strcmp(entry->long_name, "show-capabilities") == 0) {
92 options_builder_add_action(builder, entry->long_name, entry->short_name, action_show_capabilities,
93 entry->help_text, entry->group);
94 } else if (strcmp(entry->long_name, "check-update") == 0) {
95 options_builder_add_action(builder, entry->long_name, entry->short_name, action_check_update, entry->help_text,
96 entry->group);
97 } else if (strcmp(entry->long_name, "help") == 0 || strcmp(entry->long_name, "version") == 0) {
98 // Help and version are handled specially in options.c, just add them for help display
99 // They don't have actual action functions - pass a dummy one
100 options_builder_add_action(builder, entry->long_name, entry->short_name, NULL, entry->help_text, entry->group);
101 }
102 break;
103 }
104
105 // Set mode bitmask on the last added descriptor
106 options_builder_set_mode_bitmask(builder, entry->mode_bitmask);
107
108 // Set custom arg_placeholder if defined
109 if (entry->arg_placeholder) {
110 options_builder_set_arg_placeholder(builder, entry->arg_placeholder);
111 }
112 }
113
114 return ASCIICHAT_OK;
115}
116
125const registry_entry_t *options_registry_get_raw(void) {
127 return g_options_registry;
128}
129
137 return g_registry_size;
138}
139
140const option_descriptor_t *options_registry_find_by_name(const char *long_name) {
141 if (!long_name) {
142 SET_ERRNO(ERROR_INVALID_PARAM, "Long name is NULL");
143 return NULL;
144 }
145
147
148 const registry_entry_t *entry = registry_find_entry_by_name(long_name);
149 if (!entry) {
150 // Don't log error for binary-level options like "config" that aren't in registry
151 if (strcmp(long_name, "config") != 0) {
152 SET_ERRNO(ERROR_NOT_FOUND, "Option not found: %s", long_name);
153 }
154 return NULL;
155 }
156
157 /* Create descriptor from registry entry */
158 static option_descriptor_t desc;
159 desc.long_name = entry->long_name;
160 desc.short_name = entry->short_name;
161 desc.type = entry->type;
162 desc.offset = entry->offset;
163 desc.help_text = entry->help_text;
164 desc.group = entry->group;
165 desc.hide_from_mode_help = false;
166 desc.hide_from_binary_help = false;
167 desc.default_value = entry->default_value;
168 desc.required = entry->required;
169 desc.env_var_name = entry->env_var_name;
170 desc.validate = entry->validate_fn;
171 desc.parse_fn = entry->parse_fn;
172 desc.action_fn = NULL;
173 desc.owns_memory = entry->owns_memory;
174 desc.optional_arg = entry->optional_arg;
175 desc.mode_bitmask = entry->mode_bitmask;
176
177 return &desc;
178}
179
180const option_descriptor_t *options_registry_find_by_short(char short_name) {
181 if (short_name == '\0') {
182 SET_ERRNO(ERROR_INVALID_PARAM, "Short name is empty");
183 return NULL;
184 }
185
187
188 const registry_entry_t *entry = registry_find_entry_by_short(short_name);
189 if (!entry) {
190 SET_ERRNO(ERROR_NOT_FOUND, "Option not found: -%c", short_name);
191 return NULL;
192 }
193
194 /* Create descriptor from registry entry */
195 static option_descriptor_t desc;
196 desc.long_name = entry->long_name;
197 desc.short_name = entry->short_name;
198 desc.type = entry->type;
199 desc.offset = entry->offset;
200 desc.help_text = entry->help_text;
201 desc.group = entry->group;
202 desc.hide_from_mode_help = false;
203 desc.hide_from_binary_help = false;
204 desc.default_value = entry->default_value;
205 desc.required = entry->required;
206 desc.env_var_name = entry->env_var_name;
207 desc.validate = entry->validate_fn;
208 desc.parse_fn = entry->parse_fn;
209 desc.action_fn = NULL;
210 desc.owns_memory = entry->owns_memory;
211 desc.optional_arg = entry->optional_arg;
212 desc.mode_bitmask = entry->mode_bitmask;
213
214 return &desc;
215}
216
217const option_descriptor_t *options_registry_get_for_mode(asciichat_mode_t mode, size_t *num_options) {
218 if (!num_options) {
219 SET_ERRNO(ERROR_INVALID_PARAM, "Number of options is NULL");
220 return NULL;
221 }
222
224
225 /* Convert mode to bitmask */
226 option_mode_bitmask_t mode_bitmask = 0;
227 switch (mode) {
228 case MODE_SERVER:
229 mode_bitmask = OPTION_MODE_SERVER;
230 break;
231 case MODE_CLIENT:
232 mode_bitmask = OPTION_MODE_CLIENT;
233 break;
234 case MODE_MIRROR:
235 mode_bitmask = OPTION_MODE_MIRROR;
236 break;
237 case MODE_DISCOVERY_SERVICE:
238 mode_bitmask = OPTION_MODE_DISCOVERY_SVC;
239 break;
240 case MODE_DISCOVERY:
241 mode_bitmask = OPTION_MODE_DISCOVERY;
242 break;
243 default:
244 SET_ERRNO(ERROR_INVALID_PARAM, "Invalid mode: %d", mode);
245 *num_options = 0;
246 return NULL;
247 }
248
249 /* Count matching options */
250 size_t count = 0;
251 for (size_t i = 0; i < g_registry_size; i++) {
252 if (g_options_registry[i].mode_bitmask & mode_bitmask) {
253 count++;
254 }
255 }
256
257 if (count == 0) {
258 *num_options = 0;
259 return NULL;
260 }
261
262 /* Allocate array for matching options */
263 option_descriptor_t *filtered = SAFE_MALLOC(count * sizeof(option_descriptor_t), option_descriptor_t *);
264 if (!filtered) {
265 SET_ERRNO(ERROR_INVALID_STATE, "Failed to allocate filtered options array");
266 *num_options = 0;
267 return NULL;
268 }
269
270 /* Copy matching options */
271 size_t idx = 0;
272 for (size_t i = 0; i < g_registry_size; i++) {
273 if (g_options_registry[i].mode_bitmask & mode_bitmask) {
275 }
276 }
277
278 *num_options = count;
279 return filtered;
280}
281
282const option_descriptor_t *options_registry_get_binary_options(size_t *num_options) {
283 if (!num_options) {
284 SET_ERRNO(ERROR_INVALID_PARAM, "Number of options is NULL");
285 return NULL;
286 }
287
289
290 /* Count binary-level options */
291 size_t count = 0;
292 for (size_t i = 0; i < g_registry_size; i++) {
293 if (g_options_registry[i].mode_bitmask & OPTION_MODE_BINARY) {
294 count++;
295 }
296 }
297
298 if (count == 0) {
299 *num_options = 0;
300 return NULL;
301 }
302
303 /* Allocate array for binary options */
304 option_descriptor_t *binary_opts = SAFE_MALLOC(count * sizeof(option_descriptor_t), option_descriptor_t *);
305 if (!binary_opts) {
306 SET_ERRNO(ERROR_INVALID_STATE, "Failed to allocate binary options array");
307 *num_options = 0;
308 return NULL;
309 }
310
311 /* Copy binary options */
312 size_t idx = 0;
313 for (size_t i = 0; i < g_registry_size; i++) {
314 if (g_options_registry[i].mode_bitmask & OPTION_MODE_BINARY) {
315 binary_opts[idx++] = registry_entry_to_descriptor(&g_options_registry[i]);
316 }
317 }
318
319 *num_options = count;
320 return binary_opts;
321}
322
323const option_descriptor_t *options_registry_get_for_display(asciichat_mode_t mode, bool for_binary_help,
324 size_t *num_options) {
325 if (!num_options) {
326 SET_ERRNO(ERROR_INVALID_PARAM, "num_options is NULL");
327 return NULL;
328 }
329
331
332 // Count matching options
333 size_t count = 0;
334 for (size_t i = 0; i < g_registry_size; i++) {
335 if (registry_entry_applies_to_mode(&g_options_registry[i], mode, for_binary_help)) {
336 count++;
337 }
338 }
339
340 if (count == 0) {
341 *num_options = 0;
342 return NULL;
343 }
344
345 // Allocate array
346 option_descriptor_t *descriptors = SAFE_MALLOC(count * sizeof(option_descriptor_t), option_descriptor_t *);
347 if (!descriptors) {
348 SET_ERRNO(ERROR_MEMORY, "Failed to allocate descriptors array");
349 *num_options = 0;
350 return NULL;
351 }
352
353 // Copy matching options
354 size_t idx = 0;
355 for (size_t i = 0; i < g_registry_size; i++) {
356 if (registry_entry_applies_to_mode(&g_options_registry[i], mode, for_binary_help)) {
357 descriptors[idx++] = registry_entry_to_descriptor(&g_options_registry[i]);
358 }
359 }
360
361 *num_options = count;
362 return descriptors;
363}
364
365// ============================================================================
366// Completion Metadata Access (Phase 3 Implementation)
367// ============================================================================
368
369const option_metadata_t *options_registry_get_metadata(const char *long_name) {
370 if (!long_name) {
371 SET_ERRNO(ERROR_INVALID_PARAM, "Long name is NULL");
372 return NULL;
373 }
374
375 // Look up option in registry and return its metadata
377 for (size_t i = 0; i < g_registry_size; i++) {
378 const registry_entry_t *entry = &g_options_registry[i];
379 if (entry->long_name && strcmp(entry->long_name, long_name) == 0) {
380 // Return the metadata from the registry entry
381 return &entry->metadata;
382 }
383 }
384
385 // If not found, return empty metadata
386 static option_metadata_t empty_metadata = {0};
387 return &empty_metadata;
388}
389
390const char **options_registry_get_enum_values(const char *option_name, const char ***descriptions, size_t *count) {
391 if (!option_name || !count) {
392 SET_ERRNO(ERROR_INVALID_PARAM, "Option name is NULL or count is NULL");
393 if (count)
394 *count = 0;
395 return NULL;
396 }
397
398 const option_metadata_t *meta = options_registry_get_metadata(option_name);
399 if (!meta || meta->input_type != OPTION_INPUT_ENUM || !meta->enum_values || meta->enum_values[0] == NULL) {
400 SET_ERRNO(ERROR_NOT_FOUND, "Option '%s' not found", option_name);
401 *count = 0;
402 if (descriptions)
403 *descriptions = NULL;
404 return NULL;
405 }
406
407 // Count enum values (NULL-terminated array)
408 size_t enum_count = 0;
409 while (meta->enum_values[enum_count] != NULL) {
410 enum_count++;
411 }
412
413 *count = enum_count;
414 if (descriptions) {
415 *descriptions = meta->enum_descriptions;
416 }
417 return meta->enum_values;
418}
419
420bool options_registry_get_numeric_range(const char *option_name, int *min_out, int *max_out, int *step_out) {
421 if (!option_name || !min_out || !max_out || !step_out) {
422 SET_ERRNO(ERROR_INVALID_PARAM, "Option name is NULL or min_out, max_out, or step_out is NULL");
423 return false;
424 }
425
426 const option_metadata_t *meta = options_registry_get_metadata(option_name);
427 if (!meta || meta->input_type != OPTION_INPUT_NUMERIC) {
428 *min_out = 0;
429 *max_out = 0;
430 *step_out = 0;
431 return false;
432 }
433
434 *min_out = meta->numeric_range.min;
435 *max_out = meta->numeric_range.max;
436 *step_out = meta->numeric_range.step;
437 return true;
438}
439
440const char **options_registry_get_examples(const char *option_name, size_t *count) {
441 if (!option_name || !count) {
442 SET_ERRNO(ERROR_INVALID_PARAM, "Option name is NULL or count is NULL");
443 if (count)
444 *count = 0;
445 return NULL;
446 }
447
448 const option_metadata_t *meta = options_registry_get_metadata(option_name);
449 if (!meta || !meta->examples || meta->examples[0] == NULL) {
450 *count = 0;
451 return NULL;
452 }
453
454 // Count examples by finding NULL terminator
455 size_t example_count = 0;
456 for (size_t i = 0; meta->examples[i] != NULL; i++) {
457 example_count++;
458 }
459
460 *count = example_count;
461 return meta->examples;
462}
463
464option_input_type_t options_registry_get_input_type(const char *option_name) {
465 if (!option_name) {
466 SET_ERRNO(ERROR_INVALID_PARAM, "Option name is NULL");
467 return OPTION_INPUT_NONE;
468 }
469
470 const option_metadata_t *meta = options_registry_get_metadata(option_name);
471 if (!meta) {
472 SET_ERRNO(ERROR_NOT_FOUND, "Option '%s' not found", option_name);
473 return OPTION_INPUT_NONE;
474 }
475
476 return meta->input_type;
477}
void options_builder_add_double(options_builder_t *builder, const char *long_name, char short_name, size_t offset, double default_value, const char *help_text, const char *group, bool required, const char *env_var_name, bool(*validate)(const void *, char **))
Definition builder.c:757
void options_builder_set_arg_placeholder(options_builder_t *builder, const char *arg_placeholder)
Definition builder.c:968
void options_builder_add_action(options_builder_t *builder, const char *long_name, char short_name, void(*action_fn)(void), const char *help_text, const char *group)
Definition builder.c:919
void options_builder_set_mode_bitmask(options_builder_t *builder, option_mode_bitmask_t mode_bitmask)
Definition builder.c:960
void options_builder_add_bool(options_builder_t *builder, const char *long_name, char short_name, size_t offset, bool default_value, const char *help_text, const char *group, bool required, const char *env_var_name)
Definition builder.c:627
void options_builder_add_double_with_metadata(options_builder_t *builder, const char *long_name, char short_name, size_t offset, double default_value, const char *help_text, const char *group, bool required, const char *env_var_name, bool(*validate)(const void *, char **), const option_metadata_t *metadata)
Definition builder.c:790
void options_builder_add_int_with_metadata(options_builder_t *builder, const char *long_name, char short_name, size_t offset, int default_value, const char *help_text, const char *group, bool required, const char *env_var_name, bool(*validate)(const void *options_struct, char **error_msg), const option_metadata_t *metadata)
Definition builder.c:684
void options_builder_add_int(options_builder_t *builder, const char *long_name, char short_name, size_t offset, int default_value, const char *help_text, const char *group, bool required, const char *env_var_name, bool(*validate)(const void *, char **))
Definition builder.c:651
void options_builder_add_callback_with_metadata(options_builder_t *builder, const char *long_name, char short_name, size_t offset, const void *default_value, size_t value_size, bool(*parse_fn)(const char *, void *, char **), const char *help_text, const char *group, bool required, const char *env_var_name, bool optional_arg, const option_metadata_t *metadata)
Definition builder.c:892
void options_builder_add_string(options_builder_t *builder, const char *long_name, char short_name, size_t offset, const char *default_value, const char *help_text, const char *group, bool required, const char *env_var_name, bool(*validate)(const void *, char **))
Definition builder.c:720
option_descriptor_t registry_entry_to_descriptor(const registry_entry_t *entry)
Convert registry entry to option descriptor.
Definition core.c:161
const registry_entry_t * registry_find_entry_by_name(const char *long_name)
Get a registry entry by long name.
Definition core.c:127
bool registry_entry_applies_to_mode(const registry_entry_t *entry, asciichat_mode_t mode, bool for_binary_help)
Check if an option applies to the given mode for display purposes.
Definition core.c:200
const registry_entry_t * registry_find_entry_by_short(char short_name)
Get a registry entry by short name.
Definition core.c:145
void registry_init_size(void)
Initialize registry size and metadata.
Definition core.c:108
void action_check_update(void)
void action_show_capabilities(void)
void action_list_microphones(void)
void action_list_webcams(void)
void action_list_speakers(void)
const option_descriptor_t * options_registry_get_for_mode(asciichat_mode_t mode, size_t *num_options)
Definition public_api.c:217
const option_descriptor_t * options_registry_get_for_display(asciichat_mode_t mode, bool for_binary_help, size_t *num_options)
Definition public_api.c:323
const char ** options_registry_get_enum_values(const char *option_name, const char ***descriptions, size_t *count)
Definition public_api.c:390
const option_descriptor_t * options_registry_find_by_name(const char *long_name)
Definition public_api.c:140
bool options_registry_get_numeric_range(const char *option_name, int *min_out, int *max_out, int *step_out)
Definition public_api.c:420
const option_descriptor_t * options_registry_get_binary_options(size_t *num_options)
Definition public_api.c:282
asciichat_error_t options_registry_add_all_to_builder(options_builder_t *builder)
Definition public_api.c:20
const char ** options_registry_get_examples(const char *option_name, size_t *count)
Definition public_api.c:440
const registry_entry_t * options_registry_get_raw(void)
Get raw access to registry for completions filtering.
Definition public_api.c:125
const option_descriptor_t * options_registry_find_by_short(char short_name)
Definition public_api.c:180
size_t options_registry_get_count(void)
Get total number of registry entries.
Definition public_api.c:135
const option_metadata_t * options_registry_get_metadata(const char *long_name)
Definition public_api.c:369
option_input_type_t options_registry_get_input_type(const char *option_name)
Definition public_api.c:464
registry_entry_t g_options_registry[2048]
Definition registry.c:35
size_t g_registry_size
Definition registry.c:37