Git fork
at reftables-rust 106 lines 3.5 kB view raw
1use std::ffi::{c_void, CStr, CString}; 2use std::path::Path; 3 4#[cfg(has_std__ffi__c_char)] 5use std::ffi::{c_char, c_int}; 6 7#[cfg(not(has_std__ffi__c_char))] 8#[allow(non_camel_case_types)] 9type c_char = i8; 10 11#[cfg(not(has_std__ffi__c_char))] 12#[allow(non_camel_case_types)] 13type c_int = i32; 14 15use libgit_sys::*; 16 17/// A ConfigSet is an in-memory cache for config-like files such as `.gitmodules` or `.gitconfig`. 18/// It does not support all config directives; notably, it will not process `include` or 19/// `includeIf` directives (but it will store them so that callers can choose whether and how to 20/// handle them). 21pub struct ConfigSet(*mut libgit_config_set); 22impl ConfigSet { 23 /// Allocate a new ConfigSet 24 pub fn new() -> Self { 25 unsafe { ConfigSet(libgit_configset_alloc()) } 26 } 27 28 /// Load the given files into the ConfigSet; conflicting directives in later files will 29 /// override those given in earlier files. 30 pub fn add_files(&mut self, files: &[&Path]) { 31 for file in files { 32 let pstr = file.to_str().expect("Invalid UTF-8"); 33 let rs = CString::new(pstr).expect("Couldn't convert to CString"); 34 unsafe { 35 libgit_configset_add_file(self.0, rs.as_ptr()); 36 } 37 } 38 } 39 40 /// Load the value for the given key and attempt to parse it as an i32. Dies with a fatal error 41 /// if the value cannot be parsed. Returns None if the key is not present. 42 pub fn get_int(&mut self, key: &str) -> Option<i32> { 43 let key = CString::new(key).expect("Couldn't convert to CString"); 44 let mut val: c_int = 0; 45 unsafe { 46 if libgit_configset_get_int(self.0, key.as_ptr(), &mut val as *mut c_int) != 0 { 47 return None; 48 } 49 } 50 51 Some(val.into()) 52 } 53 54 /// Clones the value for the given key. Dies with a fatal error if the value cannot be 55 /// converted to a String. Returns None if the key is not present. 56 pub fn get_string(&mut self, key: &str) -> Option<String> { 57 let key = CString::new(key).expect("Couldn't convert key to CString"); 58 let mut val: *mut c_char = std::ptr::null_mut(); 59 unsafe { 60 if libgit_configset_get_string(self.0, key.as_ptr(), &mut val as *mut *mut c_char) != 0 61 { 62 return None; 63 } 64 let borrowed_str = CStr::from_ptr(val); 65 let owned_str = 66 String::from(borrowed_str.to_str().expect("Couldn't convert val to str")); 67 free(val as *mut c_void); // Free the xstrdup()ed pointer from the C side 68 Some(owned_str) 69 } 70 } 71} 72 73impl Default for ConfigSet { 74 fn default() -> Self { 75 Self::new() 76 } 77} 78 79impl Drop for ConfigSet { 80 fn drop(&mut self) { 81 unsafe { 82 libgit_configset_free(self.0); 83 } 84 } 85} 86 87#[cfg(test)] 88mod tests { 89 use super::*; 90 91 #[test] 92 fn load_configs_via_configset() { 93 let mut cs = ConfigSet::new(); 94 cs.add_files(&[ 95 Path::new("testdata/config1"), 96 Path::new("testdata/config2"), 97 Path::new("testdata/config3"), 98 ]); 99 // ConfigSet retrieves correct value 100 assert_eq!(cs.get_int("trace2.eventTarget"), Some(1)); 101 // ConfigSet respects last config value set 102 assert_eq!(cs.get_int("trace2.eventNesting"), Some(3)); 103 // ConfigSet returns None for missing key 104 assert_eq!(cs.get_string("foo.bar"), None); 105 } 106}