qubit_config/config_property_mut.rs
1/*******************************************************************************
2 *
3 * Copyright (c) 2025 - 2026.
4 * Haixing Hu, Qubit Co. Ltd.
5 *
6 * All rights reserved.
7 *
8 ******************************************************************************/
9//! # Mutable Configuration Property Guard
10//!
11//! Provides guarded mutable access to non-final configuration properties.
12//!
13//! # Author
14//!
15//! Haixing Hu
16
17use std::ops::Deref;
18
19use qubit_value::MultiValues;
20use qubit_value::multi_values::{
21 MultiValuesAddArg, MultiValuesAdder, MultiValuesMultiAdder, MultiValuesSetArg,
22 MultiValuesSetter, MultiValuesSetterSlice, MultiValuesSingleSetter,
23};
24
25use crate::{ConfigError, ConfigResult, Property};
26
27/// Guarded mutable access to a non-final [`Property`] stored in a
28/// [`crate::Config`].
29///
30/// This wrapper deliberately exposes read-only deref to [`Property`], but not
31/// `DerefMut`. Value-changing operations re-check the property's final flag on
32/// every call, so setting a property final through the guard immediately blocks
33/// subsequent mutation through the same guard.
34pub struct ConfigPropertyMut<'a> {
35 property: &'a mut Property,
36}
37
38impl<'a> ConfigPropertyMut<'a> {
39 /// Creates a guarded mutable property reference.
40 ///
41 /// # Parameters
42 ///
43 /// * `property` - The property to guard.
44 ///
45 /// # Returns
46 ///
47 /// A mutable guard for `property`.
48 #[inline]
49 pub(crate) fn new(property: &'a mut Property) -> Self {
50 Self { property }
51 }
52
53 /// Returns the underlying property as a read-only reference.
54 ///
55 /// # Returns
56 ///
57 /// The guarded property.
58 #[inline]
59 pub fn as_property(&self) -> &Property {
60 self.property
61 }
62
63 /// Sets the property description when the property is not final.
64 ///
65 /// # Parameters
66 ///
67 /// * `description` - New property description.
68 ///
69 /// # Returns
70 ///
71 /// `Ok(())` on success.
72 ///
73 /// # Errors
74 ///
75 /// Returns [`ConfigError::PropertyIsFinal`] if the property has already
76 /// been marked final.
77 #[inline]
78 pub fn set_description(&mut self, description: Option<String>) -> ConfigResult<()> {
79 self.ensure_not_final()?;
80 self.property.set_description(description);
81 Ok(())
82 }
83
84 /// Sets whether the property is final.
85 ///
86 /// Marking a non-final property as final succeeds. A property that is
87 /// already final may be marked final again, but cannot be unset through
88 /// this guard.
89 ///
90 /// # Parameters
91 ///
92 /// * `is_final` - Whether the property should be final.
93 ///
94 /// # Returns
95 ///
96 /// `Ok(())` on success.
97 ///
98 /// # Errors
99 ///
100 /// Returns [`ConfigError::PropertyIsFinal`] when trying to unset an
101 /// already-final property.
102 #[inline]
103 pub fn set_final(&mut self, is_final: bool) -> ConfigResult<()> {
104 if self.property.is_final() && !is_final {
105 return Err(ConfigError::PropertyIsFinal(
106 self.property.name().to_string(),
107 ));
108 }
109 self.property.set_final(is_final);
110 Ok(())
111 }
112
113 /// Replaces the property value when the property is not final.
114 ///
115 /// # Parameters
116 ///
117 /// * `value` - New property value.
118 ///
119 /// # Returns
120 ///
121 /// `Ok(())` on success.
122 ///
123 /// # Errors
124 ///
125 /// Returns [`ConfigError::PropertyIsFinal`] if the property has already
126 /// been marked final.
127 #[inline]
128 pub fn set_value(&mut self, value: MultiValues) -> ConfigResult<()> {
129 self.ensure_not_final()?;
130 self.property.set_value(value);
131 Ok(())
132 }
133
134 /// Replaces the property value using the generic [`MultiValues`] setter.
135 ///
136 /// # Type Parameters
137 ///
138 /// * `S` - Input accepted by [`MultiValues`] setter traits.
139 ///
140 /// # Parameters
141 ///
142 /// * `values` - New value or values.
143 ///
144 /// # Returns
145 ///
146 /// `Ok(())` on success.
147 ///
148 /// # Errors
149 ///
150 /// Returns [`ConfigError::PropertyIsFinal`] if the property has already
151 /// been marked final, or a converted value error if setting fails.
152 pub fn set<S>(&mut self, values: S) -> ConfigResult<()>
153 where
154 S: for<'b> MultiValuesSetArg<'b>,
155 <S as MultiValuesSetArg<'static>>::Item: Clone,
156 MultiValues: MultiValuesSetter<<S as MultiValuesSetArg<'static>>::Item>
157 + MultiValuesSetterSlice<<S as MultiValuesSetArg<'static>>::Item>
158 + MultiValuesSingleSetter<<S as MultiValuesSetArg<'static>>::Item>,
159 {
160 self.ensure_not_final()?;
161 self.property.set(values).map_err(ConfigError::from)
162 }
163
164 /// Appends values using the generic [`MultiValues`] adder.
165 ///
166 /// # Type Parameters
167 ///
168 /// * `S` - Input accepted by [`MultiValues`] adder traits.
169 ///
170 /// # Parameters
171 ///
172 /// * `values` - Value or values to append.
173 ///
174 /// # Returns
175 ///
176 /// `Ok(())` on success.
177 ///
178 /// # Errors
179 ///
180 /// Returns [`ConfigError::PropertyIsFinal`] if the property has already
181 /// been marked final, or a converted value error if appending fails.
182 pub fn add<S>(&mut self, values: S) -> ConfigResult<()>
183 where
184 S: for<'b> MultiValuesAddArg<'b, Item = <S as MultiValuesSetArg<'static>>::Item>
185 + for<'b> MultiValuesSetArg<'b>,
186 <S as MultiValuesSetArg<'static>>::Item: Clone,
187 MultiValues: MultiValuesAdder<<S as MultiValuesSetArg<'static>>::Item>
188 + MultiValuesMultiAdder<<S as MultiValuesSetArg<'static>>::Item>
189 + MultiValuesSetter<<S as MultiValuesSetArg<'static>>::Item>
190 + MultiValuesSetterSlice<<S as MultiValuesSetArg<'static>>::Item>
191 + MultiValuesSingleSetter<<S as MultiValuesSetArg<'static>>::Item>,
192 {
193 self.ensure_not_final()?;
194 self.property.add(values).map_err(ConfigError::from)
195 }
196
197 /// Clears the property value when the property is not final.
198 ///
199 /// # Returns
200 ///
201 /// `Ok(())` on success.
202 ///
203 /// # Errors
204 ///
205 /// Returns [`ConfigError::PropertyIsFinal`] if the property has already
206 /// been marked final.
207 #[inline]
208 pub fn clear(&mut self) -> ConfigResult<()> {
209 self.ensure_not_final()?;
210 self.property.clear();
211 Ok(())
212 }
213
214 /// Ensures the guarded property has not been marked final.
215 ///
216 /// # Returns
217 ///
218 /// `Ok(())` if the property is mutable.
219 ///
220 /// # Errors
221 ///
222 /// Returns [`ConfigError::PropertyIsFinal`] if the property is final.
223 #[inline]
224 fn ensure_not_final(&self) -> ConfigResult<()> {
225 if self.property.is_final() {
226 return Err(ConfigError::PropertyIsFinal(
227 self.property.name().to_string(),
228 ));
229 }
230 Ok(())
231 }
232}
233
234impl Deref for ConfigPropertyMut<'_> {
235 type Target = Property;
236
237 /// Dereferences to the guarded property for read-only access.
238 #[inline]
239 fn deref(&self) -> &Self::Target {
240 self.property
241 }
242}