use rs_teststand::{UIMessage, UIMessageCode};
use rs_teststand_serde::PropertyObjectValue as _;
use crate::Error;
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct MessageEvent {
pub code: i32,
#[serde(serialize_with = "finite")]
pub numeric: f64,
pub text: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub payload: Option<String>,
pub synchronous: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub execution_id: Option<i32>,
}
impl MessageEvent {
#[must_use]
pub const fn is_from_sequence(&self) -> bool {
UIMessageCode::is_user_message(self.code)
}
#[must_use]
pub fn engine_code(&self) -> Option<UIMessageCode> {
UIMessageCode::from_bits(self.code).ok()
}
pub fn from_ui_message(message: &UIMessage, policy: PayloadPolicy) -> Result<Self, Error> {
let code = message.event()?;
let payload = match message.activex_data()? {
Some(container) if policy.admits(code) => match container.to_value() {
Ok(value) => Some(serde_json::to_string(&value)?),
Err(rs_teststand::Error::RecursionLimit { .. }) => None,
Err(error) => return Err(error.into()),
},
Some(_) | None => None,
};
Ok(Self {
code,
numeric: message.numeric_data()?,
text: message.string_data()?,
payload,
synchronous: message.is_synchronous()?,
execution_id: message
.execution()?
.map(|execution| execution.id())
.transpose()?,
})
}
}
#[allow(
clippy::trivially_copy_pass_by_ref,
reason = "serde requires this exact signature for serialize_with"
)]
fn finite<S: serde::Serializer>(value: &f64, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_f64(if value.is_finite() { *value } else { 0.0 })
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum PayloadPolicy {
#[default]
SequenceMessagesOnly,
Everything,
Never,
}
impl PayloadPolicy {
#[must_use]
pub const fn admits(self, code: i32) -> bool {
match self {
Self::Everything => true,
Self::Never => false,
Self::SequenceMessagesOnly => UIMessageCode::is_user_message(code),
}
}
}