qubit_argument/lib.rs
1// =============================================================================
2// Copyright (c) 2025 - 2026 Haixing Hu.
3//
4// SPDX-License-Identifier: Apache-2.0
5//
6// Licensed under the Apache License, Version 2.0.
7// =============================================================================
8//! # Qubit Argument
9//!
10//! Ownership-preserving validation for arguments, configuration values,
11//! durations, indexes, and bounds.
12//!
13//! All public traits, functions, and error types are exported from the crate
14//! root. Validation returns [`ArgumentResult`] instead of panicking, and each
15//! failure contains an owned [`ArgumentPath`] plus an inspectable
16//! [`ArgumentErrorKind`]. Successful validation returns the original owned
17//! value or borrow without cloning it.
18//!
19//! Validation extension traits are sealed and implemented only for the types
20//! documented by this crate. [`ArgumentErrorKind`], [`ArgumentValue`], and
21//! [`LengthMetric`] are non-exhaustive; downstream matches must include a
22//! wildcard arm so the structured vocabulary can evolve compatibly.
23//!
24//! # Ownership-preserving validation
25//!
26//! ```rust
27//! use qubit_argument::{ArgumentResult, NumericArgument, StringArgument};
28//!
29//! fn validate_user(age: u8, name: String) -> ArgumentResult<(u8, String)> {
30//! let age = age.require_in_range("age", 0..=150)?;
31//! let name = name
32//! .require_non_blank("name")?
33//! .require_char_count_in("name", 3, 32)?;
34//! Ok((age, name))
35//! }
36//!
37//! let (age, name) = validate_user(42, String::from("Ada"))?;
38//! assert_eq!((age, name.as_str()), (42, "Ada"));
39//! # Ok::<(), qubit_argument::ArgumentError>(())
40//! ```
41//!
42//! # Downstream errors
43//!
44//! A downstream error can implement `From<ArgumentError>` and then use `?`
45//! without a `map_err` adapter:
46//!
47//! ```rust
48//! use qubit_argument::{ArgumentError, NumericArgument};
49//!
50//! #[derive(Debug)]
51//! enum DomainError {
52//! InvalidArgument(ArgumentError),
53//! }
54//!
55//! impl From<ArgumentError> for DomainError {
56//! fn from(error: ArgumentError) -> Self {
57//! Self::InvalidArgument(error)
58//! }
59//! }
60//!
61//! fn validate_pool_size(size: u32) -> Result<u32, DomainError> {
62//! let size = size.require_positive("pool_size")?;
63//! Ok(size)
64//! }
65//!
66//! assert_eq!(validate_pool_size(4)?, 4);
67//! # Ok::<(), DomainError>(())
68//! ```
69//!
70//! Callers that are checking a genuine internal invariant may instead choose
71//! to call `expect` with a meaningful explanation. The validation APIs do not
72//! make that recovery-versus-panic decision on the caller's behalf.
73//!
74//! # Nested configuration and durations
75//!
76//! Nested validators can report local paths and add parent context only on
77//! failure. Durations retain their unit in structured comparison errors:
78//!
79//! ```rust
80//! use std::time::Duration;
81//!
82//! use qubit_argument::{
83//! ArgumentResult,
84//! ArgumentResultExt,
85//! DurationArgument,
86//! };
87//!
88//! fn validate_timeouts(connect: Duration) -> ArgumentResult<()> {
89//! connect.require_positive("connect")?;
90//! Ok(())
91//! }
92//!
93//! let error = validate_timeouts(Duration::ZERO)
94//! .with_path_prefix("timeouts")
95//! .expect_err("a zero connection timeout is invalid");
96//! assert_eq!(error.path().as_str(), "timeouts.connect");
97//! ```
98//!
99//! # Strings and optional regex support
100//!
101//! Byte-length methods measure UTF-8 bytes. Character-count methods measure
102//! Unicode scalar values, not grapheme clusters. String validation errors do
103//! not retain the inspected input string. Structured length errors retain a
104//! [`LengthMetric`] so byte length, Unicode scalar count, and collection
105//! element count remain distinguishable.
106//!
107//! The default feature set is empty. Enabling the `regex` feature adds
108//! `StringArgument::require_match` and `StringArgument::require_not_match`.
109//! They use `Regex::is_match` semantics and do not implicitly anchor patterns.
110
111/// Argument validation implementations re-exported at the crate root.
112mod argument;
113
114pub use argument::{
115 ArgumentBound,
116 ArgumentError,
117 ArgumentErrorKind,
118 ArgumentPath,
119 ArgumentResult,
120 ArgumentResultExt,
121 ArgumentValue,
122 CollectionArgument,
123 ComparisonConstraint,
124 DurationArgument,
125 FloatArgument,
126 IndexRole,
127 LengthConstraint,
128 LengthMetric,
129 NumericArgument,
130 OptionArgument,
131 PatternExpectation,
132 RangeConstraint,
133 StringArgument,
134 check_bounds,
135 check_element_index,
136 check_position_index,
137 check_position_range,
138 require_that,
139};