openscenario-rs 0.3.1

Rust library for parsing and manipulating OpenSCENARIO files
Documentation
//! Value-based condition types for parameter and time-based triggering
//!
//! This file contains:
//! - Parameter conditions for scenario parameter monitoring
//! - Variable conditions for dynamic variable state checking
//! - Time-based conditions (simulation time, time-of-day)
//! - Storyboard element state conditions for execution flow control
//! - Traffic signal conditions for infrastructure interaction
//! - User-defined custom condition support
//!
use crate::types::basic::DateTime;
use crate::types::basic::{Double, OSString};
use crate::types::enums::{Rule, StoryboardElementState, StoryboardElementType};
use serde::{Deserialize, Serialize};

/// Simulation time-based condition
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct SimulationTimeCondition {
    #[serde(rename = "@value")]
    pub value: Double,
    #[serde(rename = "@rule")]
    pub rule: Rule,
}

/// Parameter-based condition for monitoring scenario parameters
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ParameterCondition {
    #[serde(rename = "@parameterRef")]
    pub parameter_ref: OSString,
    #[serde(rename = "@rule")]
    pub rule: Rule,
    #[serde(rename = "@value")]
    pub value: OSString,
}

/// Time-of-day condition for scheduling based on absolute time
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct TimeOfDayCondition {
    #[serde(rename = "@dateTime")]
    pub date_time: DateTime,
    #[serde(rename = "@rule")]
    pub rule: Rule,
}

/// Storyboard element state condition for execution flow control
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct StoryboardElementStateCondition {
    #[serde(rename = "@storyboardElementRef")]
    pub storyboard_element_ref: OSString,
    #[serde(rename = "@state")]
    pub state: StoryboardElementState,
    #[serde(rename = "@storyboardElementType")]
    pub storyboard_element_type: StoryboardElementType,
}

/// User-defined custom condition for extensible condition logic
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct UserDefinedValueCondition {
    #[serde(rename = "@name")]
    pub name: OSString,
    #[serde(rename = "@rule")]
    pub rule: Rule,
    #[serde(rename = "@value")]
    pub value: OSString,
}

/// Traffic signal condition for infrastructure signal monitoring
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct TrafficSignalCondition {
    #[serde(rename = "@name")]
    pub name: OSString,
    #[serde(rename = "@state")]
    pub state: OSString,
}

/// Traffic signal controller condition for controller phase monitoring
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct TrafficSignalControllerCondition {
    #[serde(rename = "@trafficSignalControllerRef")]
    pub traffic_signal_controller_ref: OSString,
    #[serde(rename = "@phase")]
    pub phase: OSString,
}

/// Variable condition for dynamic variable state checking
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct VariableCondition {
    #[serde(rename = "@variableRef")]
    pub variable_ref: OSString,
    #[serde(rename = "@rule")]
    pub rule: Rule,
    #[serde(rename = "@value")]
    pub value: OSString,
}

/// Value-based condition types - implements all OpenSCENARIO ByValueCondition variants
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ByValueCondition {
    /// Parameter-based condition
    #[serde(rename = "ParameterCondition", skip_serializing_if = "Option::is_none")]
    pub parameter_condition: Option<ParameterCondition>,

    /// Time-of-day condition
    #[serde(rename = "TimeOfDayCondition", skip_serializing_if = "Option::is_none")]
    pub time_of_day_condition: Option<TimeOfDayCondition>,

    /// Simulation time-based condition
    #[serde(
        rename = "SimulationTimeCondition",
        skip_serializing_if = "Option::is_none"
    )]
    pub simulation_time_condition: Option<SimulationTimeCondition>,

    /// Storyboard element state condition
    #[serde(
        rename = "StoryboardElementStateCondition",
        skip_serializing_if = "Option::is_none"
    )]
    pub storyboard_element_state_condition: Option<StoryboardElementStateCondition>,

    /// User-defined condition
    #[serde(
        rename = "UserDefinedValueCondition",
        skip_serializing_if = "Option::is_none"
    )]
    pub user_defined_value_condition: Option<UserDefinedValueCondition>,

    /// Traffic signal condition
    #[serde(
        rename = "TrafficSignalCondition",
        skip_serializing_if = "Option::is_none"
    )]
    pub traffic_signal_condition: Option<TrafficSignalCondition>,

    /// Traffic signal controller condition
    #[serde(
        rename = "TrafficSignalControllerCondition",
        skip_serializing_if = "Option::is_none"
    )]
    pub traffic_signal_controller_condition: Option<TrafficSignalControllerCondition>,

    /// Variable condition
    #[serde(rename = "VariableCondition", skip_serializing_if = "Option::is_none")]
    pub variable_condition: Option<VariableCondition>,
}

