feat(options): --boot now supports selecting by entry name or index, not just title

This commit is contained in:
2025-10-24 16:32:48 -07:00
parent d3f9e876fb
commit 911b617d92
5 changed files with 118 additions and 35 deletions

View File

@@ -2,6 +2,7 @@
#![feature(uefi_std)]
use crate::context::{RootContext, SproutContext};
use crate::entries::BootableEntry;
use crate::options::SproutOptions;
use crate::options::parser::OptionsRepresentable;
use crate::phases::phase;
@@ -109,61 +110,73 @@ fn main() -> Result<()> {
// Execute the late phase.
phase(context.clone(), &config.phases.startup).context("unable to execute startup phase")?;
let mut staged_entries = Vec::new();
let mut entries = Vec::new();
// Insert all the static entries from the configuration into the entry list.
for (_name, entry) in config.entries {
for (name, entry) in config.entries {
// Associate the main context with the static entry.
staged_entries.push((context.clone(), entry));
entries.push(BootableEntry::new(
name,
context.stamp(&entry.title),
context.clone(),
entry,
));
}
// Run all the generators declared in the configuration.
for (_name, generator) in config.generators {
for (name, generator) in config.generators {
let context = context.fork().freeze();
// We will prefix all entries with [name]-.
let prefix = format!("{}-", name);
// Add all the entries generated by the generator to the entry list.
// The generator specifies the context associated with the entry.
for entry in generators::generate(context.clone(), &generator)? {
staged_entries.push(entry);
for mut entry in generators::generate(context.clone(), &generator)? {
entry.prepend_name_prefix(&prefix);
entries.push(entry);
}
}
// Build a list of all the final boot entries.
let mut final_entries = Vec::new();
for (context, entry) in staged_entries {
for entry in &mut entries {
let mut context = context.fork();
// Insert the values from the entry configuration into the
// sprout context to use with the entry itself.
context.insert(&entry.values);
context.insert(&entry.declaration().values);
let context = context.finalize().freeze();
// Insert the entry configuration into final boot entries with the extended context.
final_entries.push((context, entry));
// Provide the new context to the bootable entry.
entry.swap_context(context);
// Restamp the title with any values.
entry.restamp_title();
}
// TODO(azenla): Implement boot menu here.
// For now, we just print all of the entries.
info!("entries:");
for (index, (context, entry)) in final_entries.iter().enumerate() {
let title = context.stamp(&entry.title);
info!(" entry {}: {}", index + 1, title);
for (index, entry) in entries.iter().enumerate() {
let title = context.stamp(&entry.declaration().title);
info!(" entry {} [{}]: {}", index, entry.name(), title);
}
// Execute the late phase.
phase(context.clone(), &config.phases.late).context("unable to execute late phase")?;
// Use the boot option if possible, otherwise pick the first entry.
let (context, entry) = if let Some(ref boot) = context.root().options().boot {
final_entries
let entry = if let Some(ref boot) = context.root().options().boot {
entries
.iter()
.find(|(_context, entry)| &entry.title == boot)
.enumerate()
.find(|(index, entry)| {
entry.name() == boot || entry.title() == boot || &index.to_string() == boot
})
.context(format!("unable to find entry: {boot}"))?
.1 // select the bootable entry.
} else {
final_entries.first().context("no entries found")?
entries.first().context("no entries found")?
};
// Execute all the actions for the selected entry.
for action in &entry.actions {
for action in &entry.declaration().actions {
let action = context.stamp(action);
actions::execute(context.clone(), &action)
.context(format!("unable to execute action '{}'", action))?;