use alloc::string::String;
use log::warn;
use uefi::Handle;
use crate::{
boot::action::BootAction,
config::{
Config,
parsers::Parsers,
types::{Architecture, DevicetreePath, EfiPath, FsHandle, MachineId, SortKey},
},
};
#[must_use = "Has no effect if the result is unused"]
pub struct ConfigBuilder {
config: Config,
}
impl ConfigBuilder {
pub fn new(filename: impl Into<String>, suffix: impl Into<String>) -> Self {
let filename = filename.into();
let suffix = suffix.into();
Self {
config: Config {
title: None,
version: None,
machine_id: None,
sort_key: None,
options: None,
devicetree_path: None,
architecture: None,
efi_path: None,
bad: false,
action: BootAction::BootEfi,
fs_handle: None,
origin: None,
filename,
suffix,
},
}
}
pub fn title(mut self, title: impl Into<String>) -> Self {
self.config.title = Some(title.into());
self
}
pub fn version(mut self, version: impl Into<String>) -> Self {
self.config.version = Some(version.into());
self
}
pub fn machine_id(mut self, machine_id: impl Into<String>) -> Self {
self.config.machine_id = match MachineId::new(&machine_id.into()) {
Ok(machine_id) => Some(machine_id),
Err(e) => {
warn!("{e}");
None
}
};
self
}
pub fn sort_key(mut self, sort_key: impl Into<String>) -> Self {
self.config.sort_key = match SortKey::new(&sort_key.into()) {
Ok(sort_key) => Some(sort_key),
Err(e) => {
warn!("{e}");
None
}
};
self
}
pub fn options(mut self, options: impl Into<String>) -> Self {
self.config.options = Some(options.into());
self
}
pub fn devicetree_path(mut self, devicetree_path: impl Into<String>) -> Self {
self.config.devicetree_path = match DevicetreePath::new(&devicetree_path.into()) {
Ok(devicetree_path) => Some(devicetree_path),
Err(e) => {
warn!("{e}");
None
}
};
self
}
pub fn architecture(mut self, architecture: impl Into<String>) -> Self {
self.config.architecture = match Architecture::new(&architecture.into()) {
Ok(architecture) => Some(architecture),
Err(e) => {
warn!("{e}");
None
}
};
self
}
pub const fn set_bad(mut self, bad: bool) -> Self {
self.config.bad = bad;
self
}
pub const fn action(mut self, action: BootAction) -> Self {
self.config.action = action;
self
}
pub fn fs_handle(mut self, fs_handle: Handle) -> Self {
self.config.fs_handle = match FsHandle::new(fs_handle) {
Ok(fs_handle) => Some(fs_handle),
Err(e) => {
warn!("{e}");
None
}
};
self
}
pub const fn origin(mut self, origin: Parsers) -> Self {
self.config.origin = Some(origin);
self
}
pub fn efi_path(mut self, efi_path: impl Into<String>) -> Self {
self.config.efi_path = match EfiPath::new(&efi_path.into()) {
Ok(efi_path) => Some(efi_path),
Err(e) => {
warn!("{e}");
None
}
};
self
}
#[must_use = "Has no effect if the result is unused"]
pub fn build(self) -> Config {
self.config
}
pub fn assign_if_some<F, T>(self, value: Option<T>, assign: F) -> Self
where
F: FnOnce(Self, T) -> Self,
{
if let Some(value) = value {
assign(self, value)
} else {
self
}
}
}
impl From<&Config> for ConfigBuilder {
fn from(value: &Config) -> Self {
Self::new(&value.filename, &value.suffix)
.set_bad(value.bad)
.assign_if_some(value.title.as_ref(), Self::title)
.assign_if_some(value.version.as_ref(), Self::version)
.assign_if_some(value.machine_id.as_deref(), Self::machine_id)
.assign_if_some(value.sort_key.as_deref(), Self::sort_key)
.assign_if_some(value.options.as_ref(), Self::options)
.assign_if_some(value.devicetree_path.as_deref(), Self::devicetree_path)
.assign_if_some(value.architecture.as_deref(), Self::architecture)
.assign_if_some(value.efi_path.as_deref(), Self::efi_path)
.assign_if_some(value.fs_handle.as_deref().copied(), Self::fs_handle)
.assign_if_some(value.origin, Self::origin)
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::borrow::ToOwned;
#[test]
fn test_basic_config() {
let config = ConfigBuilder::new("foo.bar", ".bar")
.efi_path("\\foo\\foo.bar")
.title("Some title")
.version("some.version")
.sort_key("some-sort-key")
.options("Some options")
.build();
assert_eq!(
config.efi_path.as_deref(),
Some(&"\\foo\\foo.bar".to_owned())
);
assert_eq!(config.filename, "foo.bar".to_owned());
assert_eq!(config.suffix, ".bar".to_owned());
assert_eq!(config.title, Some("Some title".to_owned()));
assert_eq!(config.version, Some("some.version".to_owned()));
assert_eq!(
config.sort_key.as_deref(),
Some(&"some-sort-key".to_owned())
);
assert_eq!(config.options, Some("Some options".to_owned()));
}
#[test]
fn test_path_replacement() {
let config = ConfigBuilder::new("foo.bar", ".bar")
.efi_path("/foo/foo.bar")
.devicetree_path("/baz/baz.qux")
.build();
assert_eq!(
config.efi_path.as_deref(),
Some(&"\\foo\\foo.bar".to_owned())
);
assert_eq!(
config.devicetree_path.as_deref(),
Some(&"\\baz\\baz.qux".to_owned())
);
}
}