rocketmq-model 1.0.0

Runtime-neutral RocketMQ domain models
Documentation
// Copyright 2023 The RocketMQ Rust Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Copyright 2023 The RocketMQ Rust Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use cheetah_string::CheetahString;

use crate::common::attribute::Attribute;
use crate::common::attribute::AttributeBase;

#[derive(Debug, Clone)]
pub struct BooleanAttribute {
    attribute: AttributeBase,
    default_value: bool,
}

impl BooleanAttribute {
    /// Create a new boolean attribute with the specified properties
    ///
    /// # Arguments
    ///
    /// * `name` - The name of the attribute
    /// * `changeable` - Whether the attribute can be changed after creation
    /// * `default_value` - Default boolean value for this attribute
    ///
    /// # Returns
    ///
    /// A new `BooleanAttribute` instance
    ///
    /// # Example
    ///
    /// ```
    /// use cheetah_string::CheetahString;
    /// use rocketmq_model::common::attribute::bool_attribute::BooleanAttribute;
    /// use rocketmq_model::common::attribute::Attribute;
    ///
    /// let attr = BooleanAttribute::new(
    ///     CheetahString::from_static_str("enabled"),
    ///     true,
    ///     false
    /// );
    /// assert_eq!(attr.default_value(), false);
    ///
    /// // Verify accepts valid boolean strings
    /// assert!(attr.verify("true").is_ok());
    /// assert!(attr.verify("FALSE").is_ok());
    /// ```
    pub fn new(name: CheetahString, changeable: bool, default_value: bool) -> Self {
        Self {
            attribute: AttributeBase::new(name, changeable),
            default_value,
        }
    }

    /// Get the default value for this attribute
    #[inline]
    pub fn default_value(&self) -> bool {
        self.default_value
    }

    /// Parse a string value to boolean
    ///
    /// Accepts "true" or "false" case-insensitively. Whitespace is not trimmed.
    ///
    /// # Arguments
    ///
    /// * `value` - The string value to parse
    ///
    /// # Returns
    ///
    /// * `Ok(bool)` - The parsed boolean value
    /// * `Err(String)` - An error message if the value is not "true" or "false"
    ///
    /// # Errors
    ///
    /// Returns an error if the input string is not "true" or "false" (case-insensitive).
    ///
    /// # Example
    ///
    /// ```
    /// use rocketmq_model::common::attribute::bool_attribute::BooleanAttribute;
    ///
    /// // Case-insensitive parsing
    /// assert_eq!(BooleanAttribute::parse_bool("true").unwrap(), true);
    /// assert_eq!(BooleanAttribute::parse_bool("TRUE").unwrap(), true);
    /// assert_eq!(BooleanAttribute::parse_bool("False").unwrap(), false);
    ///
    /// // Invalid values return errors
    /// assert!(BooleanAttribute::parse_bool("yes").is_err());
    /// assert!(BooleanAttribute::parse_bool(" true").is_err()); // whitespace not trimmed
    /// ```
    pub fn parse_bool(value: &str) -> Result<bool, String> {
        match value.to_lowercase().as_str() {
            "true" => Ok(true),
            "false" => Ok(false),
            _ => Err(format!("Boolean attribute must be 'true' or 'false', got '{value}'",)),
        }
    }
}

impl Attribute for BooleanAttribute {
    #[inline]
    fn verify(&self, value: &str) -> Result<(), String> {
        if value.is_empty() {
            return Err("Boolean attribute value cannot be empty".to_string());
        }

        // Try parsing to ensure it's a valid boolean
        Self::parse_bool(value)?;
        Ok(())
    }

    #[inline]
    fn name(&self) -> &CheetahString {
        self.attribute.name()
    }

    #[inline]
    fn is_changeable(&self) -> bool {
        self.attribute.is_changeable()
    }
}

#[cfg(test)]
mod tests {
    use cheetah_string::CheetahString;

    use super::*;

    #[test]
    fn create_boolean_attribute() {
        let name = CheetahString::from_static_str("test_attribute");
        let attribute = BooleanAttribute::new(name.clone(), true, true);
        assert_eq!(attribute.name(), &name);
        assert!(attribute.is_changeable());
        assert!(attribute.default_value());
    }

    #[test]
    fn parse_bool_true() {
        let result = BooleanAttribute::parse_bool("true");
        assert!(result.is_ok());
        assert!(result.unwrap());
    }

    #[test]
    fn parse_bool_false() {
        let result = BooleanAttribute::parse_bool("false");
        assert!(result.is_ok());
        assert!(!result.unwrap());
    }

    #[test]
    fn parse_bool_invalid() {
        let result = BooleanAttribute::parse_bool("invalid");
        assert!(result.is_err());
        assert_eq!(
            result.unwrap_err(),
            "Boolean attribute must be 'true' or 'false', got 'invalid'"
        );
    }

    #[test]
    fn verify_valid_true() {
        let attribute = BooleanAttribute::new(CheetahString::from_static_str("test_attribute"), true, true);
        let result = attribute.verify("true");
        assert!(result.is_ok());
    }

    #[test]
    fn verify_valid_false() {
        let attribute = BooleanAttribute::new(CheetahString::from_static_str("test_attribute"), true, false);
        let result = attribute.verify("false");
        assert!(result.is_ok());
    }

    #[test]
    fn verify_empty_value() {
        let attribute = BooleanAttribute::new(CheetahString::from_static_str("test_attribute"), true, true);
        let result = attribute.verify("");
        assert!(result.is_err());
        assert_eq!(result.unwrap_err(), "Boolean attribute value cannot be empty");
    }

    #[test]
    fn verify_invalid_value() {
        let attribute = BooleanAttribute::new(CheetahString::from_static_str("test_attribute"), true, true);
        let result = attribute.verify("invalid");
        assert!(result.is_err());
        assert_eq!(
            result.unwrap_err(),
            "Boolean attribute must be 'true' or 'false', got 'invalid'"
        );
    }
}