1use serde::{ser::SerializeSeq, Serialize, Serializer};
2use std::error;
3use std::{fmt, string::FromUtf8Error};
4
5pub struct SMBiosStringSet {
9 strings: Vec<Vec<u8>>,
10 current_string_index: usize,
11}
12
13impl SMBiosStringSet {
14 pub fn new(string_area: Vec<u8>) -> SMBiosStringSet {
16 SMBiosStringSet {
17 strings: {
18 if string_area == &[] {
19 vec![]
20 } else {
21 string_area
22 .split(|num| *num == 0)
23 .into_iter()
24 .map(|string_slice| string_slice.to_vec())
25 .collect()
26 }
27 },
28 current_string_index: 0,
29 }
30 }
31
32 fn reset(&mut self) {
33 self.current_string_index = 0;
34 }
35
36 pub fn get_string(&self, index: u8) -> SMBiosString {
42 let index_usize = index as usize;
43
44 SMBiosString {
60 value: match index_usize == 0 {
61 true => Ok(String::new()),
62 false => match index_usize <= self.strings.len() {
63 true => String::from_utf8(self.strings[index_usize - 1].clone())
64 .map_err(|err| err.into()),
65 false => Err(SMBiosStringError::InvalidStringNumber(index)),
66 },
67 },
68 }
69 }
70
71 pub fn iter(&self) -> std::slice::Iter<'_, Vec<u8>> {
73 self.strings.iter()
74 }
75}
76
77impl Iterator for SMBiosStringSet {
78 type Item = SMBiosString;
79
80 fn next(&mut self) -> Option<Self::Item> {
81 if self.current_string_index == self.strings.len() {
82 self.reset();
83 return None;
84 }
85
86 let result = String::from_utf8(self.strings[self.current_string_index].clone())
87 .map_err(|err| err.into());
88
89 self.current_string_index = self.current_string_index + 1;
90
91 Some(SMBiosString::from(result))
92 }
93}
94
95impl IntoIterator for &SMBiosStringSet {
96 type Item = SMBiosString;
97 type IntoIter = SMBiosStringSet;
98
99 fn into_iter(self) -> Self::IntoIter {
100 SMBiosStringSet {
101 strings: self.strings.clone(),
102 current_string_index: 0,
103 }
104 }
105}
106
107impl fmt::Debug for SMBiosStringSet {
108 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
109 fmt.debug_list().entries(self.into_iter()).finish()
110 }
111}
112
113impl Serialize for SMBiosStringSet {
114 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
115 where
116 S: Serializer,
117 {
118 let strings: Vec<SMBiosString> = self.into_iter().collect();
119 let mut seq = serializer.serialize_seq(Some(strings.len()))?;
120 for e in strings {
121 match e.value {
122 Ok(val) => seq.serialize_element(&val)?,
123 Err(err) => seq.serialize_element(format!("{}", err).as_str())?,
124 }
125 }
126 seq.end()
127 }
128}
129
130#[derive(Serialize, Debug)]
134pub enum SMBiosStringError {
135 FieldOutOfBounds,
137 InvalidStringNumber(u8),
139 #[serde(serialize_with = "ser_from_utf8_error")]
141 Utf8(FromUtf8Error),
142}
143
144fn ser_from_utf8_error<S>(data: &FromUtf8Error, serializer: S) -> Result<S::Ok, S::Error>
145where
146 S: Serializer,
147{
148 serializer.serialize_str(format!("{}", data).as_str())
149}
150
151impl fmt::Display for SMBiosStringError {
152 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
153 match *self {
154 SMBiosStringError::FieldOutOfBounds => {
155 write!(
156 f,
157 "The structure's field is out of bounds of the formatted portion of the SMBIOS structure"
158 )
159 }
160 SMBiosStringError::InvalidStringNumber(_) => {
161 write!(
162 f,
163 "The given string number was outside the range of the SMBIOS structure's string-set"
164 )
165 }
166 SMBiosStringError::Utf8(..) => {
169 write!(f, "UTF8 parsing error")
170 }
171 }
172 }
173}
174
175impl error::Error for SMBiosStringError {
176 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
177 match *self {
178 SMBiosStringError::Utf8(ref e) => Some(e),
182 _ => None,
183 }
184 }
185}
186
187impl From<FromUtf8Error> for SMBiosStringError {
191 fn from(err: FromUtf8Error) -> SMBiosStringError {
192 SMBiosStringError::Utf8(err)
193 }
194}
195
196impl From<Result<String, SMBiosStringError>> for SMBiosString {
197 fn from(data: Result<String, SMBiosStringError>) -> Self {
198 SMBiosString { value: data }
199 }
200}
201
202pub struct SMBiosString {
206 value: Result<String, SMBiosStringError>,
207}
208
209impl SMBiosString {
210 pub fn to_utf8_lossy(&self) -> Option<String> {
213 match &self.value {
214 Ok(val) => Some(val.to_string()),
215 Err(err) => match err {
216 SMBiosStringError::Utf8(utf8) => {
217 Some(String::from_utf8_lossy(utf8.as_bytes()).to_string())
218 }
219 _ => None,
220 },
221 }
222 }
223
224 pub const fn is_ok(&self) -> bool {
226 self.value.is_ok()
227 }
228
229 pub const fn is_err(&self) -> bool {
231 self.value.is_err()
232 }
233
234 pub fn ok(self) -> Option<String> {
236 self.value.ok()
237 }
238
239 pub fn err(self) -> Option<SMBiosStringError> {
241 self.value.err()
242 }
243
244 pub const fn as_ref(&self) -> Result<&String, &SMBiosStringError> {
246 self.value.as_ref()
247 }
248
249 pub fn as_mut(&mut self) -> Result<&mut String, &mut SMBiosStringError> {
251 self.value.as_mut()
252 }
253}
254
255impl fmt::Display for SMBiosString {
256 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
257 match &self.value {
258 Ok(val) => write!(f, "{}", val),
259 Err(err) => write!(f, "{}", err),
260 }
261 }
262}
263
264impl fmt::Debug for SMBiosString {
265 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
266 match &self.value {
267 Ok(val) => write!(f, "{}", val),
268 Err(err) => write!(f, "{}", err),
269 }
270 }
271}
272
273impl Serialize for SMBiosString {
274 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
275 where
276 S: Serializer,
277 {
278 serializer.serialize_str(format!("{}", &self).as_str())
279 }
280}
281
282#[cfg(test)]
283mod tests {
284 use super::*;
285
286 #[test]
287 fn test_string_parsing() {
288 let string_set_bytes = vec![
289 0x65, 0x6E, 0x7C, 0x55, 0x53, 0x7C, 0x69, 0x73, 0x6F, 0x38, 0x38, 0x35, 0x39, 0x2D,
291 0x31, 0x00, b'H', b'e', b'a', b'r', b't', b'=', 240, 159, 146, 150, 0x00, b'E', b'r', b'r', b'o', b'r', b'=', 1, 159, 146, 150, 0x00,
294 0x6A, 0x61, 0x7C, 0x4A, 0x50, 0x7C, 0x75, 0x6E, 0x69, 0x63, 0x6F, 0x64, 0x65,
296 ];
297
298 let string_set = SMBiosStringSet::new(string_set_bytes);
299
300 let mut string_iterator = string_set.into_iter();
301
302 let first_string = string_iterator.next().unwrap().value.unwrap();
303 assert_eq!(first_string, "en|US|iso8859-1".to_string());
304
305 let second_string = string_iterator.next().unwrap().value.unwrap();
306 assert_eq!(second_string, "Heart=💖".to_string());
307
308 match string_iterator.next().unwrap().value {
310 Ok(_) => panic!("This should have been a UTF8 error"),
311 Err(err) => match err {
312 SMBiosStringError::FieldOutOfBounds => panic!("This should have been inbounds"),
313 SMBiosStringError::InvalidStringNumber(_) => {
314 panic!("This should have been a valid string number")
315 }
316 SMBiosStringError::Utf8(utf8) => {
317 assert_eq!(7, utf8.utf8_error().valid_up_to());
318 assert_eq!(
319 "Error=\u{1}���",
320 String::from_utf8_lossy(utf8.as_bytes()).to_string()
321 );
322 }
323 },
324 }
325
326 let fourth_string = string_iterator.next().unwrap().value.unwrap();
327 assert_eq!(fourth_string, "ja|JP|unicode".to_string());
328 }
329}