Skip to content

Commit 1a9d2e8

Browse files
committed
libgit-rs: add get_bool(), get_ulong(), and get_pathname() methods to ConfigSet
Expand the ConfigSet API with support for additional configuration value types: - get_bool(): Parse boolean configuration values using Git's git_configset_get_bool() C function. Handles all Git boolean formats (true/false, yes/no, on/off, 1/0) and edge cases like "00" and "100". - get_ulong(): Parse unsigned long integers for large numeric values using git_configset_get_ulong(). - get_pathname(): Parse file paths using git_configset_get_pathname(), returning PathBuf for type safety. All functions follow the established pattern of existing get_* methods, using Git's C functions for consistent parsing behavior with the main Git codebase. Add wrapper functions in public_symbol_export.[ch] to expose the required C functions to the Rust FFI layer. Includes comprehensive tests covering normal cases, edge cases, and error handling for all new functionality. Signed-off-by: ionnss <[email protected]>
1 parent bb69721 commit 1a9d2e8

File tree

6 files changed

+142
-4
lines changed

6 files changed

+142
-4
lines changed

contrib/libgit-rs/src/config.rs

Lines changed: 81 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use std::ffi::{c_void, CStr, CString};
2-
use std::path::Path;
2+
use std::path::{Path, PathBuf};
33

44
#[cfg(has_std__ffi__c_char)]
5-
use std::ffi::{c_char, c_int};
5+
use std::ffi::{c_char, c_int, c_ulong};
66

77
#[cfg(not(has_std__ffi__c_char))]
88
#[allow(non_camel_case_types)]
@@ -12,6 +12,10 @@ type c_char = i8;
1212
#[allow(non_camel_case_types)]
1313
type c_int = i32;
1414

15+
#[cfg(not(has_std__ffi__c_char))]
16+
#[allow(non_camel_case_types)]
17+
type c_ulong = u64;
18+
1519
use libgit_sys::*;
1620

1721
/// A ConfigSet is an in-memory cache for config-like files such as `.gitmodules` or `.gitconfig`.
@@ -68,6 +72,55 @@ impl ConfigSet {
6872
Some(owned_str)
6973
}
7074
}
75+
76+
/// Load the value for the given key and attempt to parse it as a boolean. Dies with a fatal error
77+
/// if the value cannot be parsed. Returns None if the key is not present.
78+
pub fn get_bool(&mut self, key: &str) -> Option<bool> {
79+
let key = CString::new(key).expect("config key should be valid CString");
80+
let mut val: c_int = 0;
81+
unsafe {
82+
if libgit_configset_get_bool(self.0, key.as_ptr(), &mut val as *mut c_int) != 0 {
83+
return None;
84+
}
85+
}
86+
87+
Some(val != 0)
88+
}
89+
90+
/// Load the value for the given key and attempt to parse it as an unsigned long. Dies with a fatal error
91+
/// if the value cannot be parsed. Returns None if the key is not present.
92+
pub fn get_ulong(&mut self, key: &str) -> Option<u64> {
93+
let key = CString::new(key).expect("config key should be valid CString");
94+
let mut val: c_ulong = 0;
95+
unsafe {
96+
if libgit_configset_get_ulong(self.0, key.as_ptr(), &mut val as *mut c_ulong) != 0 {
97+
return None;
98+
}
99+
}
100+
Some(val as u64)
101+
}
102+
103+
/// Load the value for the given key and attempt to parse it as a file path. Dies with a fatal error
104+
/// if the value cannot be converted to a PathBuf. Returns None if the key is not present.
105+
pub fn get_pathname(&mut self, key: &str) -> Option<PathBuf> {
106+
let key = CString::new(key).expect("config key should be valid CString");
107+
let mut val: *mut c_char = std::ptr::null_mut();
108+
unsafe {
109+
if libgit_configset_get_pathname(self.0, key.as_ptr(), &mut val as *mut *mut c_char)
110+
!= 0
111+
{
112+
return None;
113+
}
114+
let borrowed_str = CStr::from_ptr(val);
115+
let owned_str = String::from(
116+
borrowed_str
117+
.to_str()
118+
.expect("config path should be valid UTF-8"),
119+
);
120+
free(val as *mut c_void); // Free the xstrdup()ed pointer from the C side
121+
Some(PathBuf::from(owned_str))
122+
}
123+
}
71124
}
72125

