cargo_image_runner/bootloader/
fetcher.rs1use crate::core::error::{Error, Result};
2use std::path::{Path, PathBuf};
3
4#[cfg(feature = "limine")]
6pub struct GitFetcher {
7 cache_dir: PathBuf,
8 verbose: bool,
9}
10
11#[cfg(feature = "limine")]
12impl GitFetcher {
13 pub fn new(cache_dir: PathBuf, verbose: bool) -> Self {
15 Self { cache_dir, verbose }
16 }
17
18 pub fn fetch(&self, url: &str, name: &str, branch: &str) -> Result<PathBuf> {
23 let repo_path = self.cache_dir.join(name);
24
25 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 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 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 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 let repo = git2::Repository::clone(url, &repo_path)
71 .map_err(|e| Error::bootloader(format!("failed to clone {}: {}", url, e)))?;
72
73 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 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#[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}