Skip to content

Commit 252f41b

Browse files
authored
Add git_libgit2_opts binding for get/set search path (#656)
* fix: set ConfigLevel to corret integer * feat(opts): get/set/reset search path * test(opts): get/set/reset search path * fix: thread-safe get/set search path * fix: get_search_path will returns CString * refactor: remove Mutex and change to unsafe fn * refactor: search path test as a integration test
1 parent 5e379e6 commit 252f41b

File tree

3 files changed

+117
-4
lines changed

3 files changed

+117
-4
lines changed

src/lib.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,7 @@ pub enum BranchType {
363363
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
364364
pub enum ConfigLevel {
365365
/// System-wide on Windows, for compatibility with portable git
366-
ProgramData,
366+
ProgramData = 1,
367367
/// System-wide configuration file, e.g. /etc/gitconfig
368368
System,
369369
/// XDG-compatible configuration file, e.g. ~/.config/git/config
@@ -375,7 +375,7 @@ pub enum ConfigLevel {
375375
/// Application specific configuration file
376376
App,
377377
/// Highest level available
378-
Highest,
378+
Highest = -1,
379379
}
380380

381381
/// Merge file favor options for `MergeOptions` instruct the file-level

src/opts.rs

+68-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,70 @@
11
//! Bindings to libgit2's git_libgit2_opts function.
22
3-
use crate::raw;
3+
use std::ffi::CString;
4+
5+
use crate::util::Binding;
6+
use crate::{call, raw, Buf, ConfigLevel, Error, IntoCString};
7+
8+
/// Set the search path for a level of config data. The search path applied to
9+
/// shared attributes and ignore files, too.
10+
///
11+
/// `level` must be one of [`ConfigLevel::System`], [`ConfigLevel::Global`],
12+
/// [`ConfigLevel::XDG`], [`ConfigLevel::ProgramData`].
13+
///
14+
/// `path` lists directories delimited by `GIT_PATH_LIST_SEPARATOR`.
15+
/// Use magic path `$PATH` to include the old value of the path
16+
/// (if you want to prepend or append, for instance).
17+
///
18+
/// This function is unsafe as it mutates the global state but cannot guarantee
19+
/// thread-safety. It needs to be externally synchronized with calls to access
20+
/// the global state.
21+
pub unsafe fn set_search_path<P>(level: ConfigLevel, path: P) -> Result<(), Error>
22+
where
23+
P: IntoCString,
24+
{
25+
call::c_try(raw::git_libgit2_opts(
26+
raw::GIT_OPT_SET_SEARCH_PATH as libc::c_int,
27+
level as libc::c_int,
28+
path.into_c_string()?.as_ptr(),
29+
))?;
30+
Ok(())
31+
}
32+
33+
/// Reset the search path for a given level of config data to the default
34+
/// (generally based on environment variables).
35+
///
36+
/// `level` must be one of [`ConfigLevel::System`], [`ConfigLevel::Global`],
37+
/// [`ConfigLevel::XDG`], [`ConfigLevel::ProgramData`].
38+
///
39+
/// This function is unsafe as it mutates the global state but cannot guarantee
40+
/// thread-safety. It needs to be externally synchronized with calls to access
41+
/// the global state.
42+
pub unsafe fn reset_search_path(level: ConfigLevel) -> Result<(), Error> {
43+
call::c_try(raw::git_libgit2_opts(
44+
raw::GIT_OPT_SET_SEARCH_PATH as libc::c_int,
45+
level as libc::c_int,
46+
core::ptr::null::<u8>(),
47+
))?;
48+
Ok(())
49+
}
50+
51+
/// Get the search path for a given level of config data.
52+
///
53+
/// `level` must be one of [`ConfigLevel::System`], [`ConfigLevel::Global`],
54+
/// [`ConfigLevel::XDG`], [`ConfigLevel::ProgramData`].
55+
///
56+
/// This function is unsafe as it mutates the global state but cannot guarantee
57+
/// thread-safety. It needs to be externally synchronized with calls to access
58+
/// the global state.
59+
pub unsafe fn get_search_path(level: ConfigLevel) -> Result<CString, Error> {
60+
let buf = Buf::new();
61+
call::c_try(raw::git_libgit2_opts(
62+
raw::GIT_OPT_GET_SEARCH_PATH as libc::c_int,
63+
level as libc::c_int,
64+
buf.raw(),
65+
))?;
66+
buf.into_c_string()
67+
}
468

569
/// Controls whether or not libgit2 will verify when writing an object that all
670
/// objects it references are valid. Enabled by default, but disabling this can
@@ -37,8 +101,10 @@ pub fn strict_hash_verification(enabled: bool) {
37101

38102
#[cfg(test)]
39103
mod test {
104+
use super::*;
105+
40106
#[test]
41107
fn smoke() {
42-
super::strict_hash_verification(false);
108+
strict_hash_verification(false);
43109
}
44110
}

tests/global_state.rs

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
//! Test for some global state set up by libgit2's `git_libgit2_init` function
2+
//! that need to be synchronized within a single process.
3+
4+
use git2::opts;
5+
use git2::{ConfigLevel, IntoCString};
6+
7+
// Test for mutating configuration file search path which is set during
8+
// initialization in libgit2's `git_sysdir_global_init` function.
9+
#[test]
10+
fn search_path() -> Result<(), Box<dyn std::error::Error>> {
11+
use std::env::join_paths;
12+
13+
let path = "fake_path";
14+
let original = unsafe { opts::get_search_path(ConfigLevel::Global) };
15+
assert_ne!(original, Ok(path.into_c_string()?));
16+
17+
// Set
18+
unsafe {
19+
opts::set_search_path(ConfigLevel::Global, &path)?;
20+
}
21+
assert_eq!(
22+
unsafe { opts::get_search_path(ConfigLevel::Global) },
23+
Ok(path.into_c_string()?)
24+
);
25+
26+
// Append
27+
let paths = join_paths(["$PATH", path].iter())?;
28+
let expected_paths = join_paths([path, path].iter())?.into_c_string()?;
29+
unsafe {
30+
opts::set_search_path(ConfigLevel::Global, paths)?;
31+
}
32+
assert_eq!(
33+
unsafe { opts::get_search_path(ConfigLevel::Global) },
34+
Ok(expected_paths)
35+
);
36+
37+
// Reset
38+
unsafe {
39+
opts::reset_search_path(ConfigLevel::Global)?;
40+
}
41+
assert_eq!(
42+
unsafe { opts::get_search_path(ConfigLevel::Global) },
43+
original
44+
);
45+
46+
Ok(())
47+
}

0 commit comments

Comments
 (0)