Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add git_libgit2_opts binding for get/set search path #656

Merged
merged 7 commits into from
Jan 6, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,7 @@ pub enum BranchType {
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
pub enum ConfigLevel {
/// System-wide on Windows, for compatibility with portable git
ProgramData,
ProgramData = 1,
/// System-wide configuration file, e.g. /etc/gitconfig
System,
/// XDG-compatible configuration file, e.g. ~/.config/git/config
Expand All @@ -375,7 +375,7 @@ pub enum ConfigLevel {
/// Application specific configuration file
App,
/// Highest level available
Highest,
Highest = -1,
}

/// Merge file favor options for `MergeOptions` instruct the file-level
Expand Down
70 changes: 68 additions & 2 deletions src/opts.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,70 @@
//! Bindings to libgit2's git_libgit2_opts function.

use crate::raw;
use std::ffi::CString;

use crate::util::Binding;
use crate::{call, raw, Buf, ConfigLevel, Error, IntoCString};

/// Set the search path for a level of config data. The search path applied to
/// shared attributes and ignore files, too.
///
/// `level` must be one of [`ConfigLevel::System`], [`ConfigLevel::Global`],
/// [`ConfigLevel::XDG`], [`ConfigLevel::ProgramData`].
///
/// `path` lists directories delimited by `GIT_PATH_LIST_SEPARATOR`.
/// Use magic path `$PATH` to include the old value of the path
/// (if you want to prepend or append, for instance).
///
/// This function is unsafe as it mutates the global state but cannot guarantee
/// thread-safety. It needs to be externally synchronized with calls to access
/// the global state.
pub unsafe fn set_search_path<P>(level: ConfigLevel, path: P) -> Result<(), Error>
where
P: IntoCString,
{
call::c_try(raw::git_libgit2_opts(
raw::GIT_OPT_SET_SEARCH_PATH as libc::c_int,
level as libc::c_int,
path.into_c_string()?.as_ptr(),
))?;
Ok(())
}

/// Reset the search path for a given level of config data to the default
/// (generally based on environment variables).
///
/// `level` must be one of [`ConfigLevel::System`], [`ConfigLevel::Global`],
/// [`ConfigLevel::XDG`], [`ConfigLevel::ProgramData`].
///
/// This function is unsafe as it mutates the global state but cannot guarantee
/// thread-safety. It needs to be externally synchronized with calls to access
/// the global state.
pub unsafe fn reset_search_path(level: ConfigLevel) -> Result<(), Error> {
call::c_try(raw::git_libgit2_opts(
raw::GIT_OPT_SET_SEARCH_PATH as libc::c_int,
level as libc::c_int,
core::ptr::null::<u8>(),
))?;
Ok(())
}

/// Get the search path for a given level of config data.
///
/// `level` must be one of [`ConfigLevel::System`], [`ConfigLevel::Global`],
/// [`ConfigLevel::XDG`], [`ConfigLevel::ProgramData`].
///
/// This function is unsafe as it mutates the global state but cannot guarantee
/// thread-safety. It needs to be externally synchronized with calls to access
/// the global state.
pub unsafe fn get_search_path(level: ConfigLevel) -> Result<CString, Error> {
let buf = Buf::new();
call::c_try(raw::git_libgit2_opts(
raw::GIT_OPT_GET_SEARCH_PATH as libc::c_int,
level as libc::c_int,
buf.raw(),
))?;
buf.into_c_string()
}

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

#[cfg(test)]
mod test {
use super::*;

#[test]
fn smoke() {
super::strict_hash_verification(false);
strict_hash_verification(false);
}
}
47 changes: 47 additions & 0 deletions tests/global_state.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
//! Test for some global state set up by libgit2's `git_libgit2_init` function
//! that need to be synchronized within a single process.

use git2::opts;
use git2::{ConfigLevel, IntoCString};

// Test for mutating configuration file search path which is set during
// initialization in libgit2's `git_sysdir_global_init` function.
#[test]
fn search_path() -> Result<(), Box<dyn std::error::Error>> {
use std::env::join_paths;

let path = "fake_path";
let original = unsafe { opts::get_search_path(ConfigLevel::Global) };
assert_ne!(original, Ok(path.into_c_string()?));

// Set
unsafe {
opts::set_search_path(ConfigLevel::Global, &path)?;
}
assert_eq!(
unsafe { opts::get_search_path(ConfigLevel::Global) },
Ok(path.into_c_string()?)
);

// Append
let paths = join_paths(["$PATH", path].iter())?;
let expected_paths = join_paths([path, path].iter())?.into_c_string()?;
unsafe {
opts::set_search_path(ConfigLevel::Global, paths)?;
}
assert_eq!(
unsafe { opts::get_search_path(ConfigLevel::Global) },
Ok(expected_paths)
);

// Reset
unsafe {
opts::reset_search_path(ConfigLevel::Global)?;
}
assert_eq!(
unsafe { opts::get_search_path(ConfigLevel::Global) },
original
);

Ok(())
}