// Default implementations
impl Default for SimulationTimeCondition {
    fn default() -> Self {
        Self {
            value: Double::literal(10.0),
            rule: Rule::GreaterThan,
        }
    }
}

impl Default for ParameterCondition {
    fn default() -> Self {
        Self {
            parameter_ref: OSString::literal("defaultParam".to_string()),
            rule: Rule::EqualTo,
            value: OSString::literal("defaultValue".to_string()),
        }
    }
}

impl Default for TimeOfDayCondition {
    fn default() -> Self {
        Self {
            date_time: DateTime::literal(chrono::Utc::now()),
            rule: Rule::GreaterThan,
        }
    }
}

impl Default for StoryboardElementStateCondition {
    fn default() -> Self {
        Self {
            storyboard_element_ref: OSString::literal("defaultElement".to_string()),
            state: StoryboardElementState::RunningState,
            storyboard_element_type: StoryboardElementType::Story,
        }
    }
}

impl Default for UserDefinedValueCondition {
    fn default() -> Self {
        Self {
            name: OSString::literal("defaultCondition".to_string()),
            rule: Rule::EqualTo,
            value: OSString::literal("defaultValue".to_string()),
        }
    }
}

impl Default for TrafficSignalCondition {
    fn default() -> Self {
        Self {
            name: OSString::literal("defaultSignal".to_string()),
            state: OSString::literal("green".to_string()),
        }
    }
}

impl Default for TrafficSignalControllerCondition {
    fn default() -> Self {
        Self {
            traffic_signal_controller_ref: OSString::literal("defaultController".to_string()),
            phase: OSString::literal("phase1".to_string()),
        }
    }
}

impl Default for VariableCondition {
    fn default() -> Self {
        Self {
            variable_ref: OSString::literal("defaultVariable".to_string()),
            rule: Rule::EqualTo,
            value: OSString::literal("defaultValue".to_string()),
        }
    }
}

impl Default for ByValueCondition {
    fn default() -> Self {
        Self {
            parameter_condition: None,
            time_of_day_condition: None,
            simulation_time_condition: Some(SimulationTimeCondition::default()),
            storyboard_element_state_condition: None,
            user_defined_value_condition: None,
            traffic_signal_condition: None,
            traffic_signal_controller_condition: None,
            variable_condition: None,
        }
    }
}

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

    #[test]
    fn test_simulation_time_condition_default() {
        let cond = SimulationTimeCondition::default();
        assert_eq!(cond.value.as_literal().unwrap(), &10.0);
        assert_eq!(cond.rule, Rule::GreaterThan);
    }

    #[test]
    fn test_simulation_time_condition_xml_roundtrip() {
        let cond = SimulationTimeCondition {
            value: Double::literal(5.0),
            rule: Rule::EqualTo,
        };
        let xml = quick_xml::se::to_string(&cond).unwrap();
        let deserialized: SimulationTimeCondition = quick_xml::de::from_str(&xml).unwrap();
        assert_eq!(cond, deserialized);
    }

    #[test]
    fn test_parameter_condition_default() {
        let cond = ParameterCondition::default();
        assert_eq!(cond.parameter_ref.as_literal().unwrap(), "defaultParam");
        assert_eq!(cond.rule, Rule::EqualTo);
        assert_eq!(cond.value.as_literal().unwrap(), "defaultValue");
    }

    #[test]
    fn test_traffic_signal_condition_xml_roundtrip() {
        let cond = TrafficSignalCondition {
            name: OSString::literal("signal1".to_string()),
            state: OSString::literal("red".to_string()),
        };
        let xml = quick_xml::se::to_string(&cond).unwrap();
        let deserialized: TrafficSignalCondition = quick_xml::de::from_str(&xml).unwrap();
        assert_eq!(cond, deserialized);
    }

    #[test]
    fn test_by_value_condition_default_has_simulation_time() {
        let cond = ByValueCondition::default();
        assert!(cond.simulation_time_condition.is_some());
        assert!(cond.parameter_condition.is_none());
        assert!(cond.variable_condition.is_none());
    }

    #[test]
    fn test_storyboard_element_state_condition_default() {
        let cond = StoryboardElementStateCondition::default();
        assert_eq!(cond.state, StoryboardElementState::RunningState);
        assert_eq!(cond.storyboard_element_type, StoryboardElementType::Story);
    }

    #[test]
    fn test_variable_condition_xml_roundtrip() {
        let cond = VariableCondition {
            variable_ref: OSString::literal("speed".to_string()),
            rule: Rule::GreaterThan,
            value: OSString::literal("100".to_string()),
        };
        let xml = quick_xml::se::to_string(&cond).unwrap();
        let deserialized: VariableCondition = quick_xml::de::from_str(&xml).unwrap();
        assert_eq!(cond, deserialized);
    }
}