jcm/
unit_status.rs

1use std::fmt;
2
3use crate::{Error, FunctionStatus, Result, UnitNumber};
4
5/// Represents the status of a JCM device unit, e.g. `Acceptor`, `Stacker`, `Recycler`, etc.
6#[repr(C)]
7#[derive(Clone, Copy, Debug, Eq, PartialEq)]
8pub struct UnitStatus {
9    unit_number: UnitNumber,
10    function_status: FunctionStatus,
11}
12
13impl UnitStatus {
14    /// Creates a new [UnitStatus].
15    pub const fn new() -> Self {
16        Self {
17            unit_number: UnitNumber::new(),
18            function_status: FunctionStatus::new(),
19        }
20    }
21
22    /// Gets the [UnitNumber] of the [UnitStatus].
23    pub const fn unit_number(&self) -> UnitNumber {
24        self.unit_number
25    }
26
27    /// Sets the [UnitNumber] of the [UnitStatus].
28    pub fn set_unit_number(&mut self, val: UnitNumber) {
29        self.unit_number = val;
30    }
31
32    /// Builder function that sets the [UnitNumber] of the [UnitStatus].
33    pub fn with_unit_number(mut self, val: UnitNumber) -> Self {
34        self.set_unit_number(val);
35        self
36    }
37
38    /// Gets the [FunctionStatus] of the [UnitStatus].
39    pub const fn function_status(&self) -> FunctionStatus {
40        self.function_status
41    }
42
43    /// Sets the [FunctionStatus] of the [UnitStatus].
44    pub fn set_function_status(&mut self, val: FunctionStatus) {
45        self.function_status = val;
46    }
47
48    /// Builder function that sets the [FunctionStatus] of the [UnitStatus].
49    pub fn with_function_status(mut self, val: FunctionStatus) -> Self {
50        self.set_function_status(val);
51        self
52    }
53
54    /// Gets the length of the [UnitStatus].
55    pub const fn len() -> usize {
56        UnitNumber::len() + FunctionStatus::len()
57    }
58
59    /// Gets whether the [UnitStatus] is empty.
60    pub const fn is_empty(&self) -> bool {
61        self.unit_number.is_empty() && self.function_status.is_empty()
62    }
63
64    /// Gets whether the [UnitStatus] is valid.
65    pub const fn is_valid(&self) -> bool {
66        self.unit_number.is_valid() && self.function_status.is_valid()
67    }
68
69    /// Infallible function that converts a byte array into a [UnitStatus].
70    pub const fn from_bytes(val: &[u8]) -> Self {
71        match val.len() {
72            0 => Self::new(),
73            1 => Self {
74                unit_number: UnitNumber::from_u8(val[0]),
75                function_status: FunctionStatus::new(),
76            },
77            _ => Self {
78                unit_number: UnitNumber::from_u8(val[0]),
79                function_status: FunctionStatus::from_u8(val[1]),
80            },
81        }
82    }
83
84    /// Infallible function that converts a [UnitStatus] into a byte array.
85    pub const fn to_bytes(&self) -> [u8; 2] {
86        [self.unit_number.to_u8(), self.function_status.to_u8()]
87    }
88
89    /// Infallible function that converts a [UnitStatus] into a byte array.
90    pub const fn into_bytes(self) -> [u8; 2] {
91        [self.unit_number.into_u8(), self.function_status.into_u8()]
92    }
93}
94
95impl TryFrom<&[u8]> for UnitStatus {
96    type Error = Error;
97
98    fn try_from(val: &[u8]) -> Result<UnitStatus> {
99        let len = Self::len();
100        let val_len = val.len();
101
102        if val_len < len {
103            Err(Error::InvalidUnitStatusLen((val_len, len)))
104        } else {
105            Ok(Self {
106                unit_number: UnitNumber::try_from(val[0])?,
107                function_status: FunctionStatus::try_from(val[1])?,
108            })
109        }
110    }
111}
112
113impl<const N: usize> TryFrom<&[u8; N]> for UnitStatus {
114    type Error = Error;
115
116    fn try_from(val: &[u8; N]) -> Result<UnitStatus> {
117        val.as_ref().try_into()
118    }
119}
120
121impl<const N: usize> TryFrom<[u8; N]> for UnitStatus {
122    type Error = Error;
123
124    fn try_from(val: [u8; N]) -> Result<UnitStatus> {
125        val.as_ref().try_into()
126    }
127}
128
129impl Default for UnitStatus {
130    fn default() -> Self {
131        Self::new()
132    }
133}
134
135impl fmt::Display for UnitStatus {
136    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
137        write!(f, "{{")?;
138        write!(f, r#""unit_number": {}, "#, self.unit_number)?;
139        write!(f, r#""function_status": {}"#, self.function_status)?;
140        write!(f, "}}")
141    }
142}
143
144/// Convenience container for a list of [UnitStatus] items.
145#[repr(C)]
146#[derive(Clone, Debug, Eq, PartialEq)]
147pub struct UnitStatusList(pub Vec<UnitStatus>);
148
149impl UnitStatusList {
150    /// Creates a new [UnitStatusList].
151    pub const fn new() -> Self {
152        Self(Vec::new())
153    }
154
155    /// Gets a reference to the list of [UnitStatus] items.
156    pub fn items(&self) -> &[UnitStatus] {
157        self.0.as_ref()
158    }
159
160    /// Converts the [UnitStatusList] into its inner representation.
161    pub fn into_inner(self) -> Vec<UnitStatus> {
162        self.0
163    }
164
165    /// Writes the [UnitStatusList] to a byte buffer.
166    pub fn to_bytes(&self, buf: &mut [u8]) -> Result<()> {
167        let len = self.len();
168        let buf_len = buf.len();
169
170        if buf_len < len {
171            Err(Error::InvalidUnitStatusListLen((buf_len, len)))
172        } else {
173            buf.iter_mut()
174                .zip(self.0.iter().flat_map(|c| c.to_bytes()))
175                .for_each(|(dst, src)| *dst = src);
176
177            Ok(())
178        }
179    }
180
181    /// Converts the [UnitStatusList] into a byte vector.
182    pub fn as_bytes(&self) -> Vec<u8> {
183        self.0.iter().flat_map(|c| c.to_bytes()).collect()
184    }
185
186    /// Converts the [UnitStatusList] into a byte vector.
187    pub fn into_bytes(self) -> Vec<u8> {
188        self.0.into_iter().flat_map(|c| c.into_bytes()).collect()
189    }
190
191    /// Gets the byte length of the [UnitStatusList].
192    pub fn len(&self) -> usize {
193        self.0.len().saturating_mul(UnitStatus::len())
194    }
195
196    /// Gets whether the [UnitStatusList] is empty.
197    pub fn is_empty(&self) -> bool {
198        self.0.is_empty() || self.0.iter().all(|u| u.is_empty())
199    }
200}
201
202impl TryFrom<&[u8]> for UnitStatusList {
203    type Error = Error;
204
205    fn try_from(val: &[u8]) -> Result<Self> {
206        let unit_len = UnitStatus::len();
207        let val_len = val.len();
208
209        if val_len % unit_len == 0 {
210            Ok(Self(
211                val.chunks_exact(UnitStatus::len())
212                    .filter_map(|c| match UnitStatus::try_from(c) {
213                        Ok(u) => Some(u),
214                        Err(err) => {
215                            log::warn!("invalid UnitStatus: {err}");
216                            None
217                        }
218                    })
219                    .collect(),
220            ))
221        } else {
222            Err(Error::InvalidUnitStatusLen((val_len, unit_len)))
223        }
224    }
225}
226
227impl fmt::Display for UnitStatusList {
228    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
229        write!(f, "[")?;
230        for (i, unit) in self.0.iter().enumerate() {
231            if i != 0 {
232                write!(f, ", ")?;
233            }
234            write!(f, "{unit}")?;
235        }
236        write!(f, "]")
237    }
238}
239
240impl Default for UnitStatusList {
241    fn default() -> Self {
242        Self::new()
243    }
244}
245
246#[cfg(test)]
247mod tests {
248    use super::*;
249
250    #[test]
251    fn test_unit_status() -> Result<()> {
252        const FUNC_STATS: [FunctionStatus; 9] = [
253            FunctionStatus::Normal,
254            FunctionStatus::NearFull,
255            FunctionStatus::Full,
256            FunctionStatus::BoxRemoved,
257            FunctionStatus::JamAcceptor,
258            FunctionStatus::JamStacker,
259            FunctionStatus::Cheat,
260            FunctionStatus::UnitRemoved,
261            FunctionStatus::Failure,
262        ];
263
264        assert!(UnitStatus::try_from(&[]).is_err());
265        assert!(UnitStatus::try_from(&[0u8]).is_err());
266        assert!(UnitStatus::try_from(&[0u8, 0u8]).is_err());
267        assert!(UnitStatus::try_from(&[1u8, 0u8]).is_ok());
268        assert!(UnitStatus::try_from(&[1u8, 0u8, 0u8]).is_ok());
269
270        let func_vals = FUNC_STATS.map(|f| f.to_u8());
271        for num in 0x1..=0xf {
272            for (i, func) in func_vals.into_iter().enumerate() {
273                let raw = [num, func];
274                let status = UnitStatus::try_from(&raw)?;
275                let exp = UnitStatus::new()
276                    .with_unit_number(UnitNumber::from_u8(num))
277                    .with_function_status(FUNC_STATS[i]);
278
279                assert_eq!(status.to_bytes(), raw);
280                assert_eq!(status, exp);
281
282                assert!(!status.is_empty());
283                assert!(status.is_valid());
284            }
285        }
286
287        Ok(())
288    }
289}