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