1use std::fmt;
2
3use crate::{Error, FunctionStatus, Result, UnitNumber};
4
5#[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 pub const fn new() -> Self {
16 Self {
17 unit_number: UnitNumber::new(),
18 function_status: FunctionStatus::new(),
19 }
20 }
21
22 pub const fn unit_number(&self) -> UnitNumber {
24 self.unit_number
25 }
26
27 pub fn set_unit_number(&mut self, val: UnitNumber) {
29 self.unit_number = val;
30 }
31
32 pub fn with_unit_number(mut self, val: UnitNumber) -> Self {
34 self.set_unit_number(val);
35 self
36 }
37
38 pub const fn function_status(&self) -> FunctionStatus {
40 self.function_status
41 }
42
43 pub fn set_function_status(&mut self, val: FunctionStatus) {
45 self.function_status = val;
46 }
47
48 pub fn with_function_status(mut self, val: FunctionStatus) -> Self {
50 self.set_function_status(val);
51 self
52 }
53
54 pub const fn len() -> usize {
56 UnitNumber::len() + FunctionStatus::len()
57 }
58
59 pub const fn is_empty(&self) -> bool {
61 self.unit_number.is_empty() && self.function_status.is_empty()
62 }
63
64 pub const fn is_valid(&self) -> bool {
66 self.unit_number.is_valid() && self.function_status.is_valid()
67 }
68
69 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 pub const fn to_bytes(&self) -> [u8; 2] {
86 [self.unit_number.to_u8(), self.function_status.to_u8()]
87 }
88
89 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#[repr(C)]
146#[derive(Clone, Debug, Eq, PartialEq)]
147pub struct UnitStatusList(pub Vec<UnitStatus>);
148
149impl UnitStatusList {
150 pub const fn new() -> Self {
152 Self(Vec::new())
153 }
154
155 pub fn items(&self) -> &[UnitStatus] {
157 self.0.as_ref()
158 }
159
160 pub fn into_inner(self) -> Vec<UnitStatus> {
162 self.0
163 }
164
165 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 pub fn as_bytes(&self) -> Vec<u8> {
183 self.0.iter().flat_map(|c| c.to_bytes()).collect()
184 }
185
186 pub fn into_bytes(self) -> Vec<u8> {
188 self.0.into_iter().flat_map(|c| c.into_bytes()).collect()
189 }
190
191 pub fn len(&self) -> usize {
193 self.0.len().saturating_mul(UnitStatus::len())
194 }
195
196 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}