//! Sprout configuration descriptions. //! This crate provides all the configuration structures for Sprout. #![no_std] extern crate alloc; use crate::actions::ActionDeclaration; use crate::drivers::DriverDeclaration; use crate::entries::EntryDeclaration; use crate::extractors::ExtractorDeclaration; use crate::generators::GeneratorDeclaration; use crate::phases::PhasesConfiguration; use alloc::collections::BTreeMap; use alloc::string::String; use serde::{Deserialize, Serialize}; pub mod actions; pub mod drivers; pub mod entries; pub mod extractors; pub mod generators; pub mod phases; /// This is the latest version of the sprout configuration format. /// This must be incremented when the configuration breaks compatibility. pub const LATEST_VERSION: u32 = 1; /// The default timeout for the boot menu in seconds. pub const DEFAULT_MENU_TIMEOUT_SECONDS: u64 = 10; /// The Sprout configuration format. #[derive(Serialize, Deserialize, Debug, Clone)] pub struct RootConfiguration { /// The version of the configuration. This should always be declared /// and be the latest version that is supported. If not specified, it is assumed /// the configuration is the latest version. #[serde(default = "latest_version")] pub version: u32, /// Default options for Sprout. #[serde(default)] pub options: OptionsConfiguration, /// Settings for each style of boot menu. #[serde(rename = "menu-styles", default)] pub menu_styles: MenuStylesConfiguration, /// Values to be inserted into the root sprout context. #[serde(default)] pub values: BTreeMap, /// Drivers to load. /// These drivers provide extra functionality like filesystem support to Sprout. /// Each driver has a name which uniquely identifies it inside Sprout. #[serde(default)] pub drivers: BTreeMap, /// Declares the extractors that add values to the sprout context that are calculated /// at runtime. Each extractor has a name which corresponds to the value it will set /// inside the sprout context. #[serde(default)] pub extractors: BTreeMap, /// Declares the actions that can execute operations for sprout. /// Actions are executable modules in sprout that take in specific structured values. /// Actions are responsible for ensuring that passed strings are stamped to replace values /// at runtime. /// Each action has a name that can be referenced by other base concepts like entries. #[serde(default)] pub actions: BTreeMap, /// Declares the entries that are displayed on the boot menu. These entries are static /// but can still use values from the sprout context. #[serde(default)] pub entries: BTreeMap, /// Declares the generators that are used to generate entries at runtime. /// Each generator has its own logic for generating entries, but generally they intake /// a template entry and stamp that template entry over some values determined at runtime. /// Each generator has an associated name used to differentiate it across sprout. #[serde(default)] pub generators: BTreeMap, /// Configures the various phases of sprout. This allows you to hook into specific parts /// of the boot process to execute actions, for example, you can show a boot splash during /// the early phase. #[serde(default)] pub phases: PhasesConfiguration, } /// Options configuration for Sprout, used when the corresponding options are not specified. #[derive(Serialize, Deserialize, Debug, Clone)] pub struct OptionsConfiguration { /// The entry to mark as the default entry, instead of the first entry. #[serde(rename = "default-entry", default)] pub default_entry: Option, /// The timeout of the boot menu in seconds. When unset, the timeout comes from the /// bootloader interface or `loader.conf`, falling back to [DEFAULT_MENU_TIMEOUT_SECONDS]. #[serde(rename = "menu-timeout", default)] pub menu_timeout: Option, /// The style of boot menu to display. #[serde(rename = "menu-style", default)] pub menu_style: MenuStyle, /// Enables autoconfiguration of Sprout based on the environment. #[serde(default)] pub autoconfigure: bool, /// Makes Sprout follow the Boot Loader Specification and systemd-boot exactly where it /// otherwise differs for convenience or safety. For example, the extended boot loader /// partition is always read, a one-shot entry only sets the default entry, and entries with /// missing files are hidden. See docs/config.md for everything it changes. #[serde(rename = "bls-strict-mode", default)] pub bls_strict_mode: bool, } /// Settings for each style of boot menu. #[derive(Serialize, Deserialize, Debug, Default, Clone)] pub struct MenuStylesConfiguration { /// Settings for the graphical boot menu. #[serde(default)] pub graphical: GraphicalMenuConfiguration, } /// Settings for the graphical boot menu. #[derive(Serialize, Deserialize, Debug, Default, Clone)] pub struct GraphicalMenuConfiguration { /// Enables selecting entries with the mouse, and shows the cursor. When disabled, or when /// the firmware has no pointing device, entries are selected with the keyboard only. #[serde(rename = "enable-mouse", default)] pub enable_mouse: bool, } /// The default configuration, which matches an empty configuration file. impl Default for RootConfiguration { fn default() -> Self { Self { version: latest_version(), options: Default::default(), menu_styles: Default::default(), values: Default::default(), drivers: Default::default(), extractors: Default::default(), actions: Default::default(), entries: Default::default(), generators: Default::default(), phases: Default::default(), } } } /// The default options, which match an empty options section. impl Default for OptionsConfiguration { fn default() -> Self { Self { default_entry: None, menu_timeout: None, menu_style: Default::default(), autoconfigure: false, bls_strict_mode: false, } } } /// The style of boot menu to display. #[derive(Serialize, Deserialize, Debug, Default, Clone, Copy, PartialEq, Eq)] pub enum MenuStyle { /// A basic menu that prints the entries and selects them by number. #[serde(rename = "basic")] Basic, /// A simple full-screen menu that selects entries with the arrow keys. #[default] #[serde(rename = "simple")] Simple, /// A graphical menu that selects entries with the keyboard or the mouse. #[serde(rename = "graphical")] Graphical, } /// Get the latest version of the Sprout configuration format. pub fn latest_version() -> u32 { LATEST_VERSION } #[cfg(test)] mod tests { use super::*; use serde::de::value::{Error, MapDeserializer}; /// Deserialize a configuration from an empty map, like an empty configuration file. fn empty() -> RootConfiguration { let map = MapDeserializer::<_, Error>::new(core::iter::empty::<(&str, &str)>()); RootConfiguration::deserialize(map).expect("empty configuration should deserialize") } #[test] fn default_matches_empty_configuration() { let default = RootConfiguration::default(); let empty = empty(); assert_eq!(default.version, empty.version); assert_eq!(default.options.menu_timeout, empty.options.menu_timeout); assert_eq!(default.options.menu_style, empty.options.menu_style); assert_eq!(default.options.default_entry, empty.options.default_entry); assert_eq!(default.options.autoconfigure, empty.options.autoconfigure); } #[test] fn graphical_mouse_is_off_by_default() { assert!( !RootConfiguration::default() .menu_styles .graphical .enable_mouse ); assert!(!empty().menu_styles.graphical.enable_mouse); } #[test] fn bls_strict_mode_is_off_by_default() { assert!(!RootConfiguration::default().options.bls_strict_mode); assert!(!empty().options.bls_strict_mode); } #[test] fn menu_timeout_is_unset_by_default() { assert_eq!(RootConfiguration::default().options.menu_timeout, None); assert_eq!(empty().options.menu_timeout, None); } #[test] fn default_menu_timeout_is_not_zero() { assert_ne!(DEFAULT_MENU_TIMEOUT_SECONDS, 0); } }