73126
impl Default for ConfigSet {
@@ -95,12 +148,38 @@ mod tests {
95148
Path::new("testdata/config1"),
96149
Path::new("testdata/config2"),
97150
Path::new("testdata/config3"),
151+
Path::new("testdata/config4"),
98152
]);
99153
// ConfigSet retrieves correct value
100154
assert_eq!(cs.get_int("trace2.eventTarget"), Some(1));
101155
// ConfigSet respects last config value set
102156
assert_eq!(cs.get_int("trace2.eventNesting"), Some(3));
103157
// ConfigSet returns None for missing key
104158
assert_eq!(cs.get_string("foo.bar"), None);
159+
// Test boolean parsing - comprehensive tests
160+
assert_eq!(cs.get_bool("test.boolTrue"), Some(true));
161+
assert_eq!(cs.get_bool("test.boolFalse"), Some(false));
162+
assert_eq!(cs.get_bool("test.boolYes"), Some(true));
163+
assert_eq!(cs.get_bool("test.boolNo"), Some(false));
164+
assert_eq!(cs.get_bool("test.boolOne"), Some(true));
165+
assert_eq!(cs.get_bool("test.boolZero"), Some(false));
166+
assert_eq!(cs.get_bool("test.boolZeroZero"), Some(false)); // "00" → false
167+
assert_eq!(cs.get_bool("test.boolHundred"), Some(true)); // "100" → true
168+
// Test missing boolean key
169+
assert_eq!(cs.get_bool("missing.boolean"), None);
170+
// Test ulong parsing
171+
assert_eq!(cs.get_ulong("test.ulongSmall"), Some(42));
172+
assert_eq!(cs.get_ulong("test.ulongBig"), Some(4294967296)); // > 32-bit int
173+
assert_eq!(cs.get_ulong("missing.ulong"), None);
174+
// Test pathname parsing
175+
assert_eq!(
176+
cs.get_pathname("test.pathRelative"),
177+
Some(PathBuf::from("./some/path"))
178+
);
179+
assert_eq!(
180+
cs.get_pathname("test.pathAbsolute"),
181+
Some(PathBuf::from("/usr/bin/git"))
182+
);
183+
assert_eq!(cs.get_pathname("missing.path"), None);
105184
}
106185
}

contrib/libgit-rs/testdata/config3

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
[trace2]
2-
eventNesting = 3
2+
eventNesting = 3

contrib/libgit-rs/testdata/config4

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
[test]
2+
boolTrue = true
3+
boolFalse = false
4+
boolYes = yes
5+
boolNo = no
6+
boolOne = 1
7+
boolZero = 0
8+
boolZeroZero = 00
9+
boolHundred = 100
10+
ulongSmall = 42
11+
ulongBig = 4294967296
12+
pathRelative = ./some/path
13+
pathAbsolute = /usr/bin/git

contrib/libgit-sys/public_symbol_export.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,24 @@ int libgit_configset_get_string(struct libgit_config_set *cs, const char *key,
4646
return git_configset_get_string(&cs->cs, key, dest);
4747
}
4848

49+
int libgit_configset_get_bool(struct libgit_config_set *cs, const char *key,
50+
int *dest)
51+
{
52+
return git_configset_get_bool(&cs->cs, key, dest);
53+
}
54+
55+
int libgit_configset_get_ulong(struct libgit_config_set *cs, const char *key,
56+
unsigned long *dest)
57+
{
58+
return git_configset_get_ulong(&cs->cs, key, dest);
59+
}
60+
61+
int libgit_configset_get_pathname(struct libgit_config_set *cs, const char *key,
62+
char **dest)
63+
{
64+
return git_configset_get_pathname(&cs->cs, key, dest);
65+
}
66+
4967
const char *libgit_user_agent(void)
5068
{
5169
return git_user_agent();

contrib/libgit-sys/public_symbol_export.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ int libgit_configset_get_int(struct libgit_config_set *cs, const char *key, int
1111

1212
int libgit_configset_get_string(struct libgit_config_set *cs, const char *key, char **dest);
1313

14+
int libgit_configset_get_bool(struct libgit_config_set *cs, const char *key, int *dest);
15+
16+
int libgit_configset_get_ulong(struct libgit_config_set *cs, const char *key, unsigned long *dest);
17+
18+
int libgit_configset_get_pathname(struct libgit_config_set *cs, const char *key, char **dest);
19+
1420
const char *libgit_user_agent(void);
1521

1622
const char *libgit_user_agent_sanitized(void);

contrib/libgit-sys/src/lib.rs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use std::ffi::c_void;
22

33
#[cfg(has_std__ffi__c_char)]
4-
use std::ffi::{c_char, c_int};
4+
use std::ffi::{c_char, c_int, c_ulong};
55

66
#[cfg(not(has_std__ffi__c_char))]
77
#[allow(non_camel_case_types)]
@@ -11,6 +11,10 @@ pub type c_char = i8;
1111
#[allow(non_camel_case_types)]
1212
pub type c_int = i32;
1313

14+
#[cfg(not(has_std__ffi__c_char))]
15+
#[allow(non_camel_case_types)]
16+
pub type c_ulong = u64;
17+
1418
extern crate libz_sys;
1519

1620
#[allow(non_camel_case_types)]
@@ -43,6 +47,24 @@ extern "C" {
4347
dest: *mut *mut c_char,
4448
) -> c_int;
4549

50+
pub fn libgit_configset_get_bool(
51+
cs: *mut libgit_config_set,
52+
key: *const c_char,
53+
dest: *mut c_int,
54+
) -> c_int;
55+
56+
pub fn libgit_configset_get_ulong(
57+
cs: *mut libgit_config_set,
58+
key: *const c_char,
59+
dest: *mut c_ulong,
60+
) -> c_int;
61+
62+
pub fn libgit_configset_get_pathname(
63+
cs: *mut libgit_config_set,
64+
key: *const c_char,
65+
dest: *mut *mut c_char,
66+
) -> c_int;
67+
4668
}
4769

4870
#[cfg(test)]

0 commit comments

Comments
 (0)