use rs_teststand_sys::{Dispatch, Value};
use crate::Error;
use crate::dispids::array_dimensions;
#[derive(Debug)]
pub struct ArrayDimensions {
dispatch: Box<dyn Dispatch>,
}
impl ArrayDimensions {
pub(crate) fn new(dispatch: Box<dyn Dispatch>) -> Self {
Self { dispatch }
}
pub fn num_dimensions(&self) -> Result<i32, Error> {
Ok(self
.dispatch
.get(array_dimensions::NUM_DIMENSIONS)?
.as_i32()?)
}
pub fn lower_bounds_string(&self) -> Result<String, Error> {
Ok(self
.dispatch
.get(array_dimensions::LOWER_BOUNDS_STRING)?
.into_string()?)
}
pub fn upper_bounds_string(&self) -> Result<String, Error> {
Ok(self
.dispatch
.get(array_dimensions::UPPER_BOUNDS_STRING)?
.into_string()?)
}
pub fn set_bounds_by_strings(&self, lower: &str, upper: &str) -> Result<(), Error> {
self.dispatch.call(
array_dimensions::SET_BOUNDS_BY_STRINGS,
&[Value::Str(lower.to_owned()), Value::Str(upper.to_owned())],
)?;
Ok(())
}
pub fn lengths(&self) -> Result<Vec<i32>, Error> {
let lower = parse_bounds(&self.lower_bounds_string()?)?;
let upper = parse_bounds(&self.upper_bounds_string()?)?;
if upper.is_empty() {
return Ok(vec![0; lower.len()]);
}
if lower.len() != upper.len() {
return Err(Error::UnexpectedType {
expected: "matching lower and upper bound counts",
actual: "differing bound counts",
});
}
Ok(lower
.iter()
.zip(upper.iter())
.map(|(low, high)| high - low + 1)
.collect())
}
}
fn parse_bounds(text: &str) -> Result<Vec<i32>, Error> {
let trimmed = text.trim();
if trimmed.is_empty() || trimmed == "[]" {
return Ok(Vec::new());
}
trimmed
.split(']')
.filter(|group| !group.trim().is_empty())
.map(|group| {
group
.trim()
.trim_start_matches('[')
.trim()
.parse::<i32>()
.map_err(|_| Error::UnexpectedType {
expected: "one bracketed integer per dimension, e.g. [0][0]",
actual: "an unparsable bound string",
})
})
.collect()
}
#[cfg(test)]
mod tests {
use super::parse_bounds;
#[test]
fn bounds_parse_one_group_per_dimension() -> Result<(), crate::Error> {
assert_eq!(parse_bounds("[0]")?, vec![0]);
assert_eq!(parse_bounds("[9][1]")?, vec![9, 1]);
assert_eq!(parse_bounds("[0][0][0]")?, vec![0, 0, 0]);
assert_eq!(parse_bounds("[-1]")?, vec![-1]);
Ok(())
}
#[test]
fn a_two_dimensional_shape_multiplies_out_to_its_element_count() -> Result<(), crate::Error> {
let lower = parse_bounds("[0][0]")?;
let upper = parse_bounds("[9][1]")?;
let lengths: Vec<i32> = lower
.iter()
.zip(upper.iter())
.map(|(low, high)| high - low + 1)
.collect();
assert_eq!(lengths, vec![10, 2]);
assert_eq!(lengths.iter().product::<i32>(), 20);
Ok(())
}
#[test]
fn an_empty_array_has_a_dimension_of_length_zero() -> Result<(), crate::Error> {
assert_eq!(parse_bounds("[0]")?, vec![0]);
assert!(parse_bounds("[]")?.is_empty());
Ok(())
}
#[test]
fn a_non_array_reports_no_bounds() -> Result<(), crate::Error> {
assert!(parse_bounds("")?.is_empty());
assert!(parse_bounds("[]")?.is_empty());
Ok(())
}
#[test]
fn a_malformed_bound_is_reported_not_guessed() {
assert!(parse_bounds("[0,oops]").is_err());
}
}