use alloc::string::String;
use alloc::vec::Vec;
use serde::Deserialize;
use crate::core::bootables::{BootTarget, GenericBootTarget};
#[cfg(feature = "iso")]
use crate::iso::IsoBootTarget;
#[derive(Debug, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum TargetConfig {
Generic {
label: String,
executable: String,
#[serde(default)]
options: String,
},
#[cfg(feature = "iso")]
Iso {
label: String,
iso_path: String,
executable: Option<String>,
#[serde(default)]
options: String,
},
}
impl TargetConfig {
fn into_boot_target(self) -> BootTarget {
match self {
TargetConfig::Generic {
label,
executable,
options,
} => BootTarget::Generic(GenericBootTarget::new(label, executable, options)),
#[cfg(feature = "iso")]
TargetConfig::Iso {
label,
iso_path,
executable,
options,
} => BootTarget::Iso(IsoBootTarget {
label,
iso_path,
executable,
options,
}),
}
}
}
#[derive(Debug, Deserialize)]
pub struct Config {
pub boot_targets: Vec<TargetConfig>,
}
impl Config {
pub fn load_from_file(path: &str) -> Result<Self, ConfigError> {
let contents = read_file_to_string(path)?;
let config: Config = toml::from_str(&contents).map_err(|e| {
log::error!("TOML parse error: {:?}", e);
ConfigError::ParseError
})?;
Ok(config)
}
pub fn into_boot_targets(self) -> Vec<BootTarget> {
self.boot_targets
.into_iter()
.map(|target| target.into_boot_target())
.collect()
}
}
fn read_file_to_string(path: &str) -> Result<String, ConfigError> {
use uefi::CString16;
use uefi::fs::FileSystem;
let path_cstr = CString16::try_from(path).map_err(|_| ConfigError::InvalidPath)?;
let mut fs = FileSystem::new(
uefi::boot::get_image_file_system(uefi::boot::image_handle())
.map_err(|_| ConfigError::FsError)?,
);
let buf = fs
.read(path_cstr.as_ref())
.map_err(|_| ConfigError::FileNotFound)?;
String::from_utf8(buf).map_err(|_| ConfigError::EncodingError)
}
#[derive(Debug)]
pub enum ConfigError {
InvalidPath,
FileNotFound,
FsError,
EncodingError,
ParseError,
}
impl core::fmt::Display for ConfigError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
ConfigError::InvalidPath => write!(f, "Invalid file path"),
ConfigError::FileNotFound => write!(f, "Config file not found"),
ConfigError::FsError => write!(f, "Filesystem error"),
ConfigError::EncodingError => write!(f, "File encoding error"),
ConfigError::ParseError => write!(f, "TOML parse error"),
}
}
}