rs_teststand/property/
array_dimensions.rs1use rs_teststand_sys::{Dispatch, Value};
4
5use crate::Error;
6use crate::dispids::array_dimensions;
7
8#[derive(Debug)]
17pub struct ArrayDimensions {
18 dispatch: Box<dyn Dispatch>,
19}
20
21impl ArrayDimensions {
22 pub(crate) fn new(dispatch: Box<dyn Dispatch>) -> Self {
24 Self { dispatch }
25 }
26
27 pub fn num_dimensions(&self) -> Result<i32, Error> {
33 Ok(self
34 .dispatch
35 .get(array_dimensions::NUM_DIMENSIONS)?
36 .as_i32()?)
37 }
38
39 pub fn lower_bounds_string(&self) -> Result<String, Error> {
44 Ok(self
45 .dispatch
46 .get(array_dimensions::LOWER_BOUNDS_STRING)?
47 .into_string()?)
48 }
49
50 pub fn upper_bounds_string(&self) -> Result<String, Error> {
55 Ok(self
56 .dispatch
57 .get(array_dimensions::UPPER_BOUNDS_STRING)?
58 .into_string()?)
59 }
60
61 pub fn set_bounds_by_strings(&self, lower: &str, upper: &str) -> Result<(), Error> {
70 self.dispatch.call(
71 array_dimensions::SET_BOUNDS_BY_STRINGS,
72 &[Value::Str(lower.to_owned()), Value::Str(upper.to_owned())],
73 )?;
74 Ok(())
75 }
76
77 pub fn lengths(&self) -> Result<Vec<i32>, Error> {
84 let lower = parse_bounds(&self.lower_bounds_string()?)?;
85 let upper = parse_bounds(&self.upper_bounds_string()?)?;
86 if upper.is_empty() {
89 return Ok(vec![0; lower.len()]);
90 }
91 if lower.len() != upper.len() {
92 return Err(Error::UnexpectedType {
93 expected: "matching lower and upper bound counts",
94 actual: "differing bound counts",
95 });
96 }
97 Ok(lower
98 .iter()
99 .zip(upper.iter())
100 .map(|(low, high)| high - low + 1)
101 .collect())
102 }
103}
104
105fn parse_bounds(text: &str) -> Result<Vec<i32>, Error> {
111 let trimmed = text.trim();
112 if trimmed.is_empty() || trimmed == "[]" {
113 return Ok(Vec::new());
114 }
115 trimmed
116 .split(']')
117 .filter(|group| !group.trim().is_empty())
118 .map(|group| {
119 group
120 .trim()
121 .trim_start_matches('[')
122 .trim()
123 .parse::<i32>()
124 .map_err(|_| Error::UnexpectedType {
125 expected: "one bracketed integer per dimension, e.g. [0][0]",
126 actual: "an unparsable bound string",
127 })
128 })
129 .collect()
130}
131
132#[cfg(test)]
133mod tests {
134 use super::parse_bounds;
135
136 #[test]
137 fn bounds_parse_one_group_per_dimension() -> Result<(), crate::Error> {
138 assert_eq!(parse_bounds("[0]")?, vec![0]);
140 assert_eq!(parse_bounds("[9][1]")?, vec![9, 1]);
141 assert_eq!(parse_bounds("[0][0][0]")?, vec![0, 0, 0]);
142 assert_eq!(parse_bounds("[-1]")?, vec![-1]);
143 Ok(())
144 }
145
146 #[test]
147 fn a_two_dimensional_shape_multiplies_out_to_its_element_count() -> Result<(), crate::Error> {
148 let lower = parse_bounds("[0][0]")?;
151 let upper = parse_bounds("[9][1]")?;
152 let lengths: Vec<i32> = lower
153 .iter()
154 .zip(upper.iter())
155 .map(|(low, high)| high - low + 1)
156 .collect();
157 assert_eq!(lengths, vec![10, 2]);
158 assert_eq!(lengths.iter().product::<i32>(), 20);
159 Ok(())
160 }
161
162 #[test]
163 fn an_empty_array_has_a_dimension_of_length_zero() -> Result<(), crate::Error> {
164 assert_eq!(parse_bounds("[0]")?, vec![0]);
166 assert!(parse_bounds("[]")?.is_empty());
167 Ok(())
168 }
169
170 #[test]
171 fn a_non_array_reports_no_bounds() -> Result<(), crate::Error> {
172 assert!(parse_bounds("")?.is_empty());
175 assert!(parse_bounds("[]")?.is_empty());
176 Ok(())
177 }
178
179 #[test]
180 fn a_malformed_bound_is_reported_not_guessed() {
181 assert!(parse_bounds("[0,oops]").is_err());
182 }
183}