Skip to content

Commit 5d848b5

Browse files
committed
Add configuration support for backend selection
Add git_backend field to ProtofetchConfig and support loading from config file or PROTOFETCH_GIT_BACKEND environment variable.
1 parent 75dcbc5 commit 5d848b5

3 files changed

Lines changed: 62 additions & 16 deletions

File tree

src/api/builder.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,8 @@ impl ProtofetchBuilder {
9696
let cache = ProtofetchGitCache::new(
9797
cache_directory,
9898
config.default_protocol,
99-
GitBackendType::default(),
100-
None,
99+
config.git_backend,
100+
config.git_executable,
101101
)?;
102102

103103
// Build the effective ParallelConfig: defaults < config < explicit builder calls.

src/config.rs

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,16 @@ use anyhow::{bail, Context};
44
use log::{debug, trace};
55
use serde::Deserialize;
66

7-
use crate::model::protofetch::Protocol;
7+
use crate::{git::backend::GitBackendType, model::protofetch::Protocol};
88

99
#[derive(Debug)]
1010
pub struct ProtofetchConfig {
1111
pub cache_dir: PathBuf,
1212
pub default_protocol: Protocol,
1313
pub jobs: Option<usize>,
1414
pub copy_jobs: Option<usize>,
15+
pub git_backend: GitBackendType,
16+
pub git_executable: Option<String>,
1517
}
1618

1719
impl ProtofetchConfig {
@@ -27,6 +29,8 @@ impl ProtofetchConfig {
2729
default_protocol: resolve_default_protocol(raw_config.git.protocol)?,
2830
jobs: raw_config.jobs,
2931
copy_jobs: raw_config.copy_jobs,
32+
git_backend: raw_config.git.backend.unwrap_or_default(),
33+
git_executable: raw_config.git.executable_path,
3034
};
3135
trace!("Loaded configuration: {:?}", config);
3236

@@ -54,13 +58,15 @@ struct CacheConfig {
5458
#[derive(Default, Debug, Deserialize, PartialEq, Eq)]
5559
struct GitConfig {
5660
protocol: Option<Protocol>,
61+
backend: Option<GitBackendType>,
62+
executable_path: Option<String>,
5763
}
5864

5965
impl RawConfig {
6066
fn load(
6167
config_dir: Option<PathBuf>,
6268
config_override: Option<toml::Table>,
63-
env_override: Option<HashMap<String, String>>,
69+
env_override: Option<HashMap<&str, &str>>,
6470
) -> anyhow::Result<Self> {
6571
// Base config: override table (tests) takes priority; otherwise read
6672
// the optional config.toml file; fall back to defaults.
@@ -87,18 +93,19 @@ impl RawConfig {
8793
// the source so tests inject a HashMap and prod reads std::env.
8894
fn get<T>(
8995
key: &str,
90-
env_override: &Option<HashMap<String, String>>,
96+
env_override: &Option<HashMap<&str, &str>>,
9197
) -> anyhow::Result<Option<T>>
9298
where
9399
T: FromStr,
94-
T::Err: std::error::Error + Send + Sync + 'static,
100+
T::Err: Into<anyhow::Error> + Send + Sync + 'static,
95101
{
96102
let raw = match env_override {
97-
Some(map) => map.get(key).cloned(),
103+
Some(map) => map.get(key).copied().map(ToOwned::to_owned),
98104
None => std::env::var(key).ok(),
99105
};
100106
raw.map(|v| {
101107
v.parse()
108+
.map_err(Into::into)
102109
.with_context(|| format!("invalid value for {key}"))
103110
})
104111
.transpose()
@@ -110,6 +117,14 @@ impl RawConfig {
110117
if let Some(protocol) = get::<Protocol>("PROTOFETCH_GIT_PROTOCOL", &env_override)? {
111118
config.git.protocol = Some(protocol);
112119
}
120+
if let Some(backend) = get::<GitBackendType>("PROTOFETCH_GIT_BACKEND", &env_override)? {
121+
config.git.backend = Some(backend);
122+
}
123+
if let Some(executable_path) =
124+
get::<String>("PROTOFETCH_GIT_EXECUTABLE_PATH", &env_override)?
125+
{
126+
config.git.executable_path = Some(executable_path);
127+
}
113128
if let Some(jobs) = get::<usize>("PROTOFETCH_JOBS", &env_override)? {
114129
config.jobs = Some(jobs);
115130
}
@@ -199,7 +214,11 @@ mod tests {
199214
config,
200215
RawConfig {
201216
cache: CacheConfig { dir: None },
202-
git: GitConfig { protocol: None },
217+
git: GitConfig {
218+
protocol: None,
219+
backend: None,
220+
executable_path: None,
221+
},
203222
jobs: None,
204223
copy_jobs: None,
205224
}
@@ -209,11 +228,15 @@ mod tests {
209228
#[test]
210229
fn load_environment() {
211230
let env = HashMap::from([
212-
("PROTOFETCH_CACHE_DIR".to_owned(), "/cache".to_owned()),
213-
("PROTOFETCH_GIT_PROTOCOL".to_owned(), "ssh".to_owned()),
214-
("PROTOFETCH_JOBS".to_owned(), "16".to_owned()),
215-
("PROTOFETCH_COPY_JOBS".to_owned(), "4".to_owned()),
231+
("PROTOFETCH_CACHE_DIR", "/cache"),
232+
("PROTOFETCH_GIT_PROTOCOL", "ssh"),
233+
("PROTOFETCH_GIT_BACKEND", "cli"),
234+
("PROTOFETCH_GIT_EXECUTABLE_PATH", "/usr/bin/git"),
235+
("PROTOFETCH_JOBS", "16"),
236+
("PROTOFETCH_COPY_JOBS", "4"),
216237
]);
238+
// Note: `git.executable_path` is a config-file-only setting; the
239+
// `Environment` source's `_` separator would split a two-word key.
217240
let config = RawConfig::load(None, Some(Default::default()), Some(env)).unwrap();
218241
assert_eq!(
219242
config,
@@ -222,7 +245,9 @@ mod tests {
222245
dir: Some("/cache".into())
223246
},
224247
git: GitConfig {
225-
protocol: Some(Protocol::Ssh)
248+
protocol: Some(Protocol::Ssh),
249+
backend: Some(GitBackendType::Cli),
250+
executable_path: Some("/usr/bin/git".to_owned()),
226251
},
227252
jobs: Some(16),
228253
copy_jobs: Some(4),
@@ -241,6 +266,8 @@ mod tests {
241266

242267
[git]
243268
protocol = "ssh"
269+
backend = "cli"
270+
executable_path = "/usr/bin/git"
244271
}),
245272
Some(env),
246273
)
@@ -252,7 +279,9 @@ mod tests {
252279
dir: Some("/cache".into())
253280
},
254281
git: GitConfig {
255-
protocol: Some(Protocol::Ssh)
282+
protocol: Some(Protocol::Ssh),
283+
backend: Some(GitBackendType::Cli),
284+
executable_path: Some("/usr/bin/git".to_owned()),
256285
},
257286
jobs: None,
258287
copy_jobs: None,

src/git/backend/mod.rs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,13 @@ pub mod error;
55
pub mod libgit2;
66
pub mod types;
77

8-
use std::path::{Path, PathBuf};
8+
use std::{
9+
panic::{RefUnwindSafe, UnwindSafe},
10+
path::{Path, PathBuf},
11+
str::FromStr,
12+
};
913

14+
use anyhow::bail;
1015
use log::info;
1116
use serde::Deserialize;
1217

@@ -35,7 +40,7 @@ pub trait GitRepository {
3540
}
3641

3742
/// Factory for opening or creating git repositories.
38-
pub trait GitBackend: Send + Sync + std::panic::UnwindSafe + std::panic::RefUnwindSafe {
43+
pub trait GitBackend: Send + Sync + UnwindSafe + RefUnwindSafe {
3944
/// Initialize a new bare repository at the given path and return a handle to it.
4045
fn init_bare(&self, path: &Path) -> Result<Box<dyn GitRepository>, GitBackendError>;
4146

@@ -71,6 +76,18 @@ pub enum GitBackendType {
7176
Cli,
7277
}
7378

79+
impl FromStr for GitBackendType {
80+
type Err = anyhow::Error;
81+
82+
fn from_str(s: &str) -> Result<Self, Self::Err> {
83+
match s.to_lowercase().as_str() {
84+
"libgit2" => Ok(GitBackendType::Libgit2),
85+
"cli" => Ok(GitBackendType::Cli),
86+
_ => bail!("invalid git backend type: {s}"),
87+
}
88+
}
89+
}
90+
7491
/// Create a git backend of the specified type.
7592
pub fn create_backend(
7693
backend_type: GitBackendType,

0 commit comments

Comments
 (0)