| 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); |
| } |
| } |