1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
use super::header::Handle;
use super::undefined_struct::{UndefinedStruct, UndefinedStructTable};
use crate::structs::{DefinedStructTable, SMBiosStruct};
use serde::{ser::SerializeStruct, Serialize, Serializer};
use std::io::Error;
use std::{cmp::Ordering, slice::Iter};
use std::{fmt, fs::read};

/// # SMBIOS Data
///
/// Contains an optional SMBIOS version and a collection of SMBIOS structures.
pub struct SMBiosData {
    table: UndefinedStructTable,
    /// Version of the contained SMBIOS structures.
    pub version: Option<SMBiosVersion>,
}

impl<'a> SMBiosData {
    /// Creates an SMBIOS table parser which can be iterated
    ///
    /// `table` is iterable table data.
    /// `version` is optional and represents the DMTF SMBIOS Standard version of the bytes in `data`.
    pub fn new(table: UndefinedStructTable, version: Option<SMBiosVersion>) -> Self {
        Self { table, version }
    }

    /// Creates an SMBIOS table parser which can be iterated
    ///
    /// `data` is a block of bytes representing the raw table data.
    /// `version` is optional and represents the DMTF SMBIOS Standard version of the bytes in `data`.
    pub fn from_vec_and_version(data: Vec<u8>, version: Option<SMBiosVersion>) -> Self {
        Self {
            table: UndefinedStructTable::from(data),
            version,
        }
    }

    /// Loads raw SMBios table data from a file
    pub fn try_load_from_file(
        filename: &str,
        version: Option<SMBiosVersion>,
    ) -> Result<SMBiosData, Error> {
        let data = read(filename)?;
        let result = Self {
            table: UndefinedStructTable::from(data),
            version,
        };
        Ok(result)
    }

    /// Iterator of the contained [UndefinedStruct] items
    pub fn iter(&self) -> Iter<'_, UndefinedStruct> {
        self.table.iter()
    }

    /// An iterator over the defined type instances within the table.
    pub fn defined_struct_iter<T: 'a>(&'a self) -> impl Iterator<Item = T> + 'a
    where
        T: SMBiosStruct<'a>,
    {
        self.table.defined_struct_iter()
    }

    /// Tests if every element of the defined struct iterator matches a predicate.
    pub fn all<T, F>(&'a self, f: F) -> bool
    where
        T: SMBiosStruct<'a>,
        F: FnMut(T) -> bool,
    {
        self.table.all(f)
    }

    /// Tests if any element of the defined struct iterator matches a predicate.
    pub fn any<T, F>(&'a self, f: F) -> bool
    where
        T: SMBiosStruct<'a>,
        F: FnMut(T) -> bool,
    {
        self.table.any(f)
    }

    /// Finds the first occurance of the structure
    pub fn first<T>(&'a self) -> Option<T>
    where
        T: SMBiosStruct<'a>,
    {
        self.table.first()
    }

    /// Finds the first occurance of the structure that satisfies a predicate.
    pub fn find<T, P>(&'a self, predicate: P) -> Option<T>
    where
        T: SMBiosStruct<'a>,
        P: FnMut(&T) -> bool,
    {
        self.table.find(predicate)
    }

    /// Applies function to the defined struct elements and returns the first non-none result.
    pub fn find_map<A, B, F>(&'a self, f: F) -> Option<B>
    where
        A: SMBiosStruct<'a>,
        F: FnMut(A) -> Option<B>,
    {
        self.table.find_map(f)
    }

    /// Creates an iterator of the defined structure which uses a closure to determine if an element should be yielded.
    pub fn filter<T: 'a, P: 'a>(&'a self, predicate: P) -> impl Iterator<Item = T> + 'a
    where
        T: SMBiosStruct<'a>,
        P: FnMut(&T) -> bool,
    {
        self.table.filter(predicate)
    }

    /// Takes a closure and creates an iterator which calls that closure on each defined struct.
    pub fn map<A: 'a, B: 'a, F: 'a>(&'a self, f: F) -> impl Iterator<Item = B> + 'a
    where
        A: SMBiosStruct<'a>,
        F: FnMut(A) -> B,
    {
        self.table.map(f)
    }

    /// Creates an iterator that both filters and maps from the defined struct iterator.
    pub fn filter_map<A: 'a, B: 'a, F: 'a>(&'a self, f: F) -> impl Iterator<Item = B> + 'a
    where
        A: SMBiosStruct<'a>,
        F: FnMut(A) -> Option<B>,
    {
        self.table.filter_map(f)
    }

    /// Finds the structure matching the given handle
    pub fn find_by_handle(&'a self, handle: &Handle) -> Option<&UndefinedStruct> {
        self.table.find_by_handle(handle)
    }

    /// Finds all occurances of the structure
    pub fn collect<T>(&'a self) -> Vec<T>
    where
        T: SMBiosStruct<'a>,
    {
        self.table.collect()
    }
}

impl IntoIterator for SMBiosData {
    type Item = UndefinedStruct;
    type IntoIter = std::vec::IntoIter<Self::Item>;

    fn into_iter(self) -> Self::IntoIter {
        self.table.into_iter()
    }
}

impl fmt::Debug for SMBiosData {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        // Convert to defined structures to see the structure fields
        let defined_table: DefinedStructTable<'_> = self.table.iter().collect();

        fmt.debug_struct(std::any::type_name::<SMBiosData>())
            .field("version", &self.version)
            .field("table", &defined_table)
            .finish()
    }
}

impl Serialize for SMBiosData {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        // Convert to defined structures to see the structure fields
        let defined_table: DefinedStructTable<'_> = self.table.iter().collect();

        let mut state = serializer.serialize_struct("SMBiosData", 2)?;
        state.serialize_field("version", &self.version)?;
        state.serialize_field("table", &defined_table)?;
        state.end()
    }
}

/// # Version of SMBIOS Structure
#[derive(Debug, Eq, PartialEq, Serialize)]
pub struct SMBiosVersion {
    /// SMBIOS major version
    pub major: u8,
    /// SMBIOS minor version
    pub minor: u8,
    /// SMBIOS version revision
    pub revision: u8,
}

impl Ord for SMBiosVersion {
    fn cmp(&self, other: &Self) -> Ordering {
        if self.major < other.major {
            Ordering::Less
        } else if self.major > other.major {
            Ordering::Greater
        } else if self.minor < other.minor {
            Ordering::Less
        } else if self.minor > other.minor {
            Ordering::Greater
        } else if self.revision < other.revision {
            Ordering::Less
        } else if self.revision > other.revision {
            Ordering::Greater
        } else {
            Ordering::Equal
        }
    }
}

impl PartialOrd for SMBiosVersion {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}