Skip to content

Commit 55b99cd

Browse files
authored
Merge pull request #31 from lucocozz/develop
Refacto choices validator and struct validation error
2 parents e6be6e9 + 7c2886f commit 55b99cd

File tree

81 files changed

+1354
-1400
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

81 files changed

+1354
-1400
lines changed

.github/workflows/build-macos.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ name: Build macOS
33
on:
44
push:
55
branches: [ develop ]
6-
pull_request:
7-
branches: [ main, develop ]
6+
# pull_request:
7+
# branches: [ main, develop ]
88
workflow_dispatch:
99

1010
jobs:

.github/workflows/build-ubuntu.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ name: Build Ubuntu
33
on:
44
push:
55
branches: [ develop ]
6-
pull_request:
7-
branches: [ main, develop ]
6+
# pull_request:
7+
# branches: [ main, develop ]
88
workflow_dispatch:
99

1010
jobs:

.github/workflows/build-windows.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ name: Build Windows
33
on:
44
push:
55
branches: [ develop ]
6-
pull_request:
7-
branches: [ main, develop ]
6+
# pull_request:
7+
# branches: [ main, develop ]
88
workflow_dispatch:
99

1010
jobs:

README.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,8 +158,8 @@ ARGUS_OPTIONS(options,
158158
OPTION_INT('p', "port", HELP("Port number"),
159159
ENV_VAR("PORT"), VALIDATOR(V_RANGE(1, 65535)), DEFAULT(8080)),
160160
// Choice validation
161-
OPTION_STRING('l', "level", HELP("Log level"),
162-
CHOICES_STRING("debug", "info", "warn", "error"), DEFAULT("info"))
161+
OPTION_STRING('l', "level", HELP("Log level"), DEFAULT("info"),
162+
VALIDATOR(V_CHOICE_STR("debug", "info", "warn", "error")))
163163
)
164164

