use crate::{
BootResult,
config::{Config, builder::ConfigBuilder, parsers::Parsers},
system::fs::UefiFileSystem,
};
use alloc::{borrow::ToOwned, collections::btree_map::BTreeMap, string::String};
use serde::{Deserialize, Serialize};
use uefi::{CStr16, cstr16};
const PERSISTENT_CONFIG_PATH: &CStr16 = cstr16!("\\loader\\bootmgr-rs-saved.conf");
#[derive(Serialize, Deserialize)]
struct SerializableConfig {
title: Option<String>,
version: Option<String>,
machine_id: Option<String>,
sort_key: Option<String>,
options: Option<String>,
devicetree_path: Option<String>,
architecture: Option<String>,
efi_path: Option<String>,
origin: Option<String>,
}
impl From<&Config> for SerializableConfig {
fn from(value: &Config) -> Self {
Self {
title: value.title.clone(), version: value.version.clone(),
machine_id: value.machine_id.as_deref().cloned(),
sort_key: value.sort_key.as_deref().cloned(),
options: value.options.clone(),
devicetree_path: value.devicetree_path.as_deref().cloned(),
architecture: value.architecture.as_deref().cloned(),
efi_path: value.efi_path.as_deref().cloned(),
origin: value.origin.map(|x| x.as_str().into()),
}
}
}
#[derive(Default)]
pub struct PersistentConfig {
configs: BTreeMap<String, SerializableConfig>,
}
impl PersistentConfig {
pub fn new() -> BootResult<Self> {
let mut configs = Self::default();
let mut fs = UefiFileSystem::from_image_fs()?;
if let Ok(content) = fs.read(PERSISTENT_CONFIG_PATH)
&& let Ok(content) = postcard::from_bytes(&content)
{
configs.configs = content;
}
Ok(configs)
}
#[must_use = "Has no effect if the result is unused"]
pub fn contains(&self, config: &Config) -> bool {
self.configs
.get(&config.filename)
.map(|x| x.origin.as_ref())
== Some(config.origin.map(|x| x.as_str().to_owned()).as_ref())
}
pub fn save_to_fs(&self) -> BootResult<()> {
let mut fs = UefiFileSystem::from_image_fs()?;
fs.create(PERSISTENT_CONFIG_PATH)?;
if let Ok(content) = postcard::to_allocvec(&self.configs) {
fs.write(PERSISTENT_CONFIG_PATH, &content)?;
}
Ok(())
}
pub fn swap_config_in_persist<'a>(&'a self, config: &'a mut Config) {
if let Some(persist_config) = self.configs.get(&config.filename)
&& persist_config.origin.as_deref() == config.origin.map(Parsers::as_str)
{
*config = ConfigBuilder::from(&*config)
.assign_if_some(persist_config.title.as_ref(), ConfigBuilder::title)
.assign_if_some(persist_config.version.as_ref(), ConfigBuilder::version)
.assign_if_some(
persist_config.machine_id.as_deref(),
ConfigBuilder::machine_id,
)
.assign_if_some(persist_config.sort_key.as_deref(), ConfigBuilder::sort_key)
.assign_if_some(persist_config.options.as_ref(), ConfigBuilder::options)
.assign_if_some(
persist_config.devicetree_path.as_deref(),
ConfigBuilder::devicetree_path,
)
.assign_if_some(
persist_config.architecture.as_deref(),
ConfigBuilder::architecture,
)
.assign_if_some(persist_config.efi_path.as_deref(), ConfigBuilder::efi_path)
.build();
}
}
pub fn add_config_to_persist(&mut self, config: &Config) {
self.configs
.insert(config.filename.clone(), SerializableConfig::from(config));
}
pub fn remove_config_from_persist(&mut self, config: &Config) {
self.configs.remove(&config.filename);
}
}