Skip to main content

Validation

Ensure your users provide correct input with Argus's comprehensive validation system, from simple built-in validators to powerful regex patterns.

Built-in Validators

Argus provides ready-to-use validators for common validation scenarios. Add them to your option definitions using the VALIDATOR() macro.

Range Validation

Validate that numeric values fall within acceptable bounds:

ARGUS_OPTIONS(
options,
HELP_OPTION(),

// Integer range validation
OPTION_INT('p', "port", HELP("Port number"),
VALIDATOR(V_RANGE(1, 65535)), DEFAULT(8080)),

// Float range validation
OPTION_FLOAT('q', "quality", HELP("Compression quality"),
VALIDATOR(V_RANGE(0, 1)), DEFAULT(0.8)),

// Negative ranges work too
OPTION_INT('t', "temperature", HELP("Temperature in Celsius"),
VALIDATOR(V_RANGE(-50, 100))),
)

Usage:

$ ./tool --port 8080        # ✅ Valid
$ ./tool --port 99999 # ❌ Error: Value 99999 is out of range [1, 65535]
$ ./tool --quality 0.9 # ✅ Valid
$ ./tool --quality 1.5 # ❌ Error: Value 1.5 is out of range [0, 1]

Length Validation

Validate string length for usernames, passwords, descriptions, etc:

ARGUS_OPTIONS(
options,
HELP_OPTION(),

// Username length validation
OPTION_STRING('u', "username", HELP("Username"),
VALIDATOR(V_LENGTH(3, 20))),

// Password minimum length
OPTION_STRING('p', "password", HELP("Password"),
VALIDATOR(V_LENGTH(8, 128))),

// Description with reasonable limits
OPTION_STRING('d', "description", HELP("Project description"),
VALIDATOR(V_LENGTH(10, 500))),
)

Usage:

$ ./tool --username "bob"          # ✅ Valid (3 chars)
$ ./tool --username "jo" # ❌ Error: Value 2 is out of length [3, 20]
$ ./tool --password "secret123" # ✅ Valid (9 chars)
$ ./tool --password "1234" # ❌ Error: Value 4 is out of length [8, 128]

Count Validation

Validate the number of elements in arrays and maps:

ARGUS_OPTIONS(
options,
HELP_OPTION(),

// Require at least one tag, max 5
OPTION_ARRAY_STRING('t', "tags", HELP("Resource tags"),
VALIDATOR(V_COUNT(1, 5))),

// Allow 1-10 port numbers
OPTION_ARRAY_INT('p', "ports", HELP("Port numbers"),
VALIDATOR(V_COUNT(1, 10))),

// Optional tags with upper limit
OPTION_ARRAY_STRING('l', "labels", HELP("Optional labels"),
VALIDATOR(V_COUNT(0, 3))),
)

Usage:

$ ./tool --tags=web,api,backend     # ✅ Valid (3 tags)
$ ./tool --tags=a,b,c,d,e,f # ❌ Error: Values count 6 is out of [1, 5]
$ ./tool # ❌ Error: Values count 0 is out of [1, 5] (for tags)

Choice Validation

Restrict values to a predefined set of valid options:

ARGUS_OPTIONS(
options,
HELP_OPTION(),

// Log level with predefined choices
OPTION_STRING('l', "level", HELP("Log level"),
VALIDATOR(V_CHOICE_STR("debug", "info", "warn", "error")),
DEFAULT("info")),

// Output format choices
OPTION_STRING('f', "format", HELP("Output format"),
VALIDATOR(V_CHOICE_STR("json", "xml", "yaml", "csv")),
DEFAULT("json")),

// Compression algorithm
OPTION_STRING('c', "compression", HELP("Compression type"),
VALIDATOR(V_CHOICE_STR("gzip", "bzip2", "lzma"))),
)

Usage:

$ ./tool --level info             # ✅ Valid
$ ./tool --level invalid # ❌ Error: Cannot be set to 'invalid'. Choose from ["debug", "info", "warn", "error"]
$ ./tool --format json # ✅ Valid
$ ./tool --format txt # ❌ Error: Invalid choice

Regex Validation

Use powerful regular expressions for pattern-based validation:

Predefined Patterns

Argus includes common regex patterns ready to use:

#include "argus/regex.h"

ARGUS_OPTIONS(
options,
HELP_OPTION(),

// Email validation
OPTION_STRING('e', "email", HELP("Email address"),
VALIDATOR(V_REGEX(ARGUS_RE_EMAIL))),

// IPv4 address validation
OPTION_STRING('i', "ip", HELP("Server IP address"),
VALIDATOR(V_REGEX(ARGUS_RE_IPV4))),

// URL validation
OPTION_STRING('u', "url", HELP("Website URL"),
VALIDATOR(V_REGEX(ARGUS_RE_URL))),

// ISO date format (YYYY-MM-DD)
OPTION_STRING('d', "date", HELP("Date in ISO format"),
VALIDATOR(V_REGEX(ARGUS_RE_ISO_DATE))),
)

