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}