Skip to main content

qubit_config/
config_property_mut.rs

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