Loading

Validation

Annotate parameters (or [AsParameters] members) with standard System.ComponentModel.DataAnnotations attributes, optionally combined with Nullean.Argh filesystem attributes. The source generator reads the attributes at build time and emits inline validation checks. No reflection, no Validator.ValidateObject call, AOT-safe.

Constraint hints appear in --help after the description. Failures print to stderr and exit with code 2.

public static void Deploy(
    [Range(1, 65535)]                          int port,
    [StringLength(64, MinimumLength = 2)]      string name,
    [AllowedValues("dev", "staging", "prod")]  string env,
    [RegularExpression(@"^[a-z0-9\-]+$")]      string slug,
    [UriScheme("https")]                       Uri endpoint)
{ … }
		
$ myapp deploy --port 99999
Error: --port: value must be between 1 and 65535.
Run 'myapp deploy --help' for usage.

$ myapp deploy --help
Options:
  --port <int>       [required] [range: 1-65535]
  --name <string>    [required] [length: 2-64]
  --env <string>     [required] [allowed: dev|staging|prod]
  --slug <string>    [required] [pattern: ^[a-z0-9\-]+$]
  --endpoint <uri>   [required] [schemes: https]
		

When [MinLength] / [MaxLength] / [Length] is applied to a collection parameter (T[], List<T>, etc.), it validates the number of items, not string length:

public static void Process([MaxLength(5)] List<string> files) { … }

public static void Archive([Argument][MinLength(2)][MaxLength(10)] string[] files) { … }
		
  1. max 5 --file flags
  2. 2-10 positional items
Note

Enum parameters automatically show [allowed: Member1|Member2] in help. The enum type itself enforces the constraint with no extra attribute needed.

These attributes apply to FileInfo / DirectoryInfo parameters (including on [AsParameters] members). Incompatible combinations (such as [Existing] with [NonExisting]) are diagnosed at compile time.

Attribute Applies to Help token
[Existing] FileInfo [existing]
[Existing] DirectoryInfo [existing]
[NonExisting] FileInfo or DirectoryInfo [unused path]
[RejectSymbolicLinks] FileInfo or DirectoryInfo [no symlinks]
[ExpandUserProfile] FileInfo or DirectoryInfo [expand ~ profile]
Important

[RejectSymbolicLinks] runs before existence checks. A symlink pointing to a real path still fails when symlink rejection is enabled.

public static Task<int> Lint(
    [Existing][FileExtensions(Extensions="json")][RejectSymbolicLinks] FileInfo manifest,
    [ExpandUserProfile][Existing] DirectoryInfo outDir)
{ … }
		

Failures produce stderr messages such as file does not exist or path must not be a symbolic link (exit code 2).

Validations include JSON kind values in __schema output: existing, nonExisting, rejectSymbolicLinks, expandUserProfile.

Validation also runs through the TryParseArgh static extension emitted for [AsParameters] DTOs, so unit tests can assert constraints without spawning a subprocess.