mirror of
https://github.com/edera-dev/sprout.git
synced 2025-12-19 17:10:17 +00:00
introduce the use of anyhow to no longer use panic
This commit is contained in:
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
29
src/main.rs
29
src/main.rs
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
38
src/utils.rs
38
src/utils.rs
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user