Skip to main content

dmidecode/structures/
003_enclosure.rs

1//! System Enclosure or Chassis (Type 3)
2//!
3//! The information in this structure defines attributes of the system’s mechanical
4//! enclosure(s). For example, if a system included a separate enclosure for its peripheral
5//! devices, two structures would be returned: one for the main system enclosure and the second for
6//! the peripheral device enclosure.
7
8use core::fmt;
9use core::hash::{Hash, Hasher};
10use core::slice::Chunks;
11
12use crate::{HeaderPacked, MalformedStructureError, RawStructure};
13
14/// System Enclosure or Chassis structure
15#[derive(Clone, Debug, Eq, Hash, PartialEq)]
16pub struct Enclosure<'buffer> {
17    /// Specifies the structure’s handle
18    pub handle: u16,
19    /// Manufacturer string is non-null
20    pub manufacturer: &'buffer str,
21    /// Chassis lock is present
22    pub chassis_lock: bool,
23    /// Type field identifies the type of enclosure. (Unknown is disallowed.)
24    pub enclosure_type: EnclosureType,
25    /// Version
26    pub version: &'buffer str,
27    /// Serial Number
28    pub serial_number: &'buffer str,
29    /// Asset Tag Number
30    pub asset_tag_number: &'buffer str,
31    /// State of the enclosure when it was last booted;
32    pub boot_up_state: Option<State>,
33    /// State of the enclosure’s power supply (or supplies) when last booted
34    pub power_supply_state: Option<State>,
35    /// Thermal state of the enclosure when last booted
36    pub thermal_state: Option<State>,
37    /// Physical security status of the enclosure when last booted
38    pub security_status: Option<SecurityStatus>,
39    /// OEM- or BIOS vendor-specific information
40    pub oem_defined: Option<u32>,
41    /// Height of the enclosure , in 'U's A U is a standard unit of measure for the height of a
42    /// rack or rack-mountable component and is equal to 1.75 inches or 4.445 cm. A value of 00h
43    /// indicates that the enclosure height is unspecified.
44    pub height: Option<u8>,
45    /// Number of power cords associated with the enclosure or chassis A value of 00h indicates
46    /// that the number is unspecified.
47    pub power_cords_number: Option<u8>,
48    /// Each Contained Element record consists of sub-fields that further describe elements
49    /// contained by the chassis
50    pub contained_elements: Option<ContainedElements<'buffer>>,
51    /// Number of null-terminated string describing the chassis or enclosure SKU number
52    pub sku_number: Option<&'buffer str>,
53}
54
55/// System Enclosure or Chassis Type
56#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
57pub enum EnclosureType {
58    Other,
59    Unknown,
60    Desktop,
61    LowProfileDesktop,
62    PizzaBox,
63    MiniTower,
64    Tower,
65    Portable,
66    Laptop,
67    Notebook,
68    HandHeld,
69    DockingStation,
70    AllInOne,
71    SubNotebook,
72    SpaceSaving,
73    LunchBox,
74    MainServerChassis,
75    ExpansionChassis,
76    SubChassis,
77    BusExpansionChassis,
78    PeripheralChassis,
79    RaidChassis,
80    RackMountChassis,
81    SealedCasePc,
82    /// When this value is specified by an SMBIOS implementation, the physical chassis associated
83    /// with this structure supports multiple, independently reporting physical systems—regardless
84    /// of the chassis' current configuration. Systems in the same physical chassis are required to
85    /// report the same value in this structure's Serial Number field.  For a chassis that may also
86    /// be configured as either a single system or multiple physical systems, the Multi-system
87    /// chassis value is reported even if the chassis is currently configured as a single system.
88    /// This allows management applications to recognize the multisystem potential of the chassis.
89    MultiSystemChassis,
90    CompactPci,
91    AdvancedTca,
92    /// An SMBIOS implementation for a Blade would contain a Type 3 Chassis structure for the
93    /// individual Blade system as well as one for the Blade Enclosure that completes the Blade
94    /// system.
95    Blade,
96    /// A Blade Enclosure is a specialized chassis that contains a set of Blades. It provides much
97    /// of the non-core computing infrastructure for a set of Blades (power, cooling, networking,
98    /// etc.). A Blade Enclosure may itself reside inside a Rack or be a standalone chassis.
99    BladeEnclosure,
100    Tablet,
101    Convertible,
102    Detachable,
103    IotGateway,
104    EmbeddedPc,
105    MiniPc,
106    StickPc,
107    Undefined(u8),
108}
109
110/// System Enclosure or Chassis States
111#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
112pub enum State {
113    Other,
114    Unknown,
115    Safe,
116    Warning,
117    Critical,
118    NonRecoverable,
119    Undefined(u8),
120}
121
122/// System Enclosure or Chassis Security Status
123#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
124pub enum SecurityStatus {
125    Other,
126    Unknown,
127    None,
128    ExternalInterfaceLockedOut,
129    ExternalInterfaceEnabled,
130    Undefined(u8),
131}
132
133/// Elements, possibly defined by other SMBIOS structures, present in this chassis
134#[derive(Clone, Debug)]
135pub struct ContainedElements<'buffer> {
136    chunks: Chunks<'buffer, u8>,
137    count: u8,
138    record_length: u8,
139}
140
141/// Each Contained Element record consists of sub-fields that further describe elements contained
142/// by the chassis.
143#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
144pub struct ContainedElement {
145    /// Specifies the type of element associated with this record
146    type_: ContainedElementType,
147    /// Specifies the minimum number of the element type that can be installed in the chassis for
148    /// the chassis to properly operate, in the range 0 to 254. The value 255 (0FFh) is reserved
149    /// for future definition.
150    minimum: u8,
151    /// Specifies the maximum number of the element type that can be installed in the chassis, in
152    /// the range 1 to 255. The value 0 is reserved for future definition.
153    maximum: u8,
154}
155
156/// Identifies whether the Type contains an SMBIOS Baseboard Type enumeration or an SMBIOS
157/// structure type enumeration.
158#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
159pub enum ContainedElementType {
160    BoardType(crate::baseboard::BoardType),
161    InfoType(crate::InfoType),
162}
163
164impl<'buffer> Enclosure<'buffer> {
165    pub(crate) fn try_from(structure: RawStructure<'buffer>) -> Result<Enclosure<'buffer>, MalformedStructureError> {
166        #[repr(C)]
167        #[repr(packed)]
168        struct EnclosurePacked2_0 {
169            manufacturer: u8,
170            enclosure_type: u8,
171            version: u8,
172            serial_number: u8,
173            asset_tag_number: u8,
174        }
175
176        // compile time assertion that our minimum enclosure structure
177        // fits the minimum required by the BIOS spec
178        const _: () = {
179            assert!(core::mem::size_of::<EnclosurePacked2_0>() + core::mem::size_of::<HeaderPacked>() == 0x09);
180        };
181
182        struct RawEnclosureType(u8);
183        impl RawEnclosureType {
184            fn new(byte: u8) -> Self {
185                RawEnclosureType(byte)
186            }
187            fn get_lock(&self) -> bool {
188                (self.0 & 0b1000_0000) != 0
189            }
190            fn get_type(&self) -> EnclosureType {
191                (self.0 & 0b0111_1111).into()
192            }
193        }
194
195        if structure.data.len() < core::mem::size_of::<EnclosurePacked2_0>() {
196            return Err(crate::MalformedStructureError::InvalidFormattedSectionLength(
197                structure.info,
198                structure.handle,
199                "minimum of ",
200                core::mem::size_of::<EnclosurePacked2_0>() as u8,
201            ));
202        }
203
204        let (minimum, mut extra) = structure.data.split_at(core::mem::size_of::<EnclosurePacked2_0>());
205        let_as_struct!(packed, EnclosurePacked2_0, minimum);
206        let enclosure_type = RawEnclosureType::new(packed.enclosure_type);
207        let mut enclosure = Enclosure {
208            handle: structure.handle,
209            manufacturer: structure.find_string(packed.manufacturer)?,
210            chassis_lock: enclosure_type.get_lock(),
211            enclosure_type: enclosure_type.get_type(),
212            version: structure.find_string(packed.version)?,
213            serial_number: structure.find_string(packed.serial_number)?,
214            asset_tag_number: structure.find_string(packed.asset_tag_number)?,
215            boot_up_state: None,
216            power_supply_state: None,
217            thermal_state: None,
218            security_status: None,
219            oem_defined: None,
220            height: None,
221            power_cords_number: None,
222            contained_elements: None,
223            sku_number: None,
224        };
225        let data = &mut extra;
226
227        // Optional 2.1+ fields
228        let sku_number = read_bytes(data)
229            .and_then(|boot_up_state: u8| {
230                enclosure.boot_up_state = Some(boot_up_state.into());
231                read_bytes(data)
232            })
233            .and_then(|power_supply_state: u8| {
234                enclosure.power_supply_state = Some(power_supply_state.into());
235                read_bytes(data)
236            })
237            .and_then(|thermal_state: u8| {
238                enclosure.thermal_state = Some(thermal_state.into());
239                read_bytes(data)
240            })
241            .and_then(|security_status: u8| {
242                enclosure.security_status = Some(security_status.into());
243                read_bytes(data)
244            })
245            // Optional 2.3+ fields
246            .and_then(|oem_defined: u32| {
247                enclosure.oem_defined = Some(u32::from_le(oem_defined));
248                read_bytes(data)
249            })
250            .and_then(|height: u8| {
251                enclosure.height = Some(height);
252                read_bytes(data)
253            })
254            .and_then(|power_cords_number: u8| {
255                enclosure.power_cords_number = Some(power_cords_number);
256                ContainedElements::new(data)
257            })
258            .and_then(|contained_elements| {
259                enclosure.contained_elements = Some(contained_elements);
260                read_bytes(data)
261            });
262
263        // Optional 2.7+ fields
264        if let Some(sku_number) = sku_number {
265            enclosure.sku_number = Some(structure.find_string(sku_number)?);
266        }
267
268        Ok(enclosure)
269    }
270}
271
272impl From<u8> for EnclosureType {
273    fn from(byte: u8) -> EnclosureType {
274        match byte {
275            0x01 => EnclosureType::Other,
276            0x02 => EnclosureType::Unknown,
277            0x03 => EnclosureType::Desktop,
278            0x04 => EnclosureType::LowProfileDesktop,
279            0x05 => EnclosureType::PizzaBox,
280            0x06 => EnclosureType::MiniTower,
281            0x07 => EnclosureType::Tower,
282            0x08 => EnclosureType::Portable,
283            0x09 => EnclosureType::Laptop,
284            0x0A => EnclosureType::Notebook,
285            0x0B => EnclosureType::HandHeld,
286            0x0C => EnclosureType::DockingStation,
287            0x0D => EnclosureType::AllInOne,
288            0x0E => EnclosureType::SubNotebook,
289            0x0F => EnclosureType::SpaceSaving,
290            0x10 => EnclosureType::LunchBox,
291            0x11 => EnclosureType::MainServerChassis,
292            0x12 => EnclosureType::ExpansionChassis,
293            0x13 => EnclosureType::SubChassis,
294            0x14 => EnclosureType::BusExpansionChassis,
295            0x15 => EnclosureType::PeripheralChassis,
296            0x16 => EnclosureType::RaidChassis,
297            0x17 => EnclosureType::RackMountChassis,
298            0x18 => EnclosureType::SealedCasePc,
299            0x19 => EnclosureType::MultiSystemChassis,
300            0x1A => EnclosureType::CompactPci,
301            0x1B => EnclosureType::AdvancedTca,
302            0x1C => EnclosureType::Blade,
303            0x1D => EnclosureType::BladeEnclosure,
304            0x1E => EnclosureType::Tablet,
305            0x1F => EnclosureType::Convertible,
306            0x20 => EnclosureType::Detachable,
307            0x21 => EnclosureType::IotGateway,
308            0x22 => EnclosureType::EmbeddedPc,
309            0x23 => EnclosureType::MiniPc,
310            0x24 => EnclosureType::StickPc,
311            v => Self::Undefined(v),
312        }
313    }
314}
315impl fmt::Display for EnclosureType {
316    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
317        match self {
318            Self::Other => write!(f, "Other"),
319            Self::Unknown => write!(f, "Unknown"),
320            Self::Desktop => write!(f, "Desktop"),
321            Self::LowProfileDesktop => write!(f, "Low Profile Desktop"),
322            Self::PizzaBox => write!(f, "Pizza Box"),
323            Self::MiniTower => write!(f, "Mini Tower"),
324            Self::Tower => write!(f, "Tower"),
325            Self::Portable => write!(f, "Portable"),
326            Self::Laptop => write!(f, "Laptop"),
327            Self::Notebook => write!(f, "Notebook"),
328            Self::HandHeld => write!(f, "Hand Held"),
329            Self::DockingStation => write!(f, "Docking Station"),
330            Self::AllInOne => write!(f, "All in One"),
331            Self::SubNotebook => write!(f, "Sub Notebook"),
332            Self::SpaceSaving => write!(f, "Space-saving"),
333            Self::LunchBox => write!(f, "Lunch Box"),
334            Self::MainServerChassis => write!(f, "Main Server Chassis"),
335            Self::ExpansionChassis => write!(f, "Expansion Chassis"),
336            Self::SubChassis => write!(f, "SubChassis"),
337            Self::BusExpansionChassis => write!(f, "Bus Expansion Chassis"),
338            Self::PeripheralChassis => write!(f, "Peripheral Chassis"),
339            Self::RaidChassis => write!(f, "RAID Chassis"),
340            Self::RackMountChassis => write!(f, "Rack Mount Chassis"),
341            Self::SealedCasePc => write!(f, "Sealed-case PC"),
342            Self::MultiSystemChassis => write!(f, "Multi-system chassis"),
343            Self::CompactPci => write!(f, "Compact PCI"),
344            Self::AdvancedTca => write!(f, "Advanced TCA"),
345            Self::Blade => write!(f, "Blade"),
346            Self::BladeEnclosure => write!(f, "Blade Enclosure"),
347            Self::Tablet => write!(f, "Tablet"),
348            Self::Convertible => write!(f, "Convertible"),
349            Self::Detachable => write!(f, "Detachable"),
350            Self::IotGateway => write!(f, "IoT Gateway"),
351            Self::EmbeddedPc => write!(f, "Embedded PC"),
352            Self::MiniPc => write!(f, "Mini PC"),
353            Self::StickPc => write!(f, "Stick PC"),
354            Self::Undefined(v) => write!(f, "Undefined: {v}"),
355        }
356    }
357}
358
359impl From<u8> for State {
360    fn from(byte: u8) -> State {
361        match byte {
362            0x01 => Self::Other,
363            0x02 => Self::Unknown,
364            0x03 => Self::Safe,
365            0x04 => Self::Warning,
366            0x05 => Self::Critical,
367            0x06 => Self::NonRecoverable,
368            v => Self::Undefined(v),
369        }
370    }
371}
372impl fmt::Display for State {
373    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
374        match self {
375            Self::Other => write!(f, "Other"),
376            Self::Unknown => write!(f, "Unknown"),
377            Self::Safe => write!(f, "Safe"),
378            Self::Warning => write!(f, "Warning"),
379            Self::Critical => write!(f, "Critical"),
380            Self::NonRecoverable => write!(f, "Non-recoverable"),
381            Self::Undefined(v) => write!(f, "Undefined: {v}"),
382        }
383    }
384}
385
386impl From<u8> for SecurityStatus {
387    fn from(byte: u8) -> SecurityStatus {
388        match byte {
389            0x01 => Self::Other,
390            0x02 => Self::Unknown,
391            0x03 => Self::None,
392            0x04 => Self::ExternalInterfaceLockedOut,
393            0x05 => Self::ExternalInterfaceEnabled,
394            v => Self::Undefined(v),
395        }
396    }
397}
398impl fmt::Display for SecurityStatus {
399    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
400        match self {
401            Self::Other => write!(f, "Other"),
402            Self::Unknown => write!(f, "Unknown"),
403            Self::None => write!(f, "None"),
404            Self::ExternalInterfaceLockedOut => write!(f, "External interface locked out"),
405            Self::ExternalInterfaceEnabled => write!(f, "External interface enabled"),
406            Self::Undefined(v) => write!(f, "Undefined: {v}"),
407        }
408    }
409}
410
411impl<'buffer> ContainedElements<'buffer> {
412    fn new(data: &mut &'buffer [u8]) -> Option<Self> {
413        let count: u8 = read_bytes(data)?;
414        let record_length: u8 = read_bytes(data)?;
415
416        if count == 0 || record_length == 0 {
417            return Some(Self {
418                chunks: [].chunks(usize::MAX),
419                count,
420                record_length,
421            });
422        }
423
424        let length = (count * record_length) as usize;
425        let chunks = data.get(0..length)?.chunks(record_length as usize);
426        *data = &data[length..];
427        Some(Self {
428            chunks,
429            count,
430            record_length,
431        })
432    }
433
434    pub fn count(&self) -> u8 {
435        self.count
436    }
437}
438
439impl PartialEq for ContainedElements<'_> {
440    fn eq(&self, other: &Self) -> bool {
441        self.chunks.clone().eq(other.chunks.clone())
442            && self.count == other.count
443            && self.record_length == other.record_length
444    }
445}
446impl Eq for ContainedElements<'_> {}
447impl Hash for ContainedElements<'_> {
448    fn hash<H: Hasher>(&self, state: &mut H) {
449        self.chunks.clone().for_each(|c| c.hash(state));
450        self.count.hash(state);
451        self.record_length.hash(state);
452    }
453}
454impl Iterator for ContainedElements<'_> {
455    type Item = ContainedElement;
456
457    fn next(&mut self) -> Option<Self::Item> {
458        self.chunks.next().map(|a| a.into())
459    }
460}
461
462impl From<&[u8]> for ContainedElement {
463    fn from(data: &[u8]) -> ContainedElement {
464        #[repr(C)]
465        #[repr(packed)]
466        struct ContainedElement2_3 {
467            type_: u8,
468            minimum: u8,
469            maximum: u8,
470        }
471        let_as_struct!(packed, ContainedElement2_3, data);
472        ContainedElement {
473            type_: packed.type_.into(),
474            minimum: packed.minimum,
475            maximum: packed.maximum,
476        }
477    }
478}
479impl fmt::Display for ContainedElement {
480    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
481        write!(f, "{} ({}-{})", self.type_, self.minimum, self.maximum)
482    }
483}
484
485impl From<u8> for ContainedElementType {
486    fn from(byte: u8) -> ContainedElementType {
487        let val = byte & 0b0111_1111;
488        if byte & 0b1000_0000 == 0 {
489            Self::BoardType(val.into())
490        } else {
491            Self::InfoType(val.into())
492        }
493    }
494}
495impl fmt::Display for ContainedElementType {
496    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
497        match self {
498            Self::BoardType(board) => write!(f, "Baseboard type: {board}"),
499            Self::InfoType(info) => write!(f, "Structure type: {info}"),
500        }
501    }
502}
503
504fn read_bytes<T: Copy>(data: &mut &[u8]) -> Option<T> {
505    if data.len() < core::mem::size_of::<T>() {
506        return None;
507    }
508
509    let value = unsafe { core::ptr::read_unaligned((*data).as_ptr() as *const T) };
510    *data = &data[core::mem::size_of::<T>()..];
511    Some(value)
512}
513
514#[cfg(test)]
515mod tests {
516    use std::prelude::v1::*;
517    #[test]
518    fn enclosure_type() {
519        use super::EnclosureType::*;
520        for i in 1..=0x24 {
521            let (e, s) = match i {
522                0x01 => (Other, "Other".into()),
523                0x09 => (Laptop, "Laptop".into()),
524                0x18 => (SealedCasePc, "Sealed-case PC".into()),
525                0x22 => (EmbeddedPc, "Embedded PC".into()),
526                v @ 0xF0..=0xFF => (Undefined(v), format!("Undefined: {v}")),
527                _ => continue,
528            };
529            assert_eq!(e, i.into(), "{i:#x}");
530            assert_eq!(s, format!("{e}"));
531        }
532    }
533
534    #[test]
535    fn state() {
536        use super::State::*;
537        for i in 0..=0xFF {
538            let (e, s) = match i {
539                0x01 => (Other, "Other".into()),
540                0x04 => (Warning, "Warning".into()),
541                0x06 => (NonRecoverable, "Non-recoverable".into()),
542                v @ 0xF0..=0xFF => (Undefined(v), format!("Undefined: {v}")),
543                _ => continue,
544            };
545            assert_eq!(e, i.into(), "{i:#x}");
546            assert_eq!(s, format!("{e}"));
547        }
548    }
549
550    #[test]
551    fn security_status() {
552        use super::SecurityStatus::*;
553        for i in 0..=0xFF {
554            let (e, s) = match i {
555                0x01 => (Other, "Other".into()),
556                0x03 => (None, "None".into()),
557                0x05 => (ExternalInterfaceEnabled, "External interface enabled".into()),
558                v @ 0xF0..=0xFF => (Undefined(v), format!("Undefined: {v}")),
559                _ => continue,
560            };
561            assert_eq!(e, i.into(), "{i:#x}");
562            assert_eq!(s, format!("{e}"));
563        }
564    }
565
566    #[test]
567    fn contained_element() {
568        use super::{ContainedElement, ContainedElementType};
569        let data = &[
570            // Type contains an SMBIOS structure type
571            (
572                [0b1000_1001, 1, 2],
573                ContainedElement {
574                    type_: ContainedElementType::InfoType(crate::InfoType::SystemSlots),
575                    minimum: 1,
576                    maximum: 2,
577                },
578                "Structure type: System Slots (1-2)",
579            ),
580            // Type contains an SMBIOS Baseboard Type
581            (
582                [0b0000_0100, 1, 2],
583                ContainedElement {
584                    type_: ContainedElementType::BoardType(crate::baseboard::BoardType::ConnectivitySwitch),
585                    minimum: 1,
586                    maximum: 2,
587                },
588                "Baseboard type: Connectivity Switch (1-2)",
589            ),
590        ];
591        for (array, contained_element, display) in data {
592            let v = &ContainedElement::from(&array[..]);
593            assert_eq!(contained_element, v);
594            assert_eq!(format!("{display}"), format!("{}", v));
595        }
596    }
597
598    #[test]
599    fn contained_elements() {
600        use super::{ContainedElement, ContainedElementType, ContainedElements};
601        let structure_data = [
602            0x02, // count = 2
603            0x03, // length = 3
604            0x91, 0x01, 0x02, 0x07, 0x03, 0x04, // 6 bytes of elements
605            0x03, // remaining
606        ];
607        let mut data: &[u8] = &structure_data;
608        let mut contained_elements = ContainedElements::new(&mut data).expect("should not be empty");
609        if let Some(el) = contained_elements.next() {
610            assert_eq!(
611                ContainedElement {
612                    type_: ContainedElementType::InfoType(crate::InfoType::MemoryDevice),
613                    minimum: 1,
614                    maximum: 2
615                },
616                el
617            );
618        }
619        if let Some(el) = contained_elements.next() {
620            assert_eq!(
621                ContainedElement {
622                    type_: ContainedElementType::BoardType(crate::baseboard::BoardType::IoModule),
623                    minimum: 3,
624                    maximum: 4
625                },
626                el
627            );
628        }
629        assert_eq!(contained_elements.next(), None);
630        // the data cursor was updated to the remaining bytes
631        assert_eq!(data, &structure_data[8..]);
632    }
633
634    #[test]
635    fn dmi_bin() {
636        use super::*;
637        const DMIDECODE_BIN: &[u8] = include_bytes!("../../tests/data/dmi.0.bin");
638        let entry_point = crate::EntryPoint::search(DMIDECODE_BIN).unwrap();
639        let enc = entry_point
640            .structures(&DMIDECODE_BIN[(entry_point.smbios_address() as usize)..])
641            .find_map(|s| {
642                if let Ok(crate::Structure::Enclosure(enc)) = s {
643                    Some(enc)
644                } else {
645                    None
646                }
647            })
648            .unwrap();
649        let sample = Enclosure {
650            handle: 768,
651            manufacturer: "Dell Inc.",
652            chassis_lock: true,
653            enclosure_type: EnclosureType::RackMountChassis,
654            version: "",
655            serial_number: "XXXXXXX",
656            asset_tag_number: "",
657            boot_up_state: Some(State::Safe),
658            power_supply_state: Some(State::Safe),
659            thermal_state: Some(State::Safe),
660            security_status: Some(SecurityStatus::Unknown),
661            oem_defined: Some(0x01010101),
662            height: Some(2),
663            power_cords_number: Some(2),
664            contained_elements: Some(ContainedElements {
665                chunks: [145, 1, 2, 3, 255, 0].chunks(3),
666                count: 2,
667                record_length: 3,
668            }),
669            sku_number: Some("SKU Number"),
670        };
671
672        assert_eq!(sample, enc);
673        assert_eq!(format!("{}", enc.manufacturer), "Dell Inc.", "Manufacturer");
674        assert_eq!(format!("{}", enc.enclosure_type), "Rack Mount Chassis", "Type");
675        assert_eq!(format!("{}", enc.chassis_lock), "true", "Lock");
676        assert_eq!(format!("{}", enc.version), "", "Version");
677        assert_eq!(format!("{}", enc.serial_number), "XXXXXXX", "Serial Number");
678        assert_eq!(format!("{}", enc.asset_tag_number), "", "Asset Tag");
679        assert_eq!(
680            enc.boot_up_state.map(|v| format!("{v}")),
681            Some("Safe".into()),
682            "Boot-up State"
683        );
684        assert_eq!(
685            enc.power_supply_state.map(|v| format!("{v}")),
686            Some("Safe".into()),
687            "Power Supply State"
688        );
689        assert_eq!(
690            enc.thermal_state.map(|v| format!("{v}")),
691            Some("Safe".into()),
692            "Thermal State"
693        );
694        assert_eq!(
695            enc.security_status.map(|v| format!("{v}")),
696            Some("Unknown".into()),
697            "Security Status"
698        );
699        assert_eq!(
700            enc.oem_defined.map(|v| format!("{v:#010X}")),
701            Some("0x01010101".into()),
702            "OEM Information"
703        );
704        assert_eq!(enc.height, Some(2), "Height");
705        assert_eq!(enc.power_cords_number, Some(2), "Number Of Power Cords");
706        assert_eq!(
707            enc.contained_elements
708                .clone()
709                .and_then(|mut ce| ce.next().map(|s| format!("{s}"))),
710            Some("Structure type: Memory Device (1-2)".into()),
711            "Number Of Power Cords"
712        );
713        assert_eq!(
714            enc.contained_elements
715                .clone()
716                .and_then(|mut ce| ce.nth(1).map(|s| format!("{s}"))),
717            Some("Baseboard type: Server Blade (255-0)".into()),
718            "Number Of Power Cords"
719        );
720        assert_eq!(
721            enc.sku_number.map(|v| v.to_string()),
722            Some("SKU Number".into()),
723            "SKU Number"
724        );
725    }
726
727    #[test]
728    fn no_sku_on_3_14() {
729        use super::*;
730
731        let enclosure = Enclosure::try_from(RawStructure {
732            version: crate::SmbiosVersion { major: 3, minor: 14 },
733            info: crate::InfoType::Enclosure,
734            length: 20,
735            handle: 153,
736            data: &[1, 1, 0, 0, 0, 3, 3, 3, 2, 0, 0, 0, 0, 0, 0, 0],
737            strings: &[71, 111, 111, 103, 108, 101, 0, 0],
738        })
739        .expect("failed to create enclosure");
740
741        assert_eq!(
742            enclosure,
743            Enclosure {
744                handle: 153,
745                manufacturer: "Google",
746                chassis_lock: false,
747                enclosure_type: EnclosureType::Other,
748                version: "",
749                serial_number: "",
750                asset_tag_number: "",
751                boot_up_state: Some(State::Safe),
752                power_supply_state: Some(State::Safe),
753                thermal_state: Some(State::Safe),
754                security_status: Some(SecurityStatus::Unknown),
755                oem_defined: Some(0),
756                height: Some(0),
757                power_cords_number: Some(0),
758                contained_elements: None,
759                sku_number: None
760            }
761        )
762    }
763}