introduce the use of anyhow to no longer use panic

This commit is contained in:
2025-10-11 14:35:29 -07:00
parent 449eb85ab8
commit fc239ea54d
14 changed files with 121 additions and 84 deletions

View File

@@ -1,4 +1,5 @@
use crate::context::Context;
use crate::context::SproutContext;
use anyhow::{Result, bail};
use serde::{Deserialize, Serialize};
use std::rc::Rc;
@@ -19,25 +20,25 @@ pub struct ActionDeclaration {
pub splash: Option<splash::SplashConfiguration>,
}
pub fn execute(context: Rc<Context>, name: impl AsRef<str>) {
pub fn execute(context: Rc<SproutContext>, name: impl AsRef<str>) -> Result<()> {
let Some(action) = context.root().actions().get(name.as_ref()) else {
panic!("unknown action: {}", name.as_ref());
bail!("unknown action '{}'", name.as_ref());
};
let context = context.finalize().freeze();
if let Some(chainload) = &action.chainload {
chainload::chainload(context.clone(), chainload);
return;
chainload::chainload(context.clone(), chainload)?;
return Ok(());
} else if let Some(print) = &action.print {
print::print(context.clone(), print);
return;
print::print(context.clone(), print)?;
return Ok(());
}
#[cfg(feature = "splash")]
if let Some(splash) = &action.splash {
splash::splash(context.clone(), splash);
return;
splash::splash(context.clone(), splash)?;
return Ok(());
}
panic!("unknown action configuration");
bail!("unknown action configuration");
}

View File

@@ -1,5 +1,6 @@
use crate::context::Context;
use crate::context::SproutContext;
use crate::utils;
use anyhow::{Context, Result, bail};
use log::info;
use serde::{Deserialize, Serialize};
use std::rc::Rc;
@@ -14,19 +15,19 @@ pub struct ChainloadConfiguration {
pub options: Vec<String>,
}
pub fn chainload(context: Rc<Context>, configuration: &ChainloadConfiguration) {
pub fn chainload(context: Rc<SproutContext>, configuration: &ChainloadConfiguration) -> Result<()> {
let sprout_image = uefi::boot::image_handle();
let image_device_path_protocol =
uefi::boot::open_protocol_exclusive::<LoadedImageDevicePath>(sprout_image)
.expect("unable to open loaded image device path protocol");
.context("unable to open loaded image device path protocol")?;
let mut full_path = utils::device_path_root(&image_device_path_protocol);
let mut full_path = utils::device_path_root(&image_device_path_protocol)?;
full_path.push_str(&context.stamp(&configuration.path));
info!("path={}", full_path);
let device_path = utils::text_to_device_path(&full_path);
let device_path = utils::text_to_device_path(&full_path)?;
let image = uefi::boot::load_image(
sprout_image,
@@ -35,10 +36,10 @@ pub fn chainload(context: Rc<Context>, configuration: &ChainloadConfiguration) {
boot_policy: uefi::proto::BootPolicy::ExactMatch,
},
)
.expect("failed to load image");
.context("failed to load image")?;
let mut loaded_image_protocol = uefi::boot::open_protocol_exclusive::<LoadedImage>(image)
.expect("unable to open loaded image protocol");
.context("unable to open loaded image protocol")?;
let options = configuration
.options
@@ -49,12 +50,12 @@ pub fn chainload(context: Rc<Context>, configuration: &ChainloadConfiguration) {
if !options.is_empty() {
let options = Box::new(
CString16::try_from(&options[..])
.expect("unable to convert chainloader options to CString16"),
.context("unable to convert chainloader options to CString16")?,
);
info!("options={}", options);
if options.num_bytes() > u32::MAX as usize {
panic!("chainloader options too large");
bail!("chainloader options too large");
}
// SAFETY: options size is checked to validate it is safe to pass.
@@ -68,5 +69,6 @@ pub fn chainload(context: Rc<Context>, configuration: &ChainloadConfiguration) {
let (base, size) = loaded_image_protocol.info();
info!("loaded image base={:#x} size={:#x}", base.addr(), size);
uefi::boot::start_image(image).expect("failed to start image");
uefi::boot::start_image(image).context("failed to start image")?;
Ok(())
}

View File

@@ -1,4 +1,5 @@
use crate::context::Context;
use crate::context::SproutContext;
use anyhow::Result;
use serde::{Deserialize, Serialize};
use std::rc::Rc;
@@ -8,6 +9,7 @@ pub struct PrintConfiguration {
pub text: String,
}
pub fn print(context: Rc<Context>, configuration: &PrintConfiguration) {
pub fn print(context: Rc<SproutContext>, configuration: &PrintConfiguration) -> Result<()> {
println!("{}", context.stamp(&configuration.text));
Ok(())
}

View File

@@ -1,6 +1,7 @@
use crate::context::Context;
use crate::context::SproutContext;
use crate::utils::framebuffer::Framebuffer;
use crate::utils::read_file_contents;
use anyhow::{Context, Result};
use image::imageops::{FilterType, resize};
use image::math::Rect;
use image::{DynamicImage, ImageBuffer, ImageFormat, ImageReader, Rgba};
@@ -22,11 +23,11 @@ pub fn default_splash_time() -> u32 {
5
}
fn setup_graphics() -> ScopedProtocol<GraphicsOutput> {
fn setup_graphics() -> Result<ScopedProtocol<GraphicsOutput>> {
let gop_handle = uefi::boot::get_handle_for_protocol::<GraphicsOutput>()
.expect("failed to get graphics output");
.context("failed to get graphics output")?;
uefi::boot::open_protocol_exclusive::<GraphicsOutput>(gop_handle)
.expect("failed to open graphics output")
.context("failed to open graphics output")
}
fn fit_to_frame(image: &DynamicImage, frame: Rect) -> Rect {
@@ -67,8 +68,8 @@ fn resize_to_fit(image: &DynamicImage, frame: Rect) -> ImageBuffer<Rgba<u8>, Vec
resize(&image, frame.width, frame.height, FilterType::Lanczos3)
}
fn draw(image: DynamicImage) {
let mut gop = setup_graphics();
fn draw(image: DynamicImage) -> Result<()> {
let mut gop = setup_graphics()?;
let (width, height) = gop.current_mode_info().resolution();
let display_frame = Rect {
x: 0,
@@ -89,15 +90,17 @@ fn draw(image: DynamicImage) {
fb.blue = pixel[2];
}
framebuffer.blit(&mut gop);
framebuffer.blit(&mut gop)?;
Ok(())
}
pub fn splash(context: Rc<Context>, configuration: &SplashConfiguration) {
pub fn splash(context: Rc<SproutContext>, configuration: &SplashConfiguration) -> Result<()> {
let image = context.stamp(&configuration.image);
let image = read_file_contents(&image);
let image = read_file_contents(&image)?;
let image = ImageReader::with_format(Cursor::new(image), ImageFormat::Png)
.decode()
.expect("failed to decode splash image");
draw(image);
.context("failed to decode splash image")?;
draw(image)?;
std::thread::sleep(Duration::from_secs(configuration.time as u64));
Ok(())
}

View File

@@ -1,6 +1,8 @@
use crate::actions::ActionDeclaration;
use crate::generators::GeneratorDeclaration;
use crate::utils;
use anyhow::Context;
use anyhow::Result;
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
@@ -43,9 +45,12 @@ pub struct PhaseConfiguration {
pub values: BTreeMap<String, String>,
}
pub fn load() -> RootConfiguration {
let content = utils::read_file_contents("sprout.toml");
toml::from_slice(&content).expect("unable to parse sprout.toml file")
pub fn load() -> Result<RootConfiguration> {
let content =
utils::read_file_contents("sprout.toml").context("unable to read sprout.toml file")?;
let config: RootConfiguration =
toml::from_slice(&content).context("unable to parse sprout.toml file")?;
Ok(config)
}
pub fn latest_version() -> u32 {

View File

@@ -21,13 +21,13 @@ impl RootContext {
}
}
pub struct Context {
pub struct SproutContext {
root: Rc<RootContext>,
parent: Option<Rc<Context>>,
parent: Option<Rc<SproutContext>>,
values: BTreeMap<String, String>,
}
impl Context {
impl SproutContext {
pub fn new(root: RootContext) -> Self {
Self {
root: Rc::new(root),
@@ -80,7 +80,7 @@ impl Context {
}
}
pub fn fork(self: &Rc<Context>) -> Self {
pub fn fork(self: &Rc<SproutContext>) -> Self {
Self {
root: self.root.clone(),
parent: Some(self.clone()),
@@ -88,11 +88,11 @@ impl Context {
}
}
pub fn freeze(self) -> Rc<Context> {
pub fn freeze(self) -> Rc<SproutContext> {
Rc::new(self)
}
pub fn finalize(&self) -> Context {
pub fn finalize(&self) -> SproutContext {
let mut current_values = self.all_values();
loop {

View File

@@ -1,6 +1,8 @@
use crate::config::EntryDeclaration;
use crate::context::Context;
use crate::context::SproutContext;
use crate::generators::matrix::MatrixConfiguration;
use anyhow::Result;
use anyhow::bail;
use serde::{Deserialize, Serialize};
use std::rc::Rc;
@@ -13,12 +15,12 @@ pub struct GeneratorDeclaration {
}
pub fn generate(
context: Rc<Context>,
context: Rc<SproutContext>,
generator: &GeneratorDeclaration,
) -> Vec<(Rc<Context>, EntryDeclaration)> {
) -> Result<Vec<(Rc<SproutContext>, EntryDeclaration)>> {
if let Some(matrix) = &generator.matrix {
matrix::generate(context, matrix)
} else {
panic!("unknown action configuration");
bail!("unknown action configuration");
}
}

View File

@@ -1,5 +1,6 @@
use crate::config::EntryDeclaration;
use crate::context::Context;
use crate::context::SproutContext;
use anyhow::Result;
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
use std::rc::Rc;
@@ -34,9 +35,9 @@ fn build_matrix(input: &BTreeMap<String, Vec<String>>) -> Vec<BTreeMap<String, S
}
pub fn generate(
context: Rc<Context>,
context: Rc<SproutContext>,
matrix: &MatrixConfiguration,
) -> Vec<(Rc<Context>, EntryDeclaration)> {
) -> Result<Vec<(Rc<SproutContext>, EntryDeclaration)>> {
let combinations = build_matrix(&matrix.values);
let mut entries = Vec::new();
@@ -55,5 +56,5 @@ pub fn generate(
entries.push((context, entry));
}
entries
Ok(entries)
}

View File

@@ -1,7 +1,8 @@
#![feature(uefi_std)]
use crate::config::PhaseConfiguration;
use crate::context::{Context, RootContext};
use crate::context::{RootContext, SproutContext};
use anyhow::{Context, Result, bail};
use log::info;
use std::rc::Rc;
@@ -12,35 +13,37 @@ pub mod generators;
pub mod setup;
pub mod utils;
fn phase(context: Rc<Context>, phase: &[PhaseConfiguration]) {
fn phase(context: Rc<SproutContext>, phase: &[PhaseConfiguration]) -> Result<()> {
for item in phase {
let mut context = context.fork();
context.insert(&item.values);
let context = context.freeze();
for action in item.actions.iter() {
actions::execute(context.clone(), action);
actions::execute(context.clone(), action)
.context(format!("failed to execute action '{}'", action))?;
}
}
Ok(())
}
fn main() {
setup::init();
fn main() -> Result<()> {
setup::init()?;
let config = config::load();
let config = config::load()?;
if config.version > config::latest_version() {
panic!("unsupported configuration version: {}", config.version);
if config.version != config::latest_version() {
bail!("unsupported configuration version: {}", config.version);
}
let mut root = RootContext::new();
root.actions_mut().extend(config.actions.clone());
let mut context = Context::new(root);
let mut context = SproutContext::new(root);
context.insert(&config.values);
let context = context.freeze();
phase(context.clone(), &config.phases.startup);
phase(context.clone(), &config.phases.startup)?;
let mut all_entries = Vec::new();
@@ -51,7 +54,7 @@ fn main() {
for (_name, generator) in config.generators {
let context = context.fork().freeze();
for entry in generators::generate(context.clone(), &generator) {
for entry in generators::generate(context.clone(), &generator)? {
all_entries.push(entry);
}
}
@@ -77,6 +80,8 @@ fn main() {
for action in &entry.actions {
let action = context.stamp(action);
actions::execute(context.clone(), action);
actions::execute(context.clone(), &action)
.context(format!("failed to execute action '{}'", action))?;
}
Ok(())
}

View File

@@ -1,6 +1,7 @@
use anyhow::{Context, Result};
use std::os::uefi as uefi_std;
pub fn init() {
pub fn init() -> Result<()> {
let system_table = uefi_std::env::system_table();
let image_handle = uefi_std::env::image_handle();
@@ -10,9 +11,10 @@ pub fn init() {
unsafe {
uefi::table::set_system_table(system_table.as_ptr().cast());
let handle = uefi::Handle::from_ptr(image_handle.as_ptr().cast())
.expect("unable to resolve image handle");
.context("unable to resolve image handle")?;
uefi::boot::set_image_handle(handle);
}
uefi::helpers::init().expect("failed to initialize uefi");
uefi::helpers::init().context("failed to initialize uefi")?;
Ok(())
}

View File

@@ -1,3 +1,4 @@
use anyhow::{Context, Result};
use uefi::CString16;
use uefi::fs::{FileSystem, Path};
use uefi::proto::device_path::text::{AllowShortcuts, DevicePathFromText, DisplayOnly};
@@ -6,28 +7,30 @@ use uefi::proto::media::fs::SimpleFileSystem;
pub mod framebuffer;
pub fn text_to_device_path(path: &str) -> PoolDevicePath {
let path = CString16::try_from(path).expect("unable to convert path to CString16");
pub fn text_to_device_path(path: &str) -> Result<PoolDevicePath> {
let path = CString16::try_from(path).context("unable to convert path to CString16")?;
let device_path_from_text = uefi::boot::open_protocol_exclusive::<DevicePathFromText>(
uefi::boot::get_handle_for_protocol::<DevicePathFromText>()
.expect("no device path from text protocol"),
.context("no device path from text protocol")?,
)
.expect("unable to open device path from text protocol");
.context("unable to open device path from text protocol")?;
device_path_from_text
.convert_text_to_device_path(&path)
.expect("unable to convert text to device path")
.context("unable to convert text to device path")
}
pub fn device_path_root(path: &DevicePath) -> String {
pub fn device_path_root(path: &DevicePath) -> Result<String> {
let mut path = path
.node_iter()
.filter_map(|item| {
let item = item
.to_string(DisplayOnly(false), AllowShortcuts(false))
.expect("unable to convert device path to string");
if item.to_string().contains("(") {
Some(item)
let item = item.to_string(DisplayOnly(false), AllowShortcuts(false));
if item
.as_ref()
.map(|item| item.to_string().contains("("))
.unwrap_or(false)
{
Some(item.unwrap_or_default())
} else {
None
}
@@ -36,16 +39,17 @@ pub fn device_path_root(path: &DevicePath) -> String {
.collect::<Vec<_>>()
.join("/");
path.push('/');
path
Ok(path)
}
pub fn read_file_contents(path: &str) -> Vec<u8> {
pub fn read_file_contents(path: &str) -> Result<Vec<u8>> {
let fs = uefi::boot::open_protocol_exclusive::<SimpleFileSystem>(
uefi::boot::get_handle_for_protocol::<SimpleFileSystem>().expect("no filesystem protocol"),
uefi::boot::get_handle_for_protocol::<SimpleFileSystem>()
.context("no filesystem protocol")?,
)
.expect("unable to open filesystem protocol");
.context("unable to open filesystem protocol")?;
let mut fs = FileSystem::new(fs);
let path = CString16::try_from(path).expect("unable to convert path to CString16");
let path = CString16::try_from(path).context("unable to convert path to CString16")?;
let content = fs.read(Path::new(&path));
content.expect("unable to read file contents")
content.context("unable to read file contents")
}

View File

@@ -1,3 +1,4 @@
use anyhow::{Context, Result};
use uefi::proto::console::gop::{BltOp, BltPixel, BltRegion, GraphicsOutput};
pub struct Framebuffer {
@@ -19,13 +20,14 @@ impl Framebuffer {
self.pixels.get_mut(y * self.width + x)
}
pub fn blit(&self, gop: &mut GraphicsOutput) {
pub fn blit(&self, gop: &mut GraphicsOutput) -> Result<()> {
gop.blt(BltOp::BufferToVideo {
buffer: &self.pixels,
src: BltRegion::Full,
dest: (0, 0),
dims: (self.width, self.height),
})
.expect("failed to blit framebuffer");
.context("failed to blit framebuffer")?;
Ok(())
}
}