Skip to main content

plex_boot/config/
mod.rs

1//! Configuration management for Plex.
2//!
3//! Provides structures and functions to parse the `plex.toml` configuration file
4//! and convert it into boot targets that the application can execute.
5
6use alloc::string::String;
7use alloc::vec::Vec;
8use serde::Deserialize;
9
10use crate::core::bootables::{BootTarget, GenericBootTarget};
11#[cfg(feature = "iso")]
12use crate::iso::IsoBootTarget;
13
14/// Represents a boot target configuration entry in `plex.toml`.
15#[derive(Debug, Deserialize)]
16#[serde(tag = "type", rename_all = "snake_case")]
17pub enum TargetConfig {
18    /// A generic UEFI executable boot target.
19    Generic {
20        /// Display label for the boot menu
21        label: String,
22        /// Path to the executable (relative to boot partition root)
23        executable: String,
24        /// Command line options to pass to the executable
25        #[serde(default)]
26        options: String,
27    },
28
29    /// A boot target representing a bootable ISO file.
30    #[cfg(feature = "iso")]
31    Iso {
32        /// Display label for the boot menu
33        label: String,
34
35        /// Path within the ISO filesystem to the EFI executable.
36        iso_path: String,
37
38        /// Path within the ISO filesystem to the EFI executable.
39        /// `None` to search for executable according to the EFI specification rules.
40        executable: Option<String>,
41
42        /// Command line options to pass to the executable
43        #[serde(default)]
44        options: String,
45    },
46}
47
48impl TargetConfig {
49    fn into_boot_target(self) -> BootTarget {
50        match self {
51            TargetConfig::Generic {
52                label,
53                executable,
54                options,
55            } => BootTarget::Generic(GenericBootTarget::new(label, executable, options)),
56            #[cfg(feature = "iso")]
57            TargetConfig::Iso {
58                label,
59                iso_path,
60                executable,
61                options,
62            } => BootTarget::Iso(IsoBootTarget {
63                label,
64                iso_path,
65                executable,
66                options,
67            }),
68        }
69    }
70}
71
72/// Top-level configuration structure
73#[derive(Debug, Deserialize)]
74pub struct Config {
75    /// List of boot targets
76    pub boot_targets: Vec<TargetConfig>,
77}
78
79impl Config {
80    /// Load configuration from a TOML file at the specified path
81    pub fn load_from_file(path: &str) -> Result<Self, ConfigError> {
82        // Read file from UEFI filesystem
83        let contents = read_file_to_string(path)?;
84
85        // Parse TOML
86        let config: Config = toml::from_str(&contents).map_err(|e| {
87            log::error!("TOML parse error: {:?}", e);
88            ConfigError::ParseError
89        })?;
90
91        Ok(config)
92    }
93
94    /// Convert config into a vector of GenericBootTarget
95    pub fn into_boot_targets(self) -> Vec<BootTarget> {
96        self.boot_targets
97            .into_iter()
98            .map(|target| target.into_boot_target())
99            .collect()
100    }
101}
102
103/// Read a file from the UEFI filesystem into a String
104fn read_file_to_string(path: &str) -> Result<String, ConfigError> {
105    use uefi::CString16;
106    use uefi::fs::FileSystem;
107
108    // Convert path to CString16
109    let path_cstr = CString16::try_from(path).map_err(|_| ConfigError::InvalidPath)?;
110
111    // Get filesystem protocol
112    let mut fs = FileSystem::new(
113        uefi::boot::get_image_file_system(uefi::boot::image_handle())
114            .map_err(|_| ConfigError::FsError)?,
115    );
116
117    // Open and read the file - fs.read() returns Vec<u8> directly
118    let buf = fs
119        .read(path_cstr.as_ref())
120        .map_err(|_| ConfigError::FileNotFound)?;
121
122    // Convert to String (assuming UTF-8)
123    String::from_utf8(buf).map_err(|_| ConfigError::EncodingError)
124}
125
126/// Errors that can occur when loading or parsing the configuration.
127#[derive(Debug)]
128pub enum ConfigError {
129    /// The specified path could not be converted to a valid UEFI path.
130    InvalidPath,
131    /// The configuration file was not found on the filesystem.
132    FileNotFound,
133    /// An error occurred while accessing the filesystem.
134    FsError,
135    /// The file contents could not be decoded as UTF-8.
136    EncodingError,
137    /// The file contents could not be parsed as valid TOML.
138    ParseError,
139}
140
141impl core::fmt::Display for ConfigError {
142    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
143        match self {
144            ConfigError::InvalidPath => write!(f, "Invalid file path"),
145            ConfigError::FileNotFound => write!(f, "Config file not found"),
146            ConfigError::FsError => write!(f, "Filesystem error"),
147            ConfigError::EncodingError => write!(f, "File encoding error"),
148            ConfigError::ParseError => write!(f, "TOML parse error"),
149        }
150    }
151}