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}