Skip to main content

qubit_argument/argument/
option_argument.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//! Ownership-preserving validation for optional arguments.
9
10use crate::argument::ArgumentError;
11use crate::argument::ArgumentErrorKind;
12use crate::argument::ArgumentResult;
13use crate::argument::internal::Sealed;
14
15/// Validates optional arguments without requiring their values to be cloned.
16///
17/// A required value can be extracted with [`Self::require_some`]. Conditional
18/// validation borrows a present value only for the validator call and returns
19/// the original option on success.
20///
21/// The trait is sealed and implemented only for `Option<T>`.
22///
23/// # Examples
24///
25/// ```
26/// use qubit_argument::OptionArgument;
27///
28/// let value = Some(42).validate_some(|value| Ok(value + 1));
29/// assert_eq!(value.unwrap(), Some(43));
30/// ```
31pub trait OptionArgument<T>: Sealed + Sized {
32    /// Requires this option to contain a value.
33    ///
34    /// A present value is moved out and returned without cloning. An absent
35    /// value returns [`ArgumentErrorKind::Missing`] at `path`.
36    ///
37    /// # Parameters
38    ///
39    /// - `path`: The argument path reported when the option is absent.
40    ///
41    /// # Returns
42    ///
43    /// The contained value, or an error identifying the missing argument.
44    fn require_some(self, path: &str) -> ArgumentResult<T>;
45
46    /// Validates a present value by temporary borrow.
47    ///
48    /// `validator` receives a shared reference when this option is present. A
49    /// successful validator returns the original option without cloning its
50    /// value; it introduces no failure kind of its own and propagates any
51    /// [`ArgumentErrorKind`] returned by `validator` unchanged. An absent
52    /// option is returned without executing `validator`.
53    ///
54    /// # Parameters
55    ///
56    /// - `validator`: A callback that checks a borrowed present value.
57    ///
58    /// # Returns
59    ///
60    /// The original option on success, or the validator's error unchanged.
61    fn validate_if_some<F>(self, validator: F) -> ArgumentResult<Self>
62    where
63        F: FnOnce(&T) -> ArgumentResult<()>;
64
65    /// Validates and returns a present owned value.
66    ///
67    /// `validator` receives ownership of the contained value when this option
68    /// is present and must return the validated value. A successful validator
69    /// is wrapped in [`Some`] and returned; it may transform the value without
70    /// requiring [`Clone`] or [`Copy`]. An absent option is returned without
71    /// executing `validator`. Validator errors are propagated unchanged.
72    ///
73    /// # Parameters
74    ///
75    /// - `validator`: A callback that consumes and returns the present value.
76    ///
77    /// # Returns
78    ///
79    /// The validated value wrapped in [`Some`], `None` for an absent option,
80    /// or the validator's error unchanged.
81    fn validate_some<F>(self, validator: F) -> ArgumentResult<Self>
82    where
83        F: FnOnce(T) -> ArgumentResult<T>;
84}
85
86impl<T> OptionArgument<T> for Option<T> {
87    /// Extracts a present value or reports that `path` is missing.
88    ///
89    /// The contained value is moved without cloning. An absent option returns
90    /// [`ArgumentErrorKind::Missing`].
91    #[inline]
92    fn require_some(self, path: &str) -> ArgumentResult<T> {
93        match self {
94            Some(value) => Ok(value),
95            None => Err(ArgumentError::new(path, ArgumentErrorKind::Missing)),
96        }
97    }
98
99    /// Borrows and validates a present value, then returns the original option.
100    ///
101    /// A validator error is returned unchanged. When this option is absent,
102    /// `validator` is not executed.
103    #[inline]
104    fn validate_if_some<F>(self, validator: F) -> ArgumentResult<Self>
105    where
106        F: FnOnce(&T) -> ArgumentResult<()>,
107    {
108        if let Some(value) = self.as_ref() {
109            validator(value)?;
110        }
111        Ok(self)
112    }
113
114    /// Moves a present value through `validator` and rewraps it on success.
115    ///
116    /// A validator error is returned unchanged. When this option is absent,
117    /// `validator` is not executed.
118    #[inline]
119    fn validate_some<F>(self, validator: F) -> ArgumentResult<Self>
120    where
121        F: FnOnce(T) -> ArgumentResult<T>,
122    {
123        match self {
124            Some(value) => validator(value).map(Some),
125            None => Ok(None),
126        }
127    }
128}