blob: 6bf04845c8f01babd2435c3213001168ea241023 [file] [log] [blame]
use std::ffi::{c_void, CStr, CString};
use std::path::Path;
#[cfg(has_std__ffi__c_char)]
use std::ffi::{c_char, c_int};
#[cfg(not(has_std__ffi__c_char))]
#[allow(non_camel_case_types)]
type c_char = i8;
#[cfg(not(has_std__ffi__c_char))]
#[allow(non_camel_case_types)]
type c_int = i32;
use libgit_sys::*;
/// A ConfigSet is an in-memory cache for config-like files such as `.gitmodules` or `.gitconfig`.
/// It does not support all config directives; notably, it will not process `include` or
/// `includeIf` directives (but it will store them so that callers can choose whether and how to
/// handle them).
pub struct ConfigSet(*mut libgit_config_set);
impl ConfigSet {
/// Allocate a new ConfigSet
pub fn new() -> Self {
unsafe { ConfigSet(libgit_configset_alloc()) }
}
/// Load the given files into the ConfigSet; conflicting directives in later files will
/// override those given in earlier files.
pub fn add_files(&mut self, files: &[&Path]) {
for file in files {
let pstr = file.to_str().expect("Invalid UTF-8");
let rs = CString::new(pstr).expect("Couldn't convert to CString");
unsafe {
libgit_configset_add_file(self.0, rs.as_ptr());
}
}
}
/// Load the value for the given key and attempt to parse it as an i32. Dies with a fatal error
/// if the value cannot be parsed. Returns None if the key is not present.
pub fn get_int(&mut self, key: &str) -> Option<i32> {
let key = CString::new(key).expect("Couldn't convert to CString");
let mut val: c_int = 0;
unsafe {
if libgit_configset_get_int(self.0, key.as_ptr(), &mut val as *mut c_int) != 0 {
return None;
}
}
Some(val.into())
}
/// Clones the value for the given key. Dies with a fatal error if the value cannot be
/// converted to a String. Returns None if the key is not present.
pub fn get_string(&mut self, key: &str) -> Option<String> {
let key = CString::new(key).expect("Couldn't convert key to CString");
let mut val: *mut c_char = std::ptr::null_mut();
unsafe {
if libgit_configset_get_string(self.0, key.as_ptr(), &mut val as *mut *mut c_char) != 0
{
return None;
}
let borrowed_str = CStr::from_ptr(val);
let owned_str =
String::from(borrowed_str.to_str().expect("Couldn't convert val to str"));
free(val as *mut c_void); // Free the xstrdup()ed pointer from the C side
Some(owned_str)
}
}
}
impl Default for ConfigSet {
fn default() -> Self {
Self::new()
}
}
impl Drop for ConfigSet {
fn drop(&mut self) {
unsafe {
libgit_configset_free(self.0);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn load_configs_via_configset() {
let mut cs = ConfigSet::new();
cs.add_files(&[
Path::new("testdata/config1"),
Path::new("testdata/config2"),
Path::new("testdata/config3"),
]);
// ConfigSet retrieves correct value
assert_eq!(cs.get_int("trace2.eventTarget"), Some(1));
// ConfigSet respects last config value set
assert_eq!(cs.get_int("trace2.eventNesting"), Some(3));
// ConfigSet returns None for missing key
assert_eq!(cs.get_string("foo.bar"), None);
}
}