Skip to main content

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}