use core::ops::Deref;
use alloc::{borrow::ToOwned, string::String};
use thiserror::Error;
use uefi::{Handle, boot, proto::media::fs::SimpleFileSystem};
use crate::system::{
fs::check_path_valid,
helper::{check_machine_id_valid, check_sort_key_valid, normalize_path},
};
#[derive(Error, Debug)]
pub enum TypeError {
#[error("\"{0}\" is not a valid machine id")]
MachineId(String),
#[error("\"{0}\" is not a valid sort key")]
SortKey(String),
#[error("\"{0}\" is not a valid path")]
Path(String),
#[error("\"{0}\" is not a valid architecture")]
Architecture(String),
#[error("\"{0:?}\" is not a valid Handle that supports SimpleFileSystem")]
Handle(Handle),
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct MachineId(String);
impl MachineId {
pub fn new(machine_id: &str) -> Result<Self, TypeError> {
if check_machine_id_valid(machine_id) {
Ok(Self(machine_id.to_ascii_lowercase()))
} else {
Err(TypeError::MachineId(machine_id.to_owned()))
}
}
}
impl Deref for MachineId {
type Target = String;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct SortKey(String);
impl SortKey {
pub fn new(sort_key: &str) -> Result<Self, TypeError> {
if check_sort_key_valid(sort_key) {
Ok(Self(sort_key.to_owned()))
} else {
Err(TypeError::SortKey(sort_key.to_owned()))
}
}
}
impl Deref for SortKey {
type Target = String;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct DevicetreePath(String);
impl DevicetreePath {
pub fn new(devicetree: &str) -> Result<Self, TypeError> {
let devicetree = normalize_path(devicetree);
if check_path_valid(&devicetree) {
Ok(Self(devicetree))
} else {
Err(TypeError::Path(devicetree))
}
}
}
impl Deref for DevicetreePath {
type Target = String;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Architecture(String);
impl Architecture {
pub fn new(arch: &str) -> Result<Self, TypeError> {
match arch {
"x86" | "x64" | "arm" | "aa64" => Ok(Self(arch.to_owned())),
_ => Err(TypeError::Architecture(arch.to_owned())),
}
}
}
impl Deref for Architecture {
type Target = String;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct EfiPath(String);
impl EfiPath {
pub fn new(efi: &str) -> Result<Self, TypeError> {
let efi = normalize_path(efi);
if check_path_valid(&efi) {
Ok(Self(efi))
} else {
Err(TypeError::Path(efi))
}
}
}
impl Deref for EfiPath {
type Target = String;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct FsHandle(Handle);
impl FsHandle {
pub fn new(handle: Handle) -> Result<Self, TypeError> {
let params = boot::OpenProtocolParams {
handle,
agent: boot::image_handle(),
controller: None,
};
match boot::test_protocol::<SimpleFileSystem>(params) {
Ok(true) => Ok(Self(handle)),
_ => Err(TypeError::Handle(handle)),
}
}
}
impl Deref for FsHandle {
type Target = Handle;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sort_key() {
let sort_key = SortKey::new("a-valid-sort-key");
assert!(matches!(
sort_key.as_deref().map(String::as_str),
Ok("a-valid-sort-key")
));
let sort_key = SortKey::new(";'[];\\[]-=invalid sort key");
assert!(sort_key.is_err());
}
#[test]
fn test_machine_id() {
let machine_id = MachineId::new("93274530989549038301177646597349");
assert!(matches!(
machine_id.as_deref().map(String::as_str),
Ok("93274530989549038301177646597349")
));
let machine_id = MachineId::new("invalidthing");
assert!(machine_id.is_err());
let machine_id = MachineId::new("1");
assert!(machine_id.is_err());
let machine_id = MachineId::new("1234567890abcdefghijklmnopqrstu");
assert!(machine_id.is_err());
}
#[test]
fn test_dtb_path() {
let devicetree = DevicetreePath::new("\\foo\\bar.dtb");
assert!(matches!(
devicetree.as_deref().map(String::as_str),
Ok("\\foo\\bar.dtb")
));
let devicetree = DevicetreePath::new("\\** / : ???? .dtb");
assert!(devicetree.is_err());
}
#[test]
fn test_arch() {
let arch = Architecture::new("x64");
assert!(matches!(arch.as_deref().map(String::as_str), Ok("x64")));
let arch = Architecture::new("notreal64");
assert!(arch.is_err());
}
#[test]
fn test_efi_path() {
let efi = EfiPath::new("\\foo\\bar.efi");
assert!(matches!(
efi.as_deref().map(String::as_str),
Ok("\\foo\\bar.efi")
));
let efi = EfiPath::new(":somethinginvalid*\">><?????;.ef");
assert!(efi.is_err());
}
}