1use byteorder::ByteOrder;
18use nom::bytes::complete::*;
19use nom::character::is_digit;
20use nom::combinator::map_res;
21use nom::sequence::tuple;
22use nom::IResult;
23use serde::{Deserialize, Serialize};
24use std::{
25 io::{Error, ErrorKind, Result},
26 str::FromStr,
27};
28
29#[derive(Debug, PartialEq, Hash, Eq, Clone, Serialize, Deserialize)]
31pub struct PnSimple {
32 kind: u16,
33 hw_kind: u8,
34 hw_id: u16,
35 rel: u8,
36}
37
38#[derive(Debug, PartialEq, Hash, Eq, Clone, Serialize, Deserialize)]
40pub enum PartNumber {
42 Simple(PnSimple),
44}
45impl PartNumber {
46 fn parse(input: &[u8]) -> IResult<&[u8], Self> {
47 let sep = tag(b"-");
49 let is_kind = take_while_m_n(3, 3, is_digit);
50 let hw_kind = take(1usize);
51 let is_hw_id = take_while_m_n(4, 4, is_digit);
52 let hw_id = map_res(is_hw_id, |x: &[u8]| {
53 u16::from_str(&String::from_utf8_lossy(x))
54 });
55 let is_rel = take_while_m_n(2, 2, is_digit);
56
57 let (input, kind) = map_res(is_kind, |x: &[u8]| {
58 u16::from_str(&String::from_utf8_lossy(x))
59 })(input)?;
60 let (input, _) = sep(input)?;
61 let (input, (hw_kind, hw_id)) = tuple((hw_kind, hw_id))(input)?;
62 let (input, _) = sep(input)?;
63 let (input, rel) = map_res(is_rel, |x: &[u8]| {
64 u8::from_str(&String::from_utf8_lossy(x))
65 })(input)?;
66
67 Ok((
68 input,
69 PartNumber::Simple(PnSimple {
70 kind,
71 hw_kind: hw_kind[0] - b'0',
72 hw_id,
73 rel,
74 }),
75 ))
76 }
77
78 pub fn from_raw<B: ByteOrder>(x: &[u8]) -> Result<(&[u8], PartNumber)> {
79 if x.len() < 9 {
80 Err(Error::new(
81 ErrorKind::InvalidData,
82 "Part number buffer too small",
83 ))
84 } else {
85 const fn base6(x: u128, byte: u8) -> u8 {
86 (((x & (0b111111 << (6 * byte))) >> (6 * byte)) & 0xffu128)
87 as u8
88 }
89 const fn get_value(x: u128) -> [u8; 12] {
90 let (mut ret, mut i) = ([0; 12], 0);
91 while i < 12 {
92 ret[i] = base6(x, 11 - i as u8).wrapping_add(0x20);
93 i += 1;
94 }
95 ret
96 }
97 let num = B::read_uint128(x, 9);
98 let buff = get_value(num);
99 let (_, ret) = PartNumber::parse(&buff).map_err(|_| {
100 Error::new(ErrorKind::InvalidData, "Unable to parse PartNumber")
101 })?;
102 Ok((&x[9..], ret))
103 }
104 }
105
106 pub fn from_str(s: &str) -> Result<Self> {
107 let bytes = s.as_bytes();
108 if bytes.len() < 12 {
109 return Err(Error::new(
110 ErrorKind::InvalidData,
111 "PartNumber Invalid size",
112 ));
113 }
114 let (_, ret) = PartNumber::parse(s.as_bytes()).map_err(|_| {
115 Error::new(ErrorKind::InvalidData, "Unable to parse PartNumber")
116 })?;
117 Ok(ret)
118 }
119}
120
121impl ToString for PartNumber {
122 fn to_string(&self) -> String {
123 match self {
124 PartNumber::Simple(PnSimple {
125 kind,
126 hw_kind,
127 hw_id,
128 rel,
129 }) => format!("{:03}-{}{:04}-{:02}", kind, hw_kind, hw_id, rel),
130 }
131 }
132}
133
134#[cfg(test)]
135mod tests {
136 use crate::PartNumber;
137
138 #[test]
140 fn part_number_from_bytes() {
141 let bytes_little: Vec<u8> =
142 vec![0x10, 0xD4, 0x5C, 0x13, 0x04, 0x45, 0x0D, 0x14, 0x41];
143 let bytes_big: Vec<u8> = bytes_little.iter().rev().copied().collect();
144
145 let (_, pn_little) =
146 PartNumber::from_raw::<byteorder::LE>(&bytes_little).unwrap();
147 let (_, pn_big) =
148 PartNumber::from_raw::<byteorder::BE>(&bytes_big).unwrap();
149
150 for pn in [pn_little, pn_big].iter() {
151 assert_eq!(pn.to_string(), "010-10037-00");
152 }
153 }
154
155 #[test]
157 #[should_panic]
158 fn part_number_invalid_str1() {
159 let text = "010-รง0037-00";
160 PartNumber::from_str(text).unwrap();
161 }
162 #[test]
164 #[should_panic]
165 fn part_number_invalid_str2() {
166 let text = "010-0037-00";
167 PartNumber::from_str(text).unwrap();
168 }
169}