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}