Skip to main content

efivar_fix/efi/
variable.rs

1//! Definition of the Variable type
2
3use std::fmt;
4use std::str::FromStr;
5
6use crate::Error;
7
8use super::variable_vendor::VariableVendor;
9
10/// Represents an EFI variable, with a name and a vendor (namespace)
11///
12/// # Examples
13///
14/// Parsing a valid variable into succeeds:
15///
16/// ```
17/// # use std::str::FromStr;
18/// # use efivar::efi::Variable;
19/// let var = Variable::from_str("BootOrder-8be4df61-93ca-11d2-aa0d-00e098032b8c").unwrap();
20/// assert_eq!(*var.vendor().as_ref(), uuid::Uuid::from_str("8be4df61-93ca-11d2-aa0d-00e098032b8c").unwrap());
21/// assert_eq!(var.name(), "BootOrder");
22/// ```
23///
24/// Parsing an invalid name fails:
25///
26/// ```
27/// # use std::str::FromStr;
28/// # use efivar::efi::Variable;
29/// let result = Variable::from_str("invalid variable");
30/// assert!(result.is_err());
31/// ```
32///
33/// Turning the structure back into a string:
34///
35/// ```
36/// # use efivar::efi::Variable;
37/// let var = Variable::new("BootOrder");
38/// assert_eq!(var.to_string(), "BootOrder-8be4df61-93ca-11d2-aa0d-00e098032b8c");
39/// ```
40#[derive(Debug, Clone, PartialEq, Eq)]
41pub struct Variable {
42    /// Variable name
43    name: String,
44    /// Vendor identifier
45    vendor: VariableVendor,
46}
47
48impl Variable {
49    /// Create a new EFI standard variable
50    ///
51    /// # Parameters
52    ///
53    /// * `name`: name of the variable
54    pub fn new(name: &str) -> Self {
55        Self {
56            name: name.to_owned(),
57            vendor: VariableVendor::Efi,
58        }
59    }
60
61    /// Create a new custom vendor variable
62    ///
63    /// # Parameters
64    ///
65    /// * `name`: name of the variable
66    /// * `vendor`: vendor identifier
67    pub fn new_with_vendor<V: Into<VariableVendor>>(name: &str, vendor: V) -> Self {
68        Self {
69            name: name.to_owned(),
70            vendor: vendor.into(),
71        }
72    }
73
74    /// Get the variable name for this instance
75    pub fn name(&self) -> &str {
76        &self.name
77    }
78
79    /// Get the vendor for this instance
80    pub fn vendor(&self) -> &VariableVendor {
81        &self.vendor
82    }
83
84    /// Return a short version of the variable as a String
85    pub fn short_name(&self) -> String {
86        if self.vendor.is_efi() {
87            self.name.clone()
88        } else {
89            self.to_string()
90        }
91    }
92
93    /// Returns the boot var ID (4 digits hex number) if this variable is a boot entry. Else, return None
94    pub fn boot_var_id(&self) -> Option<u16> {
95        if self.name.len() == 8 && &self.name[0..4] == "Boot" {
96            u16::from_str_radix(&self.name[4..8], 16).ok()
97        } else {
98            None
99        }
100    }
101}
102
103impl FromStr for Variable {
104    type Err = Error;
105
106    fn from_str(s: &str) -> Result<Self, Self::Err> {
107        let name_parts = s.splitn(2, '-').collect::<Vec<_>>();
108        if name_parts.len() != 2 {
109            return Err(Error::InvalidVarName { name: s.into() });
110        }
111
112        // Parse GUID
113        let vendor = uuid::Uuid::from_str(name_parts[1])
114            .map_err(|error| crate::Error::UuidError { error })?;
115
116        Ok(Self::new_with_vendor(name_parts[0], vendor))
117    }
118}
119
120impl fmt::Display for Variable {
121    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
122        write!(f, "{}-{}", self.name, self.vendor)
123    }
124}
125
126#[cfg(test)]
127mod tests {
128    use uuid::Uuid;
129
130    use super::*;
131
132    #[test]
133    fn parse_valid_var() {
134        let var = Variable::from_str("BootOrder-c9c4c263-cb10-45ea-bdb6-cabdb201d0f5").unwrap();
135        assert_eq!(var.name(), "BootOrder");
136        assert_eq!(
137            var.vendor().to_string(),
138            "c9c4c263-cb10-45ea-bdb6-cabdb201d0f5"
139        );
140    }
141
142    #[test]
143    fn parse_invalid_var() {
144        assert!(Variable::from_str("BootOrder_Invalid").is_err());
145    }
146
147    #[test]
148    fn parse_invalid_var_2() {
149        assert!(Variable::from_str("BootOrder-Invalid").is_err());
150    }
151
152    #[test]
153    fn to_fullname_partial() {
154        assert_eq!(
155            Variable::new("BootOrder").to_string(),
156            "BootOrder-8be4df61-93ca-11d2-aa0d-00e098032b8c"
157        );
158    }
159
160    #[test]
161    fn short_name() {
162        assert_eq!(Variable::new("BootOrder").short_name(), "BootOrder");
163        assert_eq!(
164            Variable::new_with_vendor(
165                "BootOrder",
166                Uuid::from_str("32e3b3d6-a5e6-47a8-980d-d9d37a104c56").unwrap()
167            )
168            .short_name(),
169            "BootOrder-32e3b3d6-a5e6-47a8-980d-d9d37a104c56"
170        );
171    }
172
173    #[test]
174    fn boot_var_id() {
175        assert_eq!(Variable::new("Boot0001").boot_var_id(), Some(0x0001));
176        assert_eq!(Variable::new("Boot1000").boot_var_id(), Some(0x1000));
177
178        assert_eq!(Variable::new("BootOrder").boot_var_id(), None);
179
180        assert_eq!(Variable::new("Boot10000").boot_var_id(), None);
181        assert_eq!(Variable::new("Boot100").boot_var_id(), None);
182    }
183}