Skip to content

Commit 4d47fcb

Browse files
authored
Merge pull request #34 from lucocozz/develop
Merge develop > main
2 parents 55b99cd + a5431d0 commit 4d47fcb

Some content is hidden

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

48 files changed

+2007
-964
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: [ 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: [ 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: [ develop ]
88
workflow_dispatch:
99

1010
jobs:

.github/workflows/tests.yml

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

1010
jobs:

argus.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,8 @@ EXPORTS
4545
choices_string_validator
4646
choices_int_validator
4747
choices_float_validator
48+
format_range_validator
49+
format_length_validator
50+
format_count_validator
51+
format_regex_validator
52+
format_choices_validator

docs/bun.lock

Lines changed: 125 additions & 69 deletions
Large diffs are not rendered by default.

docs/docs/advanced/custom-validators.md

Lines changed: 190 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ int even_validator(argus_t *argus, void *option_ptr, validator_data_t data)
3737

3838
// Helper macro
3939
#define V_EVEN() \
40-
MAKE_VALIDATOR(even_validator, _V_DATA_CUSTOM_(NULL), ORDER_POST)
40+
MAKE_VALIDATOR(even_validator, NULL, _V_DATA_CUSTOM_(NULL), ORDER_POST)
4141

4242
// Usage
4343
OPTION_INT('n', "number", HELP("Even number"), VALIDATOR(V_EVEN()))
@@ -72,7 +72,7 @@ int email_format_validator(argus_t *argus, void *value_ptr, validator_data_t dat
7272
}
7373
7474
#define V_EMAIL_FORMAT() \
75-
MAKE_VALIDATOR(email_format_validator, _V_DATA_CUSTOM_(NULL), ORDER_PRE)
75+
MAKE_VALIDATOR(email_format_validator, NULL, _V_DATA_CUSTOM_(NULL), ORDER_PRE)
7676
7777
// Usage
7878
OPTION_STRING('e', "email", HELP("Email address"), VALIDATOR(V_EMAIL_FORMAT()))
@@ -103,7 +103,7 @@ int divisible_validator(argus_t *argus, void *option_ptr, validator_data_t data)
103103
}
104104

105105
#define V_DIVISIBLE_BY(n) \
106-
MAKE_VALIDATOR(divisible_validator, _V_DATA_CUSTOM_(n), ORDER_POST)
106+
MAKE_VALIDATOR(divisible_validator, NULL, _V_DATA_CUSTOM_(n), ORDER_POST)
107107

108108
// Usage
109109
OPTION_INT('p', "port", HELP("Port (multiple of 100)"),
@@ -141,7 +141,7 @@ int advanced_range_validator(argus_t *argus, void *option_ptr, validator_data_t
141141
}
142142
143143
#define V_ADVANCED_RANGE(min, max, allow_zero) \
144-
MAKE_VALIDATOR(advanced_range_validator, \
144+
MAKE_VALIDATOR(advanced_range_validator, NULL, \
145145
_V_DATA_CUSTOM_(&((range_config_t){min, max, allow_zero})), \
146146
ORDER_POST)
147147
@@ -176,7 +176,7 @@ int max_greater_than_min_validator(argus_t *argus, void *option_ptr, validator_d
176176
}
177177

178178
#define V_GREATER_THAN(option_name) \
179-
MAKE_VALIDATOR(max_greater_than_min_validator, _V_DATA_CUSTOM_(option_name), ORDER_POST)
179+
MAKE_VALIDATOR(max_greater_than_min_validator, NULL, _V_DATA_CUSTOM_(option_name), ORDER_POST)
180180

181181
ARGUS_OPTIONS(
182182
options,
@@ -214,7 +214,7 @@ int company_email_validator(argus_t *argus, void *option_ptr, validator_data_t d
214214
}
215215

216216
#define V_COMPANY_EMAIL(domain) \
217-
MAKE_VALIDATOR(company_email_validator, _V_DATA_CUSTOM_(domain), ORDER_POST)
217+
MAKE_VALIDATOR(company_email_validator, NULL, _V_DATA_CUSTOM_(domain), ORDER_POST)
218218

219219
// Usage
220220
OPTION_STRING('e', "email", HELP("Company email"),
@@ -263,11 +263,11 @@ int case_validator(argus_t *argus, void *value_ptr, validator_data_t data)
263263
}
264264
265265
#define V_LOWERCASE() \
266-
MAKE_VALIDATOR(case_validator, _V_DATA_CUSTOM_(&(case_requirement_t){LOWERCASE}), ORDER_PRE)
266+
MAKE_VALIDATOR(case_validator, NULL, _V_DATA_CUSTOM_(&(case_requirement_t){LOWERCASE}), ORDER_PRE)
267267
#define V_UPPERCASE() \
268-
MAKE_VALIDATOR(case_validator, _V_DATA_CUSTOM_(&(case_requirement_t){UPPERCASE}), ORDER_PRE)
268+
MAKE_VALIDATOR(case_validator, NULL, _V_DATA_CUSTOM_(&(case_requirement_t){UPPERCASE}), ORDER_PRE)
269269
#define V_MIXED_CASE() \
270-
MAKE_VALIDATOR(case_validator, _V_DATA_CUSTOM_(&(case_requirement_t){MIXED}), ORDER_PRE)
270+
MAKE_VALIDATOR(case_validator, NULL, _V_DATA_CUSTOM_(&(case_requirement_t){MIXED}), ORDER_PRE)
271271
```
272272

273273
</TabItem>
@@ -299,9 +299,9 @@ int file_exists_validator(argus_t *argus, void *option_ptr, validator_data_t dat
299299
}
300300

301301
#define V_FILE_EXISTS() \
302-
MAKE_VALIDATOR(file_exists_validator, _V_DATA_CUSTOM_(true), ORDER_POST)
302+
MAKE_VALIDATOR(file_exists_validator, NULL, _V_DATA_CUSTOM_(true), ORDER_POST)
303303
#define V_FILE_NOT_EXISTS() \
304-
MAKE_VALIDATOR(file_exists_validator, _V_DATA_CUSTOM_(false), ORDER_POST)
304+
MAKE_VALIDATOR(file_exists_validator, NULL, _V_DATA_CUSTOM_(false), ORDER_POST)
305305

306306
// Usage
307307
OPTION_STRING('i', "input", HELP("Input file"), VALIDATOR(V_FILE_EXISTS()))
@@ -335,13 +335,190 @@ int array_all_positive_validator(argus_t *argus, void *option_ptr, validator_dat
335335
}
336336
337337
#define V_ALL_POSITIVE() \
338-
MAKE_VALIDATOR(array_all_positive_validator, _V_DATA_CUSTOM_(NULL), ORDER_POST)
338+
MAKE_VALIDATOR(array_all_positive_validator, NULL, _V_DATA_CUSTOM_(NULL), ORDER_POST)
339339
340340
// Usage
341341
OPTION_ARRAY_INT('p', "ports", HELP("Port numbers"),
342342
VALIDATOR(V_COUNT(1, 10), V_ALL_POSITIVE()))
343343
```
344344

345+
## Custom Formatters
346+
347+
Add custom formatting to display validator constraints in help output:
348+
349+
<Tabs>
350+
<TabItem value="basic-formatter" label="Basic Formatter" default>
351+
352+
```c
353+
// Divisibility validator with formatter
354+
int divisible_validator(argus_t *argus, void *option_ptr, validator_data_t data)
355+
{
356+
argus_option_t *option = (argus_option_t *)option_ptr;
357+
int divisor = (int)data.custom;
358+
359+
if (option->value.as_int % divisor != 0) {
360+
ARGUS_PARSING_ERROR(argus, "Value must be divisible by %d", divisor);
361+
return ARGUS_ERROR_INVALID_VALUE;
362+
}
363+
return ARGUS_SUCCESS;
364+
}
365+
366+
// Formatter function
367+
char *format_divisible_validator(validator_data_t data)
368+
{
369+
char *result = malloc(64);
370+
if (!result)
371+
return NULL;
372+
373+
snprintf(result, 64, "divisible by %d", (int)data.custom);
374+
return result;
375+
}
376+
377+
#define V_DIVISIBLE_BY(n) \
378+
MAKE_VALIDATOR(divisible_validator, format_divisible_validator, _V_DATA_CUSTOM_(n), ORDER_POST)
379+
380+
// Usage
381+
OPTION_INT('p', "port", HELP("Port number"), VALIDATOR(V_DIVISIBLE_BY(100)))
382+
```
383+
384+
**Generated help:**
385+
```bash
386+
-p, --port <NUM> - Port number (divisible by 100)
387+
```
388+
389+
</TabItem>
390+
<TabItem value="simple-formatter" label="Simple Range">
391+
392+
```c
393+
// Simple range validator for percentages
394+
char *format_percentage_validator(validator_data_t data)
395+
{
396+
char *result = malloc(16);
397+
if (!result)
398+
return NULL;
399+
400+
snprintf(result, 16, "0-100");
401+
return result;
402+
}
403+
404+
#define V_PERCENTAGE() \
405+
MAKE_VALIDATOR(percentage_validator, format_percentage_validator, \
406+
_V_DATA_CUSTOM_(NULL), ORDER_POST)
407+
408+
// Usage
409+
OPTION_INT('q', "quality", HELP("Compression quality"),
410+
VALIDATOR(V_PERCENTAGE()))
411+
```
412+
413+
**Generated help:**
414+
```bash
415+
-q, --quality <0-100> - Compression quality
416+
```
417+
418+
</TabItem>
419+
<TabItem value="domain-formatter" label="Domain Validator">
420+
421+
```c
422+
// Company email validator with formatter
423+
char *format_domain_validator(validator_data_t data)
424+
{
425+
const char *domain = (const char *)data.custom;
426+
char *result = malloc(128);
427+
if (!result)
428+
return NULL;
429+
430+
snprintf(result, 128, "company domain %s", domain);
431+
return result;
432+
}
433+
434+
#define V_DOMAIN_EMAIL(domain) \
435+
MAKE_VALIDATOR(company_email_validator, format_domain_validator, \
436+
_V_DATA_CUSTOM_(domain), ORDER_POST)
437+
438+
// Usage
439+
OPTION_STRING('e', "email", HELP("Company email"),
440+
VALIDATOR(V_DOMAIN_EMAIL("example.com")))
441+
```
442+
443+
**Generated help:**
444+
```bash
445+
-e, --email <STR> - Company email (company domain example.com)
446+
```
447+
448+
</TabItem>
449+
<TabItem value="ip-formatter" label="IP Validator">
450+
451+
```c
452+
typedef struct {
453+
bool allow_ipv6;
454+
bool allow_private;
455+
} ip_config_t;
456+
457+
char *format_ip_validator(validator_data_t data)
458+
{
459+
ip_config_t *config = (ip_config_t *)data.custom;
460+
char *result = malloc(128);
461+
if (!result)
462+
return NULL;
463+
464+
if (config->allow_ipv6 && config->allow_private) {
465+
safe_strcpy(result, 128, "IPv4 or IPv6 address");
466+
} else if (config->allow_ipv6) {
467+
safe_strcpy(result, 128, "IPv4 or IPv6, no private addresses");
468+
} else if (config->allow_private) {
469+
safe_strcpy(result, 128, "IPv4 address");
470+
} else {
471+
safe_strcpy(result, 128, "IPv4, no private addresses");
472+
}
473+
474+
return result;
475+
}
476+
477+
#define V_IP_ADDRESS(ipv6, private) \
478+
MAKE_VALIDATOR(ip_validator, format_ip_validator, \
479+
_V_DATA_CUSTOM_(&((ip_config_t){ipv6, private})), \
480+
ORDER_POST)
481+
482+
// Usage
483+
OPTION_STRING('i', "ip", HELP("Server IP"),
484+
VALIDATOR(V_IP_ADDRESS(false, false)))
485+
```
486+
487+
**Generated help:**
488+
```bash
489+
-i, --ip <STR> - Server IP (IPv4, no private addresses)
490+
```
491+
492+
</TabItem>
493+
</Tabs>
494+
495+
### Formatter Implementation
496+
497+
```c
498+
// Template for formatter functions
499+
char *format_my_validator(validator_data_t data)
500+
{
501+
// Allocate memory for result
502+
char *result = malloc(BUFFER_SIZE);
503+
if (!result)
504+
return NULL;
505+
506+
// Format based on your data
507+
snprintf(result, BUFFER_SIZE, "your descriptive format");
508+
509+
return result; // Memory will be freed by caller
510+
}
511+
```
512+
513+
**Integration with `MAKE_VALIDATOR`:**
514+
```c
515+
#define MY_VALIDATOR(param) \
516+
MAKE_VALIDATOR(my_validator_func, format_my_validator, \
517+
_V_DATA_CUSTOM_(param), ORDER_POST)
518+
```
519+
520+
The formatter automatically integrates with the help system and appears in option descriptions or hints depending on the output format.
521+
345522
## Combining Validators
346523

347524
Multiple validators execute in order:
@@ -382,7 +559,7 @@ int descriptive_validator(argus_t *argus, void *option_ptr, validator_data_t dat
382559

383560
// ✅ Reusable with parameters
384561
#define V_MIN_WORDS(count) \
385-
MAKE_VALIDATOR(min_words_validator, _V_DATA_CUSTOM_(count), ORDER_POST)
562+
MAKE_VALIDATOR(min_words_validator, NULL, _V_DATA_CUSTOM_(count), ORDER_POST)
386563

387564
// ❌ Avoid these patterns
388565
int bad_validator(argus_t *argus, void *option_ptr, validator_data_t data)

docs/docs/examples/configuration-tool.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -258,15 +258,17 @@ Arguments:
258258
Options:
259259
-h, --help - Display this help message (exit)
260260
-V, --version - Display version information (exit)
261-
-t, --tags <STR,...> - Resource tags [1-10 items] (sorted, unique)
262-
-p, --ports <NUM,...> - Port numbers (supports ranges) [1-5 items]
261+
-t, --tags <STR,...> - Resource tags (count: 1-10) (sorted, unique)
262+
-p, --ports <NUM,...> - Port numbers (supports ranges) (count: 1-5)
263263
-e, --env <KEY=VAL,...>
264-
- Environment variables (env: CONFIG_CONFIG_ENV) (sorted by key)
264+
- Environment variables (env: CONFIG_CONFIG_ENV)
265+
(sorted by key)
265266
-f, --features <KEY=BOOL,...>
266267
- Feature toggles (sorted by key)
267268
-n, --name <STR> - Service name (env: CONFIG_SERVICE_NAME) (required)
268-
-h, --host <STR> - Host address (env: CONFIG_HOST) (default: "localhost")
269-
-P, --port <NUM> - Main port (env: CONFIG_PORT) [1-65535] (default: 8080)
269+
-h, --host <STR> - Host address (env: CONFIG_HOST) (default:
270+
"localhost")
271+
-P, --port <1-65535> - Main port (env: CONFIG_PORT) (default: 8080)
270272
-c, --contact <STR> - Contact email
271273
```
272274

docs/docs/examples/simple-cli.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,9 @@ Options:
133133
-V, --version - Display version information (exit)
134134
-v, --verbose - Enable verbose output
135135
-o, --output <STR> - Output file (default: "result.txt")
136-
-c, --count <NUM> - Processing iterations [1-100] (default: 1)
137-
-f, --format <STR> - Output format [text|json|xml] (default: "text")
136+
-c, --count <1-100> - Processing iterations (default: 1)
137+
-f, --format <text|json|xml>
138+
- Output format (default: "text")
138139
```
139140

140141
## Key Features

0 commit comments

Comments
 (0)