165165
// Usage: ./server --host 0.0.0.0 --port 8080 --level debug
@@ -192,6 +192,15 @@ ARGUS_OPTIONS(options,
192192
- **C11 compatible compiler** (GCC 13+, Clang 14+)
193193
- **Optional:** PCRE2 for regex validation (disable with `-Dregex=false`)
194194

195+
## Roadmap
196+
197+
- 📄 Config files - JSON/YAML configuration loading
198+
- 🪶 Lightweight version - Minimal footprint option for embedded systems
199+
- 🎨 Themed help - Customizable colored help output
200+
- 📁 Shell completion - Auto-generated tab completion for bash/zsh/fish
201+
- 🔗 Alias support - Command and option aliases for better UX
202+
- 📦 Plugin system - Extensibility mechanisms for custom handlers
203+
195204
## Contributing
196205

197206
Contributions welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.

argus.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,6 @@ EXPORTS
4242
length_validator
4343
count_validator
4444
regex_validator
45+
choices_string_validator
46+
choices_int_validator
47+
choices_float_validator

benchmarks/benchmark_release_mode.c

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,18 @@ ARGUS_OPTIONS(
2020

2121
GROUP_START("Output Options", GROUP_DESC("Options related to output")),
2222
OPTION_STRING('o', "output", HELP("Output file"), DEFAULT("output.txt")),
23-
OPTION_STRING('f', "format", HELP("Output format"), CHOICES_STRING("text", "json", "xml", "binary")),
24-
OPTION_FLAG('s', "silent", HELP("Suppress output"), CONFLICTS("verbose")),
23+
OPTION_STRING('f', "format", HELP("Output format"),
24+
VALIDATOR(V_CHOICE_STR("text", "json", "xml", "binary"))),
25+
OPTION_FLAG('s', "silent", HELP("Suppress output"), CONFLICT("verbose")),
2526
GROUP_END(),
2627

2728
GROUP_START("Processing Options", GROUP_DESC("Options controlling processing")),
28-
OPTION_INT('l', "level", HELP("Processing level"), RANGE(1, 10), DEFAULT(5)),
29-
OPTION_INT('j', "jobs", HELP("Number of parallel jobs"), RANGE(1, 100), DEFAULT(4)),
30-
OPTION_FLOAT('t', "threshold", HELP("Processing threshold"), DEFAULT(0.5)),
29+
OPTION_INT('l', "level", HELP("Processing level"),
30+
VALIDATOR(V_RANGE(1, 10)), DEFAULT(5)),
31+
OPTION_INT('j', "jobs", HELP("Number of parallel jobs"),
32+
VALIDATOR(V_RANGE(1, 100)), DEFAULT(4)),
33+
OPTION_FLOAT('t', "threshold", HELP("Processing threshold"),
34+
VALIDATOR(V_RANGE(0.0, 1.0)), DEFAULT(0.5)),
3135
OPTION_MAP_STRING('D', "define", HELP("Define variables"), FLAGS(FLAG_SORTED_KEY)),
3236
GROUP_END(),
3337

docs/docs/advanced/custom-handlers.md

Lines changed: 58 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ int handler_function(argus_t *argus, argus_option_t *option, char *arg);
1717
1818
**Return values:**
1919
- `ARGUS_SUCCESS` (0) - Parsing succeeded
20-
- Error code - Parsing failed (use `ARGUS_REPORT_ERROR`)
20+
- Error code - Parsing failed (use `ARGUS_PARSING_ERROR` and return error code)
2121
2222
## Basic Custom Handler
2323
@@ -40,7 +40,8 @@ int endpoint_handler(argus_t *argus, argus_option_t *option, char *arg)
4040
// Allocate structure
4141
endpoint_t *endpoint = calloc(1, sizeof(endpoint_t));
4242
if (!endpoint) {
43-
ARGUS_REPORT_ERROR(argus, ARGUS_ERROR_MEMORY, "Memory allocation failed");
43+
ARGUS_PARSING_ERROR(argus, "Memory allocation failed");
44+
return ARGUS_ERROR_MEMORY;
4445
}
4546
4647
// Store immediately for automatic cleanup
@@ -50,22 +51,23 @@ int endpoint_handler(argus_t *argus, argus_option_t *option, char *arg)
5051
// Find separator
5152
char *colon = strchr(arg, ':');
5253
if (!colon) {
53-
ARGUS_REPORT_ERROR(argus, ARGUS_ERROR_INVALID_FORMAT,
54-
"Invalid format '%s' (expected host:port)", arg);
54+
ARGUS_PARSING_ERROR(argus, "Invalid format '%s' (expected host:port)", arg);
55+
return ARGUS_ERROR_INVALID_FORMAT;
5556
}
5657
5758
// Extract host
5859
size_t host_len = colon - arg;
5960
endpoint->host = strndup(arg, host_len);
6061
if (!endpoint->host) {
61-
ARGUS_REPORT_ERROR(argus, ARGUS_ERROR_MEMORY, "Memory allocation failed");
62+
ARGUS_PARSING_ERROR(argus, "Memory allocation failed");
63+
return ARGUS_ERROR_MEMORY;
6264
}
6365
6466
// Extract and validate port
6567
long port = strtol(colon + 1, NULL, 10);
6668
if (port <= 0 || port > 65535) {
67-
ARGUS_REPORT_ERROR(argus, ARGUS_ERROR_INVALID_VALUE,
68-
"Invalid port %ld (must be 1-65535)", port);
69+
ARGUS_PARSING_ERROR(argus, "Invalid port %ld (must be 1-65535)", port);
70+
return ARGUS_ERROR_INVALID_VALUE;
6971
}
7072
7173
endpoint->port = (int)port;
@@ -193,7 +195,8 @@ int url_handler(argus_t *argus, argus_option_t *option, char *arg)
193195
{
194196
url_t *url = calloc(1, sizeof(url_t));
195197
if (!url) {
196-
ARGUS_REPORT_ERROR(argus, ARGUS_ERROR_MEMORY, "Memory allocation failed");
198+
ARGUS_PARSING_ERROR(argus, "Memory allocation failed");
199+
return ARGUS_ERROR_MEMORY;
197200
}
198201

199202
option->value.as_ptr = url;
@@ -267,7 +270,8 @@ int coordinate_handler(argus_t *argus, argus_option_t *option, char *arg)
267270
{
268271
coordinate_t *coord = calloc(1, sizeof(coordinate_t));
269272
if (!coord) {
270-
ARGUS_REPORT_ERROR(argus, ARGUS_ERROR_MEMORY, "Memory allocation failed");
273+
ARGUS_PARSING_ERROR(argus, "Memory allocation failed");
274+
return ARGUS_ERROR_MEMORY;
271275
}
272276
273277
option->value.as_ptr = coord;
@@ -277,38 +281,38 @@ int coordinate_handler(argus_t *argus, argus_option_t *option, char *arg)
277281
char *comma = strchr(arg, ',');
278282
if (!comma) {
279283
free(coord);
280-
ARGUS_REPORT_ERROR(argus, ARGUS_ERROR_INVALID_FORMAT,
281-
"Invalid format '%s' (expected lat,lon)", arg);
284+
ARGUS_PARSING_ERROR(argus, "Invalid format '%s' (expected lat,lon)", arg);
285+
return ARGUS_ERROR_INVALID_FORMAT;
282286
}
283287
284288
// Parse latitude
285289
char *endptr;
286290
coord->latitude = strtod(arg, &endptr);
287291
if (endptr != comma) {
288292
free(coord);
289-
ARGUS_REPORT_ERROR(argus, ARGUS_ERROR_INVALID_VALUE,
290-
"Invalid latitude value");
293+
ARGUS_PARSING_ERROR(argus, "Invalid latitude value");
294+
return ARGUS_ERROR_INVALID_VALUE;
291295
}
292296
293297
// Parse longitude
294298
coord->longitude = strtod(comma + 1, &endptr);
295299
if (*endptr != '\0') {
296300
free(coord);
297-
ARGUS_REPORT_ERROR(argus, ARGUS_ERROR_INVALID_VALUE,
298-
"Invalid longitude value");
301+
ARGUS_PARSING_ERROR(argus, "Invalid longitude value");
302+
return ARGUS_ERROR_INVALID_VALUE;
299303
}
300304
301305
// Validate ranges
302306
if (coord->latitude < -90.0 || coord->latitude > 90.0) {
303307
free(coord);
304-
ARGUS_REPORT_ERROR(argus, ARGUS_ERROR_INVALID_RANGE,
305-
"Latitude must be between -90 and 90");
308+
ARGUS_PARSING_ERROR(argus, "Latitude must be between -90 and 90");
309+
return ARGUS_ERROR_INVALID_RANGE;
306310
}
307311
308312
if (coord->longitude < -180.0 || coord->longitude > 180.0) {
309313
free(coord);
310-
ARGUS_REPORT_ERROR(argus, ARGUS_ERROR_INVALID_RANGE,
311-
"Longitude must be between -180 and 180");
314+
ARGUS_PARSING_ERROR(argus, "Longitude must be between -180 and 180");
315+
return ARGUS_ERROR_INVALID_RANGE;
312316
}
313317
314318
return ARGUS_SUCCESS;
@@ -327,7 +331,8 @@ int color_handler(argus_t *argus, argus_option_t *option, char *arg)
327331
{
328332
rgb_color_t *color = calloc(1, sizeof(rgb_color_t));
329333
if (!color) {
330-
ARGUS_REPORT_ERROR(argus, ARGUS_ERROR_MEMORY, "Memory allocation failed");
334+
ARGUS_PARSING_ERROR(argus, "Memory allocation failed");
335+
return ARGUS_ERROR_MEMORY;
331336
}
332337

333338
option->value.as_ptr = color;
@@ -342,46 +347,46 @@ int color_handler(argus_t *argus, argus_option_t *option, char *arg)
342347
unsigned int r, g, b;
343348
if (sscanf(arg, "%2x%2x%2x", &r, &g, &b) != 3) {
344349
free(color);
345-
ARGUS_REPORT_ERROR(argus, ARGUS_ERROR_INVALID_FORMAT,
346-
"Invalid hex color format");
350+
ARGUS_PARSING_ERROR(argus, "Invalid hex color format");
351+
return ARGUS_ERROR_INVALID_FORMAT;
347352
}
348353
color->r = r; color->g = g; color->b = b;
349354
} else if (strlen(arg) == 3) {
350355
// #RGB format (expand to #RRGGBB)
351356
unsigned int r, g, b;
352357
if (sscanf(arg, "%1x%1x%1x", &r, &g, &b) != 3) {
353358
free(color);
354-
ARGUS_REPORT_ERROR(argus, ARGUS_ERROR_INVALID_FORMAT,
355-
"Invalid hex color format");
359+
ARGUS_PARSING_ERROR(argus, "Invalid hex color format");
360+
return ARGUS_ERROR_INVALID_FORMAT;
356361
}
357362
color->r = r * 17; color->g = g * 17; color->b = b * 17;
358363
} else {
359364
free(color);
360-
ARGUS_REPORT_ERROR(argus, ARGUS_ERROR_INVALID_FORMAT,
361-
"Hex color must be #RGB or #RRGGBB");
365+
ARGUS_PARSING_ERROR(argus, "Hex color must be #RGB or #RRGGBB");
366+
return ARGUS_ERROR_INVALID_FORMAT;
362367
}
363368
}
364369
// Handle rgb(r,g,b) format
365370
else if (strncmp(arg, "rgb(", 4) == 0) {
366371
int r, g, b;
367372
if (sscanf(arg, "rgb(%d,%d,%d)", &r, &g, &b) != 3) {
368373
free(color);
369-
ARGUS_REPORT_ERROR(argus, ARGUS_ERROR_INVALID_FORMAT,
370-
"Invalid rgb() format (expected rgb(r,g,b))");
374+
ARGUS_PARSING_ERROR(argus, "Invalid rgb() format (expected rgb(r,g,b))");
375+
return ARGUS_ERROR_INVALID_FORMAT;
371376
}
372377
373378
if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255) {
374379
free(color);
375-
ARGUS_REPORT_ERROR(argus, ARGUS_ERROR_INVALID_RANGE,
376-
"RGB values must be 0-255");
380+
ARGUS_PARSING_ERROR(argus, "RGB values must be 0-255");
381+
return ARGUS_ERROR_INVALID_RANGE;
377382
}
378383
379384
color->r = r; color->g = g; color->b = b;
380385
}
381386
else {
382387
free(color);
383-
ARGUS_REPORT_ERROR(argus, ARGUS_ERROR_INVALID_FORMAT,
384-
"Unsupported color format (use #RRGGBB or rgb(r,g,b))");
388+
ARGUS_PARSING_ERROR(argus, "Unsupported color format (use #RRGGBB or rgb(r,g,b))");
389+
return ARGUS_ERROR_INVALID_FORMAT;
385390
}
386391

387392
return ARGUS_SUCCESS;
@@ -403,8 +408,8 @@ int url_format_validator(argus_t *argus, void *value_ptr, validator_data_t data)
403408
404409
// Basic format check before parsing
405410
if (!strstr(url, "://") && !strchr(url, '/')) {
406-
ARGUS_REPORT_ERROR(argus, ARGUS_ERROR_INVALID_FORMAT,
407-
"URL must contain protocol or path");
411+
ARGUS_PARSING_ERROR(argus, "URL must contain protocol or path");
412+
return ARGUS_ERROR_INVALID_FORMAT;
408413
}
409414
410415
return ARGUS_SUCCESS;
@@ -435,7 +440,8 @@ int safe_handler(argus_t *argus, argus_option_t *option, char *arg)
435440
// 1. Allocate main structure
436441
my_type_t *obj = calloc(1, sizeof(my_type_t));
437442
if (!obj) {
438-
ARGUS_REPORT_ERROR(argus, ARGUS_ERROR_MEMORY, "Memory allocation failed");
443+
ARGUS_PARSING_ERROR(argus, "Memory allocation failed");
444+
return ARGUS_ERROR_MEMORY;
439445
}
440446

441447
// 2. Store immediately for automatic cleanup
@@ -444,11 +450,13 @@ int safe_handler(argus_t *argus, argus_option_t *option, char *arg)
444450

445451
// 3. Parse and validate - can exit immediately on error
446452
if (parse_step_1(obj, arg) != 0) {
447-
ARGUS_REPORT_ERROR(argus, ARGUS_ERROR_INVALID_FORMAT, "Invalid format");
453+
ARGUS_PARSING_ERROR(argus, "Invalid format");
454+
return ARGUS_ERROR_INVALID_FORMAT;
448455
}
449456

450457
if (parse_step_2(obj, arg) != 0) {
451-
ARGUS_REPORT_ERROR(argus, ARGUS_ERROR_INVALID_VALUE, "Invalid value");
458+
ARGUS_PARSING_ERROR(argus, "Invalid value");
459+
return ARGUS_ERROR_INVALID_VALUE;
452460
}
453461

454462
return ARGUS_SUCCESS;
@@ -459,22 +467,20 @@ int safe_handler(argus_t *argus, argus_option_t *option, char *arg)
459467
- Validate input early
460468
- Set `option->value.as_ptr` and `option->is_allocated = true` early
461469
- Use specific error codes
462-
- `ARGUS_REPORT_ERROR` handles cleanup and returns
470+
- `ARGUS_PARSING_ERROR` reports the error, then return the error code
463471
464472
</TabItem>
465473
<TabItem value="error-messages" label="Clear Error Messages">
466474
467475
```c
468476
// ✅ Good error messages
469-
ARGUS_REPORT_ERROR(argus, ARGUS_ERROR_INVALID_FORMAT,
470-
"Invalid endpoint format '%s' (expected host:port)", arg);
477+
ARGUS_PARSING_ERROR(argus, "Invalid endpoint format '%s' (expected host:port)", arg);
471478
472-
ARGUS_REPORT_ERROR(argus, ARGUS_ERROR_INVALID_RANGE,
473-
"Port %ld out of range (must be 1-65535)", port);
479+
ARGUS_PARSING_ERROR(argus, "Port %ld out of range (must be 1-65535)", port);
474480
475481
// ❌ Poor error messages
476-
ARGUS_REPORT_ERROR(argus, ARGUS_ERROR_INVALID_VALUE, "Bad input");
477-
ARGUS_REPORT_ERROR(argus, ARGUS_ERROR_INVALID_FORMAT, "Parse error");
482+
ARGUS_PARSING_ERROR(argus, "Bad input");
483+
ARGUS_PARSING_ERROR(argus, "Parse error");
478484
```
479485

480486
**Guidelines:**
@@ -497,7 +503,8 @@ int simple_handler(argus_t *argus, argus_option_t *option, char *arg)
497503
{
498504
my_type_t *obj = malloc(sizeof(my_type_t));
499505
if (!obj) {
500-
ARGUS_REPORT_ERROR(argus, ARGUS_ERROR_MEMORY, "Allocation failed");
506+
ARGUS_PARSING_ERROR(argus, "Allocation failed");
507+
return ARGUS_ERROR_MEMORY;
501508
}
502509

503510
// Store immediately for automatic cleanup
@@ -514,7 +521,8 @@ int complex_handler(argus_t *argus, argus_option_t *option, char *arg)
514521
{
515522
complex_type_t *obj = calloc(1, sizeof(complex_type_t));
516523
if (!obj) {
517-
ARGUS_REPORT_ERROR(argus, ARGUS_ERROR_MEMORY, "Allocation failed");
524+
ARGUS_PARSING_ERROR(argus, "Allocation failed");
525+
return ARGUS_ERROR_MEMORY;
518526
}
519527

520528
// Store immediately for automatic cleanup
@@ -524,12 +532,14 @@ int complex_handler(argus_t *argus, argus_option_t *option, char *arg)
524532
// Additional allocations
525533
obj->data = malloc(data_size);
526534
if (!obj->data) {
527-
ARGUS_REPORT_ERROR(argus, ARGUS_ERROR_MEMORY, "Data allocation failed");
535+
ARGUS_PARSING_ERROR(argus, "Data allocation failed");
536+
return ARGUS_ERROR_MEMORY;
528537
}
529538

530539
obj->name = strdup(parsed_name);
531540
if (!obj->name) {
532-
ARGUS_REPORT_ERROR(argus, ARGUS_ERROR_MEMORY, "Name allocation failed");
541+
ARGUS_PARSING_ERROR(argus, "Name allocation failed");
542+
return ARGUS_ERROR_MEMORY;
533543
}
534544

535545
return ARGUS_SUCCESS;

0 commit comments

Comments
 (0)