use crate::Error;
use crate::property::{PropertyObject, PropertyOptions};
const STEP_NAME: &str = "TS.StepName";
const STEP_TYPE: &str = "TS.StepType";
const STATUS: &str = "Status";
const NUMERIC: &str = "Numeric";
const STRING: &str = "String";
#[derive(Debug, Clone, PartialEq)]
pub struct StepResult {
pub name: String,
pub step_type: String,
pub status: String,
pub value: Option<ResultValue>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum ResultValue {
Number(f64),
Text(String),
}
#[derive(Debug)]
pub struct ResultList {
results: PropertyObject,
}
impl ResultList {
pub fn from_result_object(result_object: &PropertyObject) -> Result<Self, Error> {
let none = PropertyOptions::NONE.bits();
if !result_object.exists("ResultList", none)? {
return Err(Error::UnexpectedType {
expected: "an object carrying a ResultList",
actual: "no ResultList property",
});
}
Ok(Self {
results: result_object.get_property_object("ResultList", none)?,
})
}
pub fn len(&self) -> Result<i32, Error> {
self.results.get_num_elements()
}
pub fn is_empty(&self) -> Result<bool, Error> {
Ok(self.len()? == 0)
}
pub fn parse(&self) -> Result<Vec<StepResult>, Error> {
let none = PropertyOptions::NONE.bits();
let mut parsed = Vec::new();
for index in 0..self.len()? {
let entry = self.results.get_property_object_by_offset(index, none)?;
let text = |path: &str| entry.get_val_string(path, none).unwrap_or_default();
let value = entry
.get_val_number(NUMERIC, none)
.ok()
.map(ResultValue::Number)
.or_else(|| {
entry
.get_val_string(STRING, none)
.ok()
.filter(|found| !found.is_empty())
.map(ResultValue::Text)
});
parsed.push(StepResult {
name: text(STEP_NAME),
step_type: text(STEP_TYPE),
status: text(STATUS),
value,
});
}
Ok(parsed)
}
#[must_use]
pub const fn as_property_object(&self) -> &PropertyObject {
&self.results
}
}
#[cfg(test)]
mod tests {
use super::{ResultValue, StepResult};
#[test]
fn a_result_without_a_measurement_is_distinguishable_from_a_zero() {
let action = StepResult {
name: "Initialize".to_owned(),
step_type: "Action".to_owned(),
status: "Done".to_owned(),
value: None,
};
let measured = StepResult {
value: Some(ResultValue::Number(0.0)),
..action.clone()
};
assert_ne!(action.value, measured.value);
assert!(action.value.is_none());
}
#[test]
fn a_text_measurement_is_kept_as_text() {
assert_eq!(
ResultValue::Text("SN-001".to_owned()),
ResultValue::Text("SN-001".to_owned())
);
assert_ne!(
ResultValue::Text("1.5".to_owned()),
ResultValue::Number(1.5)
);
}
}