Skip to main content

cargo_image_runner/bootloader/
fetcher.rs

1use crate::core::error::{Error, Result};
2use std::path::{Path, PathBuf};
3
4/// Git repository fetcher for bootloader files.
5#[cfg(feature = "limine")]
6pub struct GitFetcher {
7    cache_dir: PathBuf,
8}
9
10#[cfg(feature = "limine")]
11impl GitFetcher {
12    /// Create a new git fetcher with the specified cache directory.
13    pub fn new(cache_dir: PathBuf) -> Self {
14        Self { cache_dir }
15    }
16
17    /// Fetch a git repository to the cache directory.
18    ///
19    /// If the repository already exists, it will be used as-is.
20    /// If not, it will be cloned from the URL.
21    pub fn fetch(&self, url: &str, name: &str, branch: &str) -> Result<PathBuf> {
22        let repo_path = self.cache_dir.join(name);
23
24        // If directory exists, assume it's already fetched
25        if repo_path.exists() {
26            println!("Using cached {} from {}", name, repo_path.display());
27            return Ok(repo_path);
28        }
29
30        println!("Fetching {} from {}...", name, url);
31        std::fs::create_dir_all(&self.cache_dir)?;
32
33        // Clone the repository
34        let mut builder = git2::build::RepoBuilder::new();
35        builder.branch(branch);
36
37        builder
38            .clone(url, &repo_path)
39            .map_err(|e| Error::bootloader(format!("failed to clone {}: {}", url, e)))?;
40
41        println!("Fetched {} successfully", name);
42        Ok(repo_path)
43    }
44
45    /// Fetch a specific commit or tag from a repository.
46    pub fn fetch_ref(&self, url: &str, name: &str, git_ref: &str) -> Result<PathBuf> {
47        let repo_path = self.cache_dir.join(format!("{}-{}", name, git_ref));
48
49        // If directory exists, assume it's already fetched
50        if repo_path.exists() {
51            println!("Using cached {} ({}) from {}", name, git_ref, repo_path.display());
52            return Ok(repo_path);
53        }
54
55        println!("Fetching {} ({}) from {}...", name, git_ref, url);
56        std::fs::create_dir_all(&self.cache_dir)?;
57
58        // Clone the repository
59        let repo = git2::Repository::clone(url, &repo_path)
60            .map_err(|e| Error::bootloader(format!("failed to clone {}: {}", url, e)))?;
61
62        // Checkout the specific ref
63        let (object, reference) = repo.revparse_ext(git_ref)
64            .map_err(|e| Error::bootloader(format!("failed to find ref {}: {}", git_ref, e)))?;
65
66        repo.checkout_tree(&object, None)
67            .map_err(|e| Error::bootloader(format!("failed to checkout {}: {}", git_ref, e)))?;
68
69        match reference {
70            Some(gref) => repo.set_head(gref.name().unwrap()),
71            None => repo.set_head_detached(object.id()),
72        }
73        .map_err(|e| Error::bootloader(format!("failed to set HEAD: {}", e)))?;
74
75        println!("Fetched {} ({}) successfully", name, git_ref);
76        Ok(repo_path)
77    }
78
79    /// Copy files from the fetched repository to a destination.
80    pub fn copy_files(&self, repo_path: &Path, files: &[&str], dest_dir: &Path) -> Result<Vec<PathBuf>> {
81        let mut copied = Vec::new();
82
83        for file in files {
84            let src = repo_path.join(file);
85            if !src.exists() {
86                return Err(Error::bootloader(format!(
87                    "required file not found in repository: {}",
88                    file
89                )));
90            }
91
92            let dest = dest_dir.join(
93                Path::new(file)
94                    .file_name()
95                    .ok_or_else(|| Error::bootloader("invalid file path"))?,
96            );
97
98            std::fs::create_dir_all(dest_dir)?;
99            std::fs::copy(&src, &dest)?;
100            copied.push(dest);
101        }
102
103        Ok(copied)
104    }
105}
106
107// Stub implementation when limine feature is disabled
108#[cfg(not(feature = "limine"))]
109pub struct GitFetcher;
110
111#[cfg(not(feature = "limine"))]
112impl GitFetcher {
113    pub fn new(_cache_dir: PathBuf) -> Self {
114        Self
115    }
116
117    pub fn fetch(&self, _url: &str, _name: &str, _branch: &str) -> Result<PathBuf> {
118        Err(Error::feature_not_enabled("limine"))
119    }
120
121    pub fn fetch_ref(&self, _url: &str, _name: &str, _git_ref: &str) -> Result<PathBuf> {
122        Err(Error::feature_not_enabled("limine"))
123    }
124
125    pub fn copy_files(&self, _repo_path: &Path, _files: &[&str], _dest_dir: &Path) -> Result<Vec<PathBuf>> {
126        Err(Error::feature_not_enabled("limine"))
127    }
128}