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;
18
19use crate::{
20    ConfigError,
21    ConfigResult,
22    Property,
23};
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(self.property.name().to_string()));
105        }
106        self.property.set_final(is_final);
107        Ok(())
108    }
109
110    /// Replaces the property value when the property is not final.
111    ///
112    /// # Parameters
113    ///
114    /// * `value` - New property value.
115    ///
116    /// # Returns
117    ///
118    /// `Ok(())` on success.
119    ///
120    /// # Errors
121    ///
122    /// Returns [`ConfigError::PropertyIsFinal`] if the property has already
123    /// been marked final.
124    #[inline]
125    pub fn set_value(&mut self, value: MultiValues) -> ConfigResult<()> {
126        self.ensure_not_final()?;
127        self.property.set_value(value);
128        Ok(())
129    }
130
131    /// Replaces the property value using the generic [`MultiValues`] setter.
132    ///
133    /// # Type Parameters
134    ///
135    /// * `S` - Input accepted by [`MultiValues`] setter traits.
136    ///
137    /// # Parameters
138    ///
139    /// * `values` - New value or values.
140    ///
141    /// # Returns
142    ///
143    /// `Ok(())` on success.
144    ///
145    /// # Errors
146    ///
147    /// Returns [`ConfigError::PropertyIsFinal`] if the property has already
148    /// been marked final, or a converted value error if setting fails.
149    pub fn set<S>(&mut self, values: S) -> ConfigResult<()>
150    where
151        S: Into<MultiValues>,
152    {
153        self.ensure_not_final()?;
154        self.property.set(values).map_err(ConfigError::from)
155    }
156
157    /// Appends values using the generic [`MultiValues`] adder.
158    ///
159    /// # Type Parameters
160    ///
161    /// * `S` - Input accepted by [`MultiValues`] adder traits.
162    ///
163    /// # Parameters
164    ///
165    /// * `values` - Value or values to append.
166    ///
167    /// # Returns
168    ///
169    /// `Ok(())` on success.
170    ///
171    /// # Errors
172    ///
173    /// Returns [`ConfigError::PropertyIsFinal`] if the property has already
174    /// been marked final, or a converted value error if appending fails.
175    pub fn add<S>(&mut self, values: S) -> ConfigResult<()>
176    where
177        S: Into<MultiValues>,
178    {
179        self.ensure_not_final()?;
180        self.property.add(values).map_err(ConfigError::from)
181    }
182
183    /// Clears the property value when the property is not final.
184    ///
185    /// # Returns
186    ///
187    /// `Ok(())` on success.
188    ///
189    /// # Errors
190    ///
191    /// Returns [`ConfigError::PropertyIsFinal`] if the property has already
192    /// been marked final.
193    #[inline]
194    pub fn clear(&mut self) -> ConfigResult<()> {
195        self.ensure_not_final()?;
196        self.property.clear();
197        Ok(())
198    }
199
200    /// Ensures the guarded property has not been marked final.
201    ///
202    /// # Returns
203    ///
204    /// `Ok(())` if the property is mutable.
205    ///
206    /// # Errors
207    ///
208    /// Returns [`ConfigError::PropertyIsFinal`] if the property is final.
209    #[inline]
210    fn ensure_not_final(&self) -> ConfigResult<()> {
211        if self.property.is_final() {
212            return Err(ConfigError::PropertyIsFinal(self.property.name().to_string()));
213        }
214        Ok(())
215    }
216}
217
218impl Deref for ConfigPropertyMut<'_> {
219    type Target = Property;
220
221    /// Dereferences to the guarded property for read-only access.
222    #[inline]
223    fn deref(&self) -> &Self::Target {
224        self.property
225    }
226}