1use crate::parsers::ARG_SMBIOS;
2use bon::Builder;
3use proptest_derive::Arbitrary;
4use std::path::PathBuf;
5use std::str::FromStr;
6
7use crate::common::OnOff;
8use crate::parsers::DELIM_COMMA;
9use crate::to_command::{ToArg, ToCommand};
10
11#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Default, Builder, Arbitrary)]
13pub struct SmbiosFile {
14 path: PathBuf,
15}
16
17impl ToCommand for SmbiosFile {
18 fn to_args(&self) -> Vec<String> {
19 let mut args = vec![];
20 args.push(format!("file={}", self.path.display()));
21 args
22 }
23}
24
25impl FromStr for SmbiosFile {
26 type Err = String;
27
28 fn from_str(s: &str) -> Result<Self, Self::Err> {
29 let path = s.strip_prefix("file=").ok_or_else(|| format!("unsupported smbios file form: {s}"))?;
30 Ok(Self { path: PathBuf::from(path) })
31 }
32}
33#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Default, Builder, Arbitrary)]
37pub struct SmbiosType0 {
38 vendor: Option<String>,
39 version: Option<String>,
40 date: Option<String>,
41 release: Option<(usize, usize)>,
42 uefi: Option<OnOff>,
43}
44
45impl ToCommand for SmbiosType0 {
46 fn to_args(&self) -> Vec<String> {
47 let mut args = vec!["type=0".to_string()];
48 if let Some(vendor) = &self.vendor {
49 args.push(format!("vendor={}", vendor));
50 }
51 if let Some(version) = &self.version {
52 args.push(format!("version={}", version));
53 }
54 if let Some(date) = &self.date {
55 args.push(format!("date={}", date));
56 }
57 if let Some(release) = &self.release {
58 args.push(format!("release={}.{}", release.0, release.1));
59 }
60 if let Some(uefi) = &self.uefi {
61 args.push(format!("uefi={}", uefi.to_arg()));
62 }
63 vec![args.join(DELIM_COMMA)]
64 }
65}
66
67impl FromStr for SmbiosType0 {
68 type Err = String;
69
70 fn from_str(s: &str) -> Result<Self, Self::Err> {
71 let mut value = Self::default();
72 for part in s.split(',') {
73 let (key, raw) = part.split_once('=').ok_or_else(|| format!("invalid smbios type 0 option: {part}"))?;
74 match key {
75 "type" if raw == "0" => {}
76 "vendor" => value.vendor = Some(raw.to_string()),
77 "version" => value.version = Some(raw.to_string()),
78 "date" => value.date = Some(raw.to_string()),
79 "release" => {
80 let (major, minor) = raw.split_once('.').ok_or_else(|| format!("invalid release value: {raw}"))?;
81 value.release = Some((major.parse::<usize>().map_err(|e| e.to_string())?, minor.parse::<usize>().map_err(|e| e.to_string())?));
82 }
83 "uefi" => value.uefi = Some(raw.parse::<OnOff>().map_err(|_| format!("invalid uefi value: {raw}"))?),
84 other => return Err(format!("unsupported smbios type 0 option: {other}")),
85 }
86 }
87 Ok(value)
88 }
89}
90#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Default, Builder, Arbitrary)]
94pub struct SmbiosType1 {
95 manufacturer: Option<String>,
96 product: Option<String>,
97 version: Option<String>,
98 serial: Option<String>,
99 uuid: Option<String>,
100 sku: Option<String>,
101 family: Option<String>,
102}
103
104impl ToCommand for SmbiosType1 {
105 fn to_args(&self) -> Vec<String> {
106 let mut args = vec!["type=1".to_string()];
107 if let Some(manufacturer) = &self.manufacturer {
108 args.push(format!("manufacturer={}", manufacturer));
109 }
110 if let Some(product) = &self.product {
111 args.push(format!("product={}", product));
112 }
113 if let Some(version) = &self.version {
114 args.push(format!("version={}", version));
115 }
116 if let Some(serial) = &self.serial {
117 args.push(format!("serial={}", serial));
118 }
119 if let Some(uuid) = &self.uuid {
120 args.push(format!("uuid={}", uuid));
121 }
122 if let Some(sku) = &self.sku {
123 args.push(format!("sku={}", sku));
124 }
125 if let Some(family) = &self.family {
126 args.push(format!("family={}", family));
127 }
128 vec![args.join(DELIM_COMMA)]
129 }
130}
131
132impl FromStr for SmbiosType1 {
133 type Err = String;
134
135 fn from_str(s: &str) -> Result<Self, Self::Err> {
136 let mut value = Self::default();
137 for part in s.split(',') {
138 let (key, raw) = part.split_once('=').ok_or_else(|| format!("invalid smbios type 1 option: {part}"))?;
139 match key {
140 "type" if raw == "1" => {}
141 "manufacturer" => value.manufacturer = Some(raw.to_string()),
142 "product" => value.product = Some(raw.to_string()),
143 "version" => value.version = Some(raw.to_string()),
144 "serial" => value.serial = Some(raw.to_string()),
145 "uuid" => value.uuid = Some(raw.to_string()),
146 "sku" => value.sku = Some(raw.to_string()),
147 "family" => value.family = Some(raw.to_string()),
148 other => return Err(format!("unsupported smbios type 1 option: {other}")),
149 }
150 }
151 Ok(value)
152 }
153}
154
155#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Default, Builder, Arbitrary)]
159pub struct SmbiosType2 {
160 manufacturer: Option<String>,
161 product: Option<String>,
162 version: Option<String>,
163 serial: Option<String>,
164 asset: Option<String>,
165 location: Option<String>,
166}
167
168impl ToCommand for SmbiosType2 {
169 fn to_args(&self) -> Vec<String> {
170 let mut args = vec!["type=2".to_string()];
171 if let Some(manufacturer) = &self.manufacturer {
172 args.push(format!("manufacturer={}", manufacturer));
173 }
174 if let Some(product) = &self.product {
175 args.push(format!("product={}", product));
176 }
177 if let Some(version) = &self.version {
178 args.push(format!("version={}", version));
179 }
180 if let Some(serial) = &self.serial {
181 args.push(format!("serial={}", serial));
182 }
183 if let Some(asset) = &self.asset {
184 args.push(format!("asset={}", asset));
185 }
186 if let Some(location) = &self.location {
187 args.push(format!("location={}", location));
188 }
189 vec![args.join(DELIM_COMMA)]
190 }
191}
192
193impl FromStr for SmbiosType2 {
194 type Err = String;
195
196 fn from_str(s: &str) -> Result<Self, Self::Err> {
197 let mut value = Self::default();
198 for part in s.split(',') {
199 let (key, raw) = part.split_once('=').ok_or_else(|| format!("invalid smbios type 2 option: {part}"))?;
200 match key {
201 "type" if raw == "2" => {}
202 "manufacturer" => value.manufacturer = Some(raw.to_string()),
203 "product" => value.product = Some(raw.to_string()),
204 "version" => value.version = Some(raw.to_string()),
205 "serial" => value.serial = Some(raw.to_string()),
206 "asset" => value.asset = Some(raw.to_string()),
207 "location" => value.location = Some(raw.to_string()),
208 other => return Err(format!("unsupported smbios type 2 option: {other}")),
209 }
210 }
211 Ok(value)
212 }
213}
214
215#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Default, Builder, Arbitrary)]
217pub struct SmbiosType3 {
218 manufacturer: Option<String>,
219 version: Option<String>,
220 serial: Option<String>,
221 asset: Option<String>,
222 sku: Option<String>,
223}
224
225impl ToCommand for SmbiosType3 {
226 fn to_args(&self) -> Vec<String> {
227 let mut args = vec!["type=3".to_string()];
228 if let Some(manufacturer) = &self.manufacturer {
229 args.push(format!("manufacturer={}", manufacturer));
230 }
231 if let Some(version) = &self.version {
232 args.push(format!("version={}", version));
233 }
234 if let Some(serial) = &self.serial {
235 args.push(format!("serial={}", serial));
236 }
237 if let Some(asset) = &self.asset {
238 args.push(format!("asset={}", asset));
239 }
240 if let Some(sku) = &self.sku {
241 args.push(format!("sku={}", sku));
242 }
243 vec![args.join(DELIM_COMMA)]
244 }
245}
246
247impl FromStr for SmbiosType3 {
248 type Err = String;
249
250 fn from_str(s: &str) -> Result<Self, Self::Err> {
251 let mut value = Self::default();
252 for part in s.split(',') {
253 let (key, raw) = part.split_once('=').ok_or_else(|| format!("invalid smbios type 3 option: {part}"))?;
254 match key {
255 "type" if raw == "3" => {}
256 "manufacturer" => value.manufacturer = Some(raw.to_string()),
257 "version" => value.version = Some(raw.to_string()),
258 "serial" => value.serial = Some(raw.to_string()),
259 "asset" => value.asset = Some(raw.to_string()),
260 "sku" => value.sku = Some(raw.to_string()),
261 other => return Err(format!("unsupported smbios type 3 option: {other}")),
262 }
263 }
264 Ok(value)
265 }
266}
267
268#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Default, Builder, Arbitrary)]
270pub struct SmbiosType4 {
271 sock_pfx: Option<String>,
272 manufacturer: Option<String>,
273 version: Option<String>,
274 serial: Option<String>,
275 asset: Option<String>,
276 part: Option<String>,
277 max_speed: Option<usize>,
278 current_speed: Option<usize>,
279 processor_family: Option<usize>,
280 processor_id: Option<usize>,
281}
282
283impl ToCommand for SmbiosType4 {
284 fn to_args(&self) -> Vec<String> {
285 let mut args = vec!["type=4".to_string()];
286 if let Some(sock_pfx) = &self.sock_pfx {
287 args.push(format!("sock_pfx={}", sock_pfx));
288 }
289 if let Some(manufacturer) = &self.manufacturer {
290 args.push(format!("manufacturer={}", manufacturer));
291 }
292 if let Some(version) = &self.version {
293 args.push(format!("version={}", version));
294 }
295 if let Some(serial) = &self.serial {
296 args.push(format!("serial={}", serial));
297 }
298 if let Some(asset) = &self.asset {
299 args.push(format!("asset={}", asset));
300 }
301 if let Some(part) = &self.part {
302 args.push(format!("part={}", part));
303 }
304 if let Some(max_speed) = &self.max_speed {
305 args.push(format!("max-speed={}", max_speed));
306 }
307 if let Some(current_speed) = &self.current_speed {
308 args.push(format!("current-speed={}", current_speed));
309 }
310 if let Some(processor_family) = &self.processor_family {
311 args.push(format!("processor-family={}", processor_family));
312 }
313 if let Some(processor_id) = &self.processor_id {
314 args.push(format!("processor-id={}", processor_id));
315 }
316 vec![args.join(DELIM_COMMA)]
317 }
318}
319
320impl FromStr for SmbiosType4 {
321 type Err = String;
322
323 fn from_str(s: &str) -> Result<Self, Self::Err> {
324 let mut value = Self::default();
325 for part in s.split(',') {
326 let (key, raw) = part.split_once('=').ok_or_else(|| format!("invalid smbios type 4 option: {part}"))?;
327 match key {
328 "type" if raw == "4" => {}
329 "sock_pfx" => value.sock_pfx = Some(raw.to_string()),
330 "manufacturer" => value.manufacturer = Some(raw.to_string()),
331 "version" => value.version = Some(raw.to_string()),
332 "serial" => value.serial = Some(raw.to_string()),
333 "asset" => value.asset = Some(raw.to_string()),
334 "part" => value.part = Some(raw.to_string()),
335 "max-speed" => value.max_speed = Some(raw.parse::<usize>().map_err(|e| e.to_string())?),
336 "current-speed" => value.current_speed = Some(raw.parse::<usize>().map_err(|e| e.to_string())?),
337 "processor-family" => value.processor_family = Some(raw.parse::<usize>().map_err(|e| e.to_string())?),
338 "processor-id" => value.processor_id = Some(raw.parse::<usize>().map_err(|e| e.to_string())?),
339 other => return Err(format!("unsupported smbios type 4 option: {other}")),
340 }
341 }
342 Ok(value)
343 }
344}
345
346#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Default, Builder, Arbitrary)]
348pub struct SmbiosType8 {
349 external_reference: Option<String>,
350 internal_reference: Option<String>,
351 connector_type: Option<usize>,
352 port_type: Option<usize>,
353}
354
355impl ToCommand for SmbiosType8 {
356 fn to_args(&self) -> Vec<String> {
357 let mut args = vec!["type=8".to_string()];
358 if let Some(external_reference) = &self.external_reference {
359 args.push(format!("external_reference={}", external_reference));
360 }
361 if let Some(internal_reference) = &self.internal_reference {
362 args.push(format!("internal_reference={}", internal_reference));
363 }
364 if let Some(connector_type) = &self.connector_type {
365 args.push(format!("connector_type={}", connector_type));
366 }
367 if let Some(port_type) = &self.port_type {
368 args.push(format!("port_type={}", port_type));
369 }
370 vec![args.join(DELIM_COMMA)]
371 }
372}
373
374impl FromStr for SmbiosType8 {
375 type Err = String;
376
377 fn from_str(s: &str) -> Result<Self, Self::Err> {
378 let mut value = Self::default();
379 for part in s.split(',') {
380 let (key, raw) = part.split_once('=').ok_or_else(|| format!("invalid smbios type 8 option: {part}"))?;
381 match key {
382 "type" if raw == "8" => {}
383 "external_reference" => value.external_reference = Some(raw.to_string()),
384 "internal_reference" => value.internal_reference = Some(raw.to_string()),
385 "connector_type" => value.connector_type = Some(raw.parse::<usize>().map_err(|e| e.to_string())?),
386 "port_type" => value.port_type = Some(raw.parse::<usize>().map_err(|e| e.to_string())?),
387 other => return Err(format!("unsupported smbios type 8 option: {other}")),
388 }
389 }
390 Ok(value)
391 }
392}
393
394#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Default, Builder, Arbitrary)]
396pub struct SmbiosType11 {
397 value: Option<String>,
398 path: Option<String>,
399}
400
401impl ToCommand for SmbiosType11 {
402 fn to_args(&self) -> Vec<String> {
403 let mut args = vec!["type=11".to_string()];
404 if let Some(value) = &self.value {
405 args.push(format!("value={}", value));
406 }
407 if let Some(path) = &self.path {
408 args.push(format!("path={}", path));
409 }
410 vec![args.join(DELIM_COMMA)]
411 }
412}
413
414impl FromStr for SmbiosType11 {
415 type Err = String;
416
417 fn from_str(s: &str) -> Result<Self, Self::Err> {
418 let mut value = Self::default();
419 for part in s.split(',') {
420 let (key, raw) = part.split_once('=').ok_or_else(|| format!("invalid smbios type 11 option: {part}"))?;
421 match key {
422 "type" if raw == "11" => {}
423 "value" => value.value = Some(raw.to_string()),
424 "path" => value.path = Some(raw.to_string()),
425 other => return Err(format!("unsupported smbios type 11 option: {other}")),
426 }
427 }
428 Ok(value)
429 }
430}
431
432#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Default, Builder, Arbitrary)]
434pub struct SmbiosType17 {
435 loc_pfx: Option<String>,
436 bank: Option<String>,
437 manufacturer: Option<String>,
438 serial: Option<String>,
439 asset: Option<String>,
440 part: Option<String>,
441 speed: Option<usize>,
442}
443
444impl ToCommand for SmbiosType17 {
445 fn to_args(&self) -> Vec<String> {
446 let mut args = vec!["type=17".to_string()];
447
448 if let Some(loc_pfx) = &self.loc_pfx {
449 args.push(format!("loc_pfx={}", loc_pfx));
450 }
451 if let Some(bank) = &self.bank {
452 args.push(format!("bank={}", bank));
453 }
454 if let Some(manufacturer) = &self.manufacturer {
455 args.push(format!("manufacturer={}", manufacturer));
456 }
457 if let Some(serial) = &self.serial {
458 args.push(format!("serial={}", serial));
459 }
460 if let Some(asset) = &self.asset {
461 args.push(format!("asset={}", asset));
462 }
463 if let Some(part) = &self.part {
464 args.push(format!("part={}", part));
465 }
466 if let Some(speed) = &self.speed {
467 args.push(format!("speed={}", speed));
468 }
469 vec![args.join(DELIM_COMMA)]
470 }
471}
472
473impl FromStr for SmbiosType17 {
474 type Err = String;
475
476 fn from_str(s: &str) -> Result<Self, Self::Err> {
477 let mut value = Self::default();
478 for part in s.split(',') {
479 let (key, raw) = part.split_once('=').ok_or_else(|| format!("invalid smbios type 17 option: {part}"))?;
480 match key {
481 "type" if raw == "17" => {}
482 "loc_pfx" => value.loc_pfx = Some(raw.to_string()),
483 "bank" => value.bank = Some(raw.to_string()),
484 "manufacturer" => value.manufacturer = Some(raw.to_string()),
485 "serial" => value.serial = Some(raw.to_string()),
486 "asset" => value.asset = Some(raw.to_string()),
487 "part" => value.part = Some(raw.to_string()),
488 "speed" => value.speed = Some(raw.parse::<usize>().map_err(|e| e.to_string())?),
489 other => return Err(format!("unsupported smbios type 17 option: {other}")),
490 }
491 }
492 Ok(value)
493 }
494}
495
496#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Default, Builder, Arbitrary)]
498pub struct SmbiosType41 {
499 designation: Option<String>,
500 kind: Option<String>,
501 instance: Option<usize>,
502 pcidev: Option<String>,
503}
504
505impl ToCommand for SmbiosType41 {
506 fn to_args(&self) -> Vec<String> {
507 let mut args = vec!["type=41".to_string()];
508
509 if let Some(designation) = &self.designation {
510 args.push(format!("designation={}", designation));
511 }
512 if let Some(kind) = &self.kind {
513 args.push(format!("kind={}", kind));
514 }
515 if let Some(instance) = self.instance {
516 args.push(format!("instance={}", instance));
517 }
518 if let Some(pcidev) = &self.pcidev {
519 args.push(format!("pcidev={}", pcidev));
520 }
521 vec![args.join(DELIM_COMMA)]
522 }
523}
524
525impl FromStr for SmbiosType41 {
526 type Err = String;
527
528 fn from_str(s: &str) -> Result<Self, Self::Err> {
529 let mut value = Self::default();
530 for part in s.split(',') {
531 let (key, raw) = part.split_once('=').ok_or_else(|| format!("invalid smbios type 41 option: {part}"))?;
532 match key {
533 "type" if raw == "41" => {}
534 "designation" => value.designation = Some(raw.to_string()),
535 "kind" => value.kind = Some(raw.to_string()),
536 "instance" => value.instance = Some(raw.parse::<usize>().map_err(|e| e.to_string())?),
537 "pcidev" => value.pcidev = Some(raw.to_string()),
538 other => return Err(format!("unsupported smbios type 41 option: {other}")),
539 }
540 }
541 Ok(value)
542 }
543}
544
545#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Arbitrary)]
550pub enum Smbios {
551 File(SmbiosFile),
552 Type0(SmbiosType0),
553 Type1(SmbiosType1),
554 Type2(SmbiosType2),
555 Type3(SmbiosType3),
556 Type4(SmbiosType4),
557 Type8(SmbiosType8),
558 Type11(SmbiosType11),
559 Type17(SmbiosType17),
560 Type41(SmbiosType41),
561}
562
563impl ToCommand for Smbios {
564 fn command(&self) -> String {
565 ARG_SMBIOS.to_string()
566 }
567 fn to_args(&self) -> Vec<String> {
568 match self {
569 Smbios::File(file) => file.to_args(),
570 Smbios::Type0(type0) => type0.to_args(),
571 Smbios::Type1(type1) => type1.to_args(),
572 Smbios::Type2(type2) => type2.to_args(),
573 Smbios::Type3(type3) => type3.to_args(),
574 Smbios::Type4(type4) => type4.to_args(),
575 Smbios::Type8(type8) => type8.to_args(),
576 Smbios::Type11(type11) => type11.to_args(),
577 Smbios::Type17(type17) => type17.to_args(),
578 Smbios::Type41(type41) => type41.to_args(),
579 }
580 }
581}
582
583impl FromStr for Smbios {
584 type Err = String;
585
586 fn from_str(s: &str) -> Result<Self, Self::Err> {
587 if s.starts_with("file=") {
588 return Ok(Self::File(s.parse::<SmbiosFile>()?));
589 }
590 if s.starts_with("type=41") {
591 return Ok(Self::Type41(s.parse::<SmbiosType41>()?));
592 }
593 if s.starts_with("type=17") {
594 return Ok(Self::Type17(s.parse::<SmbiosType17>()?));
595 }
596 if s.starts_with("type=11") {
597 return Ok(Self::Type11(s.parse::<SmbiosType11>()?));
598 }
599 if s.starts_with("type=0") {
600 return Ok(Self::Type0(s.parse::<SmbiosType0>()?));
601 }
602 if s.starts_with("type=1") {
603 return Ok(Self::Type1(s.parse::<SmbiosType1>()?));
604 }
605 if s.starts_with("type=2") {
606 return Ok(Self::Type2(s.parse::<SmbiosType2>()?));
607 }
608 if s.starts_with("type=3") {
609 return Ok(Self::Type3(s.parse::<SmbiosType3>()?));
610 }
611 if s.starts_with("type=4") {
612 return Ok(Self::Type4(s.parse::<SmbiosType4>()?));
613 }
614 if s.starts_with("type=8") {
615 return Ok(Self::Type8(s.parse::<SmbiosType8>()?));
616 }
617
618 Err(format!("unsupported smbios argument: {s}"))
619 }
620}