Skip to main content

rocketmq_model/common/attribute/
bool_attribute.rs

1// Copyright 2023 The RocketMQ Rust Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15// Copyright 2023 The RocketMQ Rust Authors
16//
17// Licensed under the Apache License, Version 2.0 (the "License");
18// you may not use this file except in compliance with the License.
19// You may obtain a copy of the License at
20//
21//     http://www.apache.org/licenses/LICENSE-2.0
22//
23// Unless required by applicable law or agreed to in writing, software
24// distributed under the License is distributed on an "AS IS" BASIS,
25// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
26// See the License for the specific language governing permissions and
27// limitations under the License.
28
29use cheetah_string::CheetahString;
30
31use crate::common::attribute::Attribute;
32use crate::common::attribute::AttributeBase;
33
34#[derive(Debug, Clone)]
35pub struct BooleanAttribute {
36    attribute: AttributeBase,
37    default_value: bool,
38}
39
40impl BooleanAttribute {
41    /// Create a new boolean attribute with the specified properties
42    ///
43    /// # Arguments
44    ///
45    /// * `name` - The name of the attribute
46    /// * `changeable` - Whether the attribute can be changed after creation
47    /// * `default_value` - Default boolean value for this attribute
48    ///
49    /// # Returns
50    ///
51    /// A new `BooleanAttribute` instance
52    ///
53    /// # Example
54    ///
55    /// ```
56    /// use cheetah_string::CheetahString;
57    /// use rocketmq_model::common::attribute::bool_attribute::BooleanAttribute;
58    /// use rocketmq_model::common::attribute::Attribute;
59    ///
60    /// let attr = BooleanAttribute::new(
61    ///     CheetahString::from_static_str("enabled"),
62    ///     true,
63    ///     false
64    /// );
65    /// assert_eq!(attr.default_value(), false);
66    ///
67    /// // Verify accepts valid boolean strings
68    /// assert!(attr.verify("true").is_ok());
69    /// assert!(attr.verify("FALSE").is_ok());
70    /// ```
71    pub fn new(name: CheetahString, changeable: bool, default_value: bool) -> Self {
72        Self {
73            attribute: AttributeBase::new(name, changeable),
74            default_value,
75        }
76    }
77
78    /// Get the default value for this attribute
79    #[inline]
80    pub fn default_value(&self) -> bool {
81        self.default_value
82    }
83
84    /// Parse a string value to boolean
85    ///
86    /// Accepts "true" or "false" case-insensitively. Whitespace is not trimmed.
87    ///
88    /// # Arguments
89    ///
90    /// * `value` - The string value to parse
91    ///
92    /// # Returns
93    ///
94    /// * `Ok(bool)` - The parsed boolean value
95    /// * `Err(String)` - An error message if the value is not "true" or "false"
96    ///
97    /// # Errors
98    ///
99    /// Returns an error if the input string is not "true" or "false" (case-insensitive).
100    ///
101    /// # Example
102    ///
103    /// ```
104    /// use rocketmq_model::common::attribute::bool_attribute::BooleanAttribute;
105    ///
106    /// // Case-insensitive parsing
107    /// assert_eq!(BooleanAttribute::parse_bool("true").unwrap(), true);
108    /// assert_eq!(BooleanAttribute::parse_bool("TRUE").unwrap(), true);
109    /// assert_eq!(BooleanAttribute::parse_bool("False").unwrap(), false);
110    ///
111    /// // Invalid values return errors
112    /// assert!(BooleanAttribute::parse_bool("yes").is_err());
113    /// assert!(BooleanAttribute::parse_bool(" true").is_err()); // whitespace not trimmed
114    /// ```
115    pub fn parse_bool(value: &str) -> Result<bool, String> {
116        match value.to_lowercase().as_str() {
117            "true" => Ok(true),
118            "false" => Ok(false),
119            _ => Err(format!("Boolean attribute must be 'true' or 'false', got '{value}'",)),
120        }
121    }
122}
123
124impl Attribute for BooleanAttribute {
125    #[inline]
126    fn verify(&self, value: &str) -> Result<(), String> {
127        if value.is_empty() {
128            return Err("Boolean attribute value cannot be empty".to_string());
129        }
130
131        // Try parsing to ensure it's a valid boolean
132        Self::parse_bool(value)?;
133        Ok(())
134    }
135
136    #[inline]
137    fn name(&self) -> &CheetahString {
138        self.attribute.name()
139    }
140
141    #[inline]
142    fn is_changeable(&self) -> bool {
143        self.attribute.is_changeable()
144    }
145}
146
147#[cfg(test)]
148mod tests {
149    use cheetah_string::CheetahString;
150
151    use super::*;
152
153    #[test]
154    fn create_boolean_attribute() {
155        let name = CheetahString::from_static_str("test_attribute");
156        let attribute = BooleanAttribute::new(name.clone(), true, true);
157        assert_eq!(attribute.name(), &name);
158        assert!(attribute.is_changeable());
159        assert!(attribute.default_value());
160    }
161
162    #[test]
163    fn parse_bool_true() {
164        let result = BooleanAttribute::parse_bool("true");
165        assert!(result.is_ok());
166        assert!(result.unwrap());
167    }
168
169    #[test]
170    fn parse_bool_false() {
171        let result = BooleanAttribute::parse_bool("false");
172        assert!(result.is_ok());
173        assert!(!result.unwrap());
174    }
175
176    #[test]
177    fn parse_bool_invalid() {
178        let result = BooleanAttribute::parse_bool("invalid");
179        assert!(result.is_err());
180        assert_eq!(
181            result.unwrap_err(),
182            "Boolean attribute must be 'true' or 'false', got 'invalid'"
183        );
184    }
185
186    #[test]
187    fn verify_valid_true() {
188        let attribute = BooleanAttribute::new(CheetahString::from_static_str("test_attribute"), true, true);
189        let result = attribute.verify("true");
190        assert!(result.is_ok());
191    }
192
193    #[test]
194    fn verify_valid_false() {
195        let attribute = BooleanAttribute::new(CheetahString::from_static_str("test_attribute"), true, false);
196        let result = attribute.verify("false");
197        assert!(result.is_ok());
198    }
199
200    #[test]
201    fn verify_empty_value() {
202        let attribute = BooleanAttribute::new(CheetahString::from_static_str("test_attribute"), true, true);
203        let result = attribute.verify("");
204        assert!(result.is_err());
205        assert_eq!(result.unwrap_err(), "Boolean attribute value cannot be empty");
206    }
207
208    #[test]
209    fn verify_invalid_value() {
210        let attribute = BooleanAttribute::new(CheetahString::from_static_str("test_attribute"), true, true);
211        let result = attribute.verify("invalid");
212        assert!(result.is_err());
213        assert_eq!(
214            result.unwrap_err(),
215            "Boolean attribute must be 'true' or 'false', got 'invalid'"
216        );
217    }
218}