logic_constructor 0.1.0

Move combat and ability logic out of code and into HOCON config — designers tweak damage, healing, and targeting in a text file; the engine parses it into typed actions and runs them against your entities.
Documentation
use hocon_rs::Value;

use crate::collision_kind::CollisionKind;

fn parse_single_collision_kind(s: &str) -> Result<CollisionKind, String> {
    let trimmed = s.trim();
    match trimmed {
        "Self" => Ok(CollisionKind::SELF),
        "SameKind" => Ok(CollisionKind::SAME_KIND),
        "Other" => Ok(CollisionKind::OTHER),
        _ => Err(format!(
            "Unknown CollisionKind: '{}'. Expected 'Self', 'SameKind', or 'Other'",
            trimmed
        )),
    }
}

/// Parses a HOCON value into a CollisionKind bitflag
/// Supports:
/// - Single flag: "Self"
/// - Multiple flags with pipe: "Self | Other" or "Self | SameKind | Other"
pub fn parse_collision_kind(value: &Value) -> Result<CollisionKind, String> {
    match value {
        Value::String(s) => {
            let parts: Vec<&str> = s.split('|').collect();

            let mut result = CollisionKind::empty();
            for part in parts {
                let flag = parse_single_collision_kind(part)?;
                result |= flag;
            }

            Ok(result)
        }
        _ => Err(format!(
            "CollisionKind parser expects a string (e.g., 'Self' or 'Self | Other'), got: {:?}",
            value
        )),
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use hocon_rs::Config;

    #[test]
    fn test_parse_self() {
        let hocon_str = r#"kind = "Self""#;
        let parsed: Value = Config::parse_str(hocon_str, None).unwrap();
        let value = parsed.as_object().unwrap().get("kind").unwrap();

        let result = parse_collision_kind(value).unwrap();
        assert_eq!(result, CollisionKind::SELF);
    }

    #[test]
    fn test_parse_same_kind() {
        let hocon_str = r#"kind = "SameKind""#;
        let parsed: Value = Config::parse_str(hocon_str, None).unwrap();
        let value = parsed.as_object().unwrap().get("kind").unwrap();

        let result = parse_collision_kind(value).unwrap();
        assert_eq!(result, CollisionKind::SAME_KIND);
    }

    #[test]
    fn test_parse_other() {
        let hocon_str = r#"kind = "Other""#;
        let parsed: Value = Config::parse_str(hocon_str, None).unwrap();
        let value = parsed.as_object().unwrap().get("kind").unwrap();

        let result = parse_collision_kind(value).unwrap();
        assert_eq!(result, CollisionKind::OTHER);
    }

    #[test]
    fn test_parse_unquoted_string() {
        let hocon_str = r#"kind = Self"#;
        let parsed: Value = Config::parse_str(hocon_str, None).unwrap();
        let value = parsed.as_object().unwrap().get("kind").unwrap();

        let result = parse_collision_kind(value).unwrap();
        assert_eq!(result, CollisionKind::SELF);
    }

    #[test]
    fn test_parse_pipe_two_flags() {
        let hocon_str = r#"kind = "Self | Other""#;
        let parsed: Value = Config::parse_str(hocon_str, None).unwrap();
        let value = parsed.as_object().unwrap().get("kind").unwrap();

        let result = parse_collision_kind(value).unwrap();
        assert_eq!(result, CollisionKind::SELF | CollisionKind::OTHER);
        assert!(result.contains(CollisionKind::SELF));
        assert!(result.contains(CollisionKind::OTHER));
        assert!(!result.contains(CollisionKind::SAME_KIND));
    }

    #[test]
    fn test_parse_pipe_all_flags() {
        let hocon_str = r#"kind = "Self | SameKind | Other""#;
        let parsed: Value = Config::parse_str(hocon_str, None).unwrap();
        let value = parsed.as_object().unwrap().get("kind").unwrap();

        let result = parse_collision_kind(value).unwrap();
        assert!(result.contains(CollisionKind::SELF));
        assert!(result.contains(CollisionKind::SAME_KIND));
        assert!(result.contains(CollisionKind::OTHER));
    }

    #[test]
    fn test_parse_unknown_collision_kind() {
        let hocon_str = r#"kind = "UnknownKind""#;
        let parsed: Value = Config::parse_str(hocon_str, None).unwrap();
        let value = parsed.as_object().unwrap().get("kind").unwrap();

        let result = parse_collision_kind(value);
        assert!(result.is_err());
        if let Err(err) = result {
            assert!(err.contains("Unknown CollisionKind"));
        }
    }

    #[test]
    fn test_parse_invalid_type() {
        let hocon_str = r#"kind = 123"#;
        let parsed: Value = Config::parse_str(hocon_str, None).unwrap();
        let value = parsed.as_object().unwrap().get("kind").unwrap();

        let result = parse_collision_kind(value);
        assert!(result.is_err());
        if let Err(err) = result {
            assert!(err.contains("expects a string"));
        }
    }

    #[test]
    fn test_parse_pipe_with_spaces() {
        let hocon_str = r#"kind = "Self|SameKind""#;
        let parsed: Value = Config::parse_str(hocon_str, None).unwrap();
        let value = parsed.as_object().unwrap().get("kind").unwrap();

        let result = parse_collision_kind(value).unwrap();
        assert!(result.contains(CollisionKind::SELF));
        assert!(result.contains(CollisionKind::SAME_KIND));
    }
}