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}