Skip to main content

rocketmq_model/common/attribute/
long_range_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 LongRangeAttribute {
36    attribute: AttributeBase,
37    /// Minimum allowed value (inclusive)
38    min: i64,
39
40    /// Maximum allowed value (inclusive)
41    max: i64,
42
43    /// Default value for this attribute
44    default_value: i64,
45}
46
47impl LongRangeAttribute {
48    /// Creates a long attribute with inclusive range bounds.
49    ///
50    /// # Arguments
51    ///
52    /// * `name` - The name of the attribute
53    /// * `changeable` - Whether the attribute can be changed after creation
54    /// * `min` - Minimum allowed value (inclusive)
55    /// * `max` - Maximum allowed value (inclusive)
56    /// * `default_value` - Default value for this attribute
57    ///
58    /// # Returns
59    ///
60    /// A new `LongRangeAttribute` without validating its bounds or default value.
61    /// Values are checked against the range by [`Attribute::verify`].
62    pub fn new(name: CheetahString, changeable: bool, min: i64, max: i64, default_value: i64) -> Self {
63        Self {
64            attribute: AttributeBase::new(name, changeable),
65            min,
66            max,
67            default_value,
68        }
69    }
70
71    /// Get the default value for this attribute
72    #[inline]
73    pub fn default_value(&self) -> i64 {
74        self.default_value
75    }
76
77    /// Get the minimum allowed value
78    pub fn min(&self) -> i64 {
79        self.min
80    }
81
82    /// Get the maximum allowed value
83    pub fn max(&self) -> i64 {
84        self.max
85    }
86
87    /// Parse a string to a long integer
88    pub fn parse_long(value: &str) -> Result<i64, String> {
89        value.parse::<i64>().map_err(|e| format!("Invalid integer format: {e}"))
90    }
91}
92
93impl Attribute for LongRangeAttribute {
94    fn verify(&self, value: &str) -> Result<(), String> {
95        // Parse the value to an i64
96        let parsed_value = Self::parse_long(value)?;
97
98        // Check if the value is in range
99        if parsed_value < self.min || parsed_value > self.max {
100            return Err(format!(
101                "Value {} is not in range [{}, {}]",
102                parsed_value, self.min, self.max
103            ));
104        }
105
106        Ok(())
107    }
108
109    #[inline]
110    fn name(&self) -> &CheetahString {
111        self.attribute.name()
112    }
113
114    #[inline]
115    fn is_changeable(&self) -> bool {
116        self.attribute.is_changeable()
117    }
118}
119
120#[cfg(test)]
121mod tests {
122    use cheetah_string::CheetahString;
123
124    use super::*;
125
126    #[test]
127    fn create_long_range_attribute() {
128        let name = CheetahString::from_static_str("test_attribute");
129        let attribute = LongRangeAttribute::new(name.clone(), true, 0, 100, 50);
130        assert_eq!(attribute.name(), &name);
131        assert!(attribute.is_changeable());
132        assert_eq!(attribute.default_value(), 50);
133    }
134
135    #[test]
136    fn parse_long_valid() {
137        let result = LongRangeAttribute::parse_long("42");
138        assert!(result.is_ok());
139        assert_eq!(result.unwrap(), 42);
140    }
141
142    #[test]
143    fn parse_long_invalid() {
144        let result = LongRangeAttribute::parse_long("invalid");
145        assert!(result.is_err());
146        assert_eq!(
147            result.unwrap_err(),
148            "Invalid integer format: invalid digit found in string"
149        );
150    }
151
152    #[test]
153    fn verify_value_in_range() {
154        let attribute = LongRangeAttribute::new(CheetahString::from_static_str("test_attribute"), true, 0, 100, 50);
155        let result = attribute.verify("42");
156        assert!(result.is_ok());
157    }
158
159    #[test]
160    fn verify_value_below_range() {
161        let attribute = LongRangeAttribute::new(CheetahString::from_static_str("test_attribute"), true, 0, 100, 50);
162        let result = attribute.verify("-1");
163        assert!(result.is_err());
164        assert_eq!(result.unwrap_err(), "Value -1 is not in range [0, 100]");
165    }
166
167    #[test]
168    fn verify_value_above_range() {
169        let attribute = LongRangeAttribute::new(CheetahString::from_static_str("test_attribute"), true, 0, 100, 50);
170        let result = attribute.verify("101");
171        assert!(result.is_err());
172        assert_eq!(result.unwrap_err(), "Value 101 is not in range [0, 100]");
173    }
174}