use super::{Bootloader, BootloaderFiles, ConfigFile};
use crate::config::BootType;
use crate::core::context::Context;
use crate::core::error::{Error, Result};
use std::path::PathBuf;
#[cfg(feature = "limine")]
use super::GitFetcher;
pub struct LimineBootloader {
repo_url: String,
}
impl LimineBootloader {
const DEFAULT_REPO_URL: &'static str = "https://github.com/limine-bootloader/limine.git";
pub fn new() -> Self {
Self {
repo_url: Self::DEFAULT_REPO_URL.to_string(),
}
}
pub fn with_repo_url(repo_url: String) -> Self {
Self { repo_url }
}
fn get_version<'a>(&self, ctx: &'a Context) -> &'a str {
&ctx.config.bootloader.limine.version
}
#[cfg(feature = "limine")]
fn fetch_limine(&self, ctx: &Context) -> Result<PathBuf> {
let version = self.get_version(ctx);
let cache_dir = ctx.cache_dir.join("bootloaders");
let fetcher = GitFetcher::new(cache_dir, ctx.config.verbose);
fetcher.fetch_ref(&self.repo_url, "limine", version)
}
#[cfg(not(feature = "limine"))]
fn fetch_limine(&self, _ctx: &Context) -> Result<PathBuf> {
Err(Error::feature_not_enabled("limine"))
}
}
impl Default for LimineBootloader {
fn default() -> Self {
Self::new()
}
}
impl Bootloader for LimineBootloader {
fn prepare(&self, ctx: &Context) -> Result<BootloaderFiles> {
let limine_repo = self.fetch_limine(ctx)?;
let mut files = BootloaderFiles::new();
if ctx.config.boot.boot_type.needs_bios() {
let limine_bios = limine_repo.join("limine-bios.sys");
if !limine_bios.exists() {
return Err(Error::bootloader(
"limine-bios.sys not found in Limine repository. \
Make sure you're using a binary release (e.g., v8.x-binary)."
.to_string(),
));
}
files = files.add_system_file(limine_bios, "limine-bios.sys".into());
let limine_bios_cd = limine_repo.join("limine-bios-cd.bin");
if !limine_bios_cd.exists() {
return Err(Error::bootloader(
"limine-bios-cd.bin not found in Limine repository. \
Make sure you're using a binary release (e.g., v8.x-binary)."
.to_string(),
));
}
files = files.add_system_file(limine_bios_cd, "limine-bios-cd.bin".into());
}
if ctx.config.boot.boot_type.needs_uefi() {
let bootx64 = limine_repo.join("BOOTX64.EFI");
if !bootx64.exists() {
return Err(Error::bootloader(
"BOOTX64.EFI not found in Limine repository. \
Make sure you're using a binary release (e.g., v8.x-binary)."
.to_string(),
));
}
files = files.add_uefi_file(bootx64, "efi/boot/bootx64.efi".into());
let limine_uefi_cd = limine_repo.join("limine-uefi-cd.bin");
if !limine_uefi_cd.exists() {
return Err(Error::bootloader(
"limine-uefi-cd.bin not found in Limine repository. \
Make sure you're using a binary release (e.g., v8.x-binary)."
.to_string(),
));
}
files = files.add_system_file(limine_uefi_cd, "limine-uefi-cd.bin".into());
}
files = files.add_system_file(
ctx.executable.clone(),
PathBuf::from("boot").join(
ctx.executable
.file_name()
.ok_or_else(|| Error::config("invalid executable path"))?,
),
);
Ok(files)
}
fn config_files(&self, ctx: &Context) -> Result<Vec<ConfigFile>> {
let mut configs = Vec::new();
let config_path = if let Some(ref path) = ctx.config.bootloader.config_file {
ctx.workspace_root.join(path)
} else {
ctx.workspace_root.join("limine.conf")
};
if config_path.exists() {
configs.push(
ConfigFile::new(config_path, "limine.conf".into())
.with_template_processing(),
);
} else {
return Err(Error::config(format!(
"limine.conf not found at {}. Please create a Limine configuration file.",
config_path.display()
)));
}
Ok(configs)
}
fn boot_type(&self) -> BootType {
BootType::Hybrid
}
fn name(&self) -> &str {
"Limine"
}
fn validate_config(&self, ctx: &Context) -> Result<()> {
let version = self.get_version(ctx);
if version.is_empty() {
return Err(Error::config(
"Limine version not specified in configuration",
));
}
if !version.contains("binary") {
eprintln!(
"Warning: Limine version '{}' may require building from source. \
Consider using a binary release like 'v8.x-binary' for faster builds.",
version
);
}
Ok(())
}
}