Git fork
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}