labview_interop/types/array/
dimensions.rs

1use crate::errors::{InternalError, LVInteropError};
2
3#[repr(transparent)]
4#[derive(Debug, Copy, Clone, PartialEq, Eq)]
5pub struct LVArrayDims<const D: usize>([i32; D]);
6
7impl<const D: usize> LVArrayDims<D> {
8    pub fn new_empty() -> Self {
9        Self([0; D])
10    }
11
12    pub fn shape(&self) -> [i32; D] {
13        self.0
14    }
15    pub fn element_count(&self) -> usize {
16        self.0.iter().fold(1, |size, dim| size * *dim as usize)
17    }
18}
19
20impl<const D: usize> From<[i32; D]> for LVArrayDims<D> {
21    fn from(dim_sizes: [i32; D]) -> Self {
22        Self(dim_sizes)
23    }
24}
25
26impl<const D: usize> TryFrom<&[usize; D]> for LVArrayDims<D> {
27    type Error = LVInteropError;
28
29    fn try_from(value: &[usize; D]) -> Result<Self, Self::Error> {
30        let mut dimensions = [0i32; D];
31
32        for (into, &from) in dimensions.iter_mut().zip(value.iter()) {
33            *into = from
34                .try_into()
35                .map_err(|_| LVInteropError::from(InternalError::ArrayDimensionsOutOfRange))?
36        }
37        Ok(dimensions.into())
38    }
39}
40
41impl<const D: usize> TryFrom<&[usize]> for LVArrayDims<D> {
42    type Error = LVInteropError;
43
44    fn try_from(value: &[usize]) -> Result<Self, Self::Error> {
45        let array: &[usize; D] = value
46            .try_into()
47            .map_err(|_| LVInteropError::from(InternalError::ArrayDimensionMismatch))?;
48        array.try_into()
49    }
50}
51
52impl<const D: usize> From<LVArrayDims<D>> for [usize; D] {
53    /// Convert to the usize version. Panics if any dimension is less than zero.
54    fn from(value: LVArrayDims<D>) -> Self {
55        let mut usize_values = [0usize; D];
56        for (output, input) in usize_values.iter_mut().zip(value.0.iter()) {
57            *output = (*input).try_into().expect("Negative dimension size.");
58        }
59        usize_values
60    }
61}
62
63/// Implement named methods for the first dimensions.
64impl LVArrayDims<2> {
65    pub fn rows(&self) -> i32 {
66        self.0[0]
67    }
68
69    pub fn columns(&self) -> i32 {
70        self.0[1]
71    }
72}
73
74impl LVArrayDims<3> {
75    pub fn rows(&self) -> i32 {
76        self.0[1]
77    }
78
79    pub fn columns(&self) -> i32 {
80        self.0[2]
81    }
82
83    pub fn pages(&self) -> i32 {
84        self.0[0]
85    }
86}
87
88#[cfg(test)]
89mod tests {
90    use super::*;
91
92    #[test]
93    fn new_dims_empty() {
94        let ref_dims = LVArrayDims::<3>([0, 0, 0]);
95        let dims = LVArrayDims::<3>::new_empty();
96        assert_eq!(dims, ref_dims);
97        assert_eq!(dims.element_count(), 0);
98    }
99
100    #[test]
101    fn dimension_element_count() {
102        let dims = LVArrayDims::<3>([2, 3, 4]);
103        assert_eq!(dims.element_count(), 24);
104
105        let dims = LVArrayDims::<2>([2, 3]);
106        assert_eq!(dims.element_count(), 6);
107
108        let dims = LVArrayDims::<1>([2]);
109        assert_eq!(dims.element_count(), 2);
110    }
111
112    #[test]
113    fn test_dim_equality() {
114        let dims1 = LVArrayDims::<3>([2, 3, 4]);
115        let dims2 = LVArrayDims::<3>([2, 3, 4]);
116        assert_eq!(dims1, dims2);
117    }
118
119    #[test]
120    fn test_dims_from_usize_ok() {
121        let dims = &[1usize, 2usize];
122        let lvdims: LVArrayDims<2> = dims.try_into().unwrap();
123        assert_eq!(lvdims, [1i32, 2].into())
124    }
125
126    #[test]
127    fn test_dims_from_usize_out_of_range() {
128        let dims = &[1usize, i32::MAX as usize + 1];
129        let result: Result<LVArrayDims<2>, _> = dims.try_into();
130
131        let _expected_err: Result<LVArrayDims<2>, _> = Err(LVInteropError::from(
132            InternalError::ArrayDimensionsOutOfRange,
133        ));
134        assert!(matches!(result, _expected_err));
135    }
136
137    #[test]
138    fn test_access_dims() {
139        let dims = LVArrayDims::<2>([2, 3]);
140        assert_eq!(dims.shape(), [2, 3]);
141    }
142
143    #[test]
144    fn test_2d_dim_names() {
145        let dims = LVArrayDims::<2>([2, 3]);
146        assert_eq!(dims.rows(), 2);
147        assert_eq!(dims.columns(), 3);
148    }
149
150    #[test]
151    fn test_3d_dim_names() {
152        let dims = LVArrayDims::<3>([2, 3, 4]);
153        assert_eq!(dims.rows(), 3);
154        assert_eq!(dims.columns(), 4);
155        assert_eq!(dims.pages(), 2);
156    }
157}