Common predefined patterns:

  • ARGUS_RE_EMAIL - Email addresses
  • ARGUS_RE_IPV4 - IPv4 addresses
  • ARGUS_RE_IPV6 - IPv6 addresses
  • ARGUS_RE_URL - URLs (any protocol)
  • ARGUS_RE_ISO_DATE - ISO dates (YYYY-MM-DD)
  • ARGUS_RE_PHONE_US - US phone numbers
  • ARGUS_RE_UUID - UUID v4 format
Regex Dependency

Regex validation requires PCRE2. If compiled without regex support (-Dregex=false), regex validators will fail with an error message.

Combining Validators

Apply multiple validators to a single option. All validators are specified within a single VALIDATOR() call:

ARGUS_OPTIONS(
options,
HELP_OPTION(),

// Combine range and choices (value must be in range AND in choices)
OPTION_INT('p', "port", HELP("Common HTTP ports"),
VALIDATOR(V_RANGE(1, 65535), V_CHOICE_INT(80, 443, 8080, 8443))),

// Combine length and regex validation
OPTION_STRING('u', "username", HELP("Valid username"),
VALIDATOR(V_LENGTH(3, 20), V_REGEX(RE_USERNAME))),

// Count validation for arrays
OPTION_ARRAY_STRING('e', "emails", HELP("Email addresses"),
VALIDATOR(V_COUNT(1, 5))),
)

Important: Only one VALIDATOR() call per option. Include all validators as comma-separated arguments.

Validation Order and Custom Validators

Argus supports two types of validation timing:

  • ORDER_PRE: Validates raw string before type conversion
  • ORDER_POST: Validates converted value after type conversion

For detailed information on creating custom validators with specific ordering and complex validation logic, see the Custom Validators guide.

Best Practices

✅ Good Practices

// 1. Define regex patterns before use
#define RE_EMAIL_DOMAIN MAKE_REGEX("^[\\w.-]+@company\\.com$", "Must use company.com domain")

// 2. Use appropriate validators for data types
OPTION_INT('p', "port", HELP("Port number"),
VALIDATOR(V_RANGE(1, 65535)), DEFAULT(8080))

// 3. Combine validators logically
OPTION_STRING('u', "username", HELP("Username"),
VALIDATOR(V_LENGTH(3, 20), V_REGEX(RE_USERNAME)))

// 4. Provide sensible defaults for choices
OPTION_STRING('l', "level", HELP("Log level"),
VALIDATOR(V_CHOICE_STR("debug", "info", "warn", "error")),
DEFAULT("info"))

❌ Avoid These Patterns

// ❌ Don't use inline regex patterns - hard to read and reuse
OPTION_STRING('e', "email", HELP("Email"),
VALIDATOR(V_REGEX(MAKE_REGEX("^[\\w.-]+@[\\w.-]+$", "Email format"))))

// ❌ Don't over-validate simple inputs
OPTION_STRING('n', "name", HELP("Your name"),
VALIDATOR(V_LENGTH(1, 50), V_REGEX(MAKE_REGEX("^[A-Za-z ]+$", "Letters only"))))

// ❌ Don't use multiple VALIDATOR() calls
OPTION_INT('p', "port", HELP("Port"),
VALIDATOR(V_RANGE(1, 65535)),
VALIDATOR(V_CHOICE(80, 443, 8080))) // ❌ Wrong - multiple calls

Complete Example

#include "argus.h"
#include "argus/regex.h"

// Define regex patterns
#define RE_USERNAME MAKE_REGEX("^[a-zA-Z][a-zA-Z0-9_]{2,19}$", "Start with letter, 3-20 chars")

ARGUS_OPTIONS(
options,
HELP_OPTION(),
VERSION_OPTION(),

// Range validation
OPTION_INT('p', "port", HELP("Server port"),
VALIDATOR(V_RANGE(1, 65535)), DEFAULT(8080)),

// Length validation
OPTION_STRING('u', "username", HELP("Username"),
VALIDATOR(V_LENGTH(3, 20), V_REGEX(RE_USERNAME))),

// Choice validation
OPTION_STRING('l', "level", HELP("Log level"),
VALIDATOR(V_CHOICE_STR("debug", "info", "warn", "error"))),
DEFAULT("info")),

// Regex validation with predefined pattern
OPTION_STRING('e', "email", HELP("Email address"),
VALIDATOR(V_REGEX(ARGUS_RE_EMAIL))),

// Count validation for arrays
OPTION_ARRAY_STRING('t', "tags", HELP("Resource tags"),
VALIDATOR(V_COUNT(1, 5))),
)

int main(int argc, char **argv)
{
argus_t argus = argus_init(options, "validation_example", "1.0.0");
argus.description = "Validation example";

if (argus_parse(&argus, argc, argv) != ARGUS_SUCCESS)
return 1;

printf("✅ All validation passed!\n");

argus_free(&argus);
return 0;
}

What's Next?

Performance Note

Validation happens during parsing. For most applications, validation overhead is negligible, but complex regex patterns or custom validators can affect startup time.