Skip to content

Commit e6b7507

Browse files
calvin-wan-googlesteadmon
authored andcommitted
libgit: add higher-level libgit crate
Wrap `struct config_set` and a few of its associated functions in libgit-sys. Also introduce a higher-level "libgit" crate which provides a more Rust-friendly interface to config_set structs. Co-authored-by: Josh Steadmon <[email protected]> Signed-off-by: Calvin Wan <[email protected]> Signed-off-by: Josh Steadmon <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 3fab382 commit e6b7507

File tree

10 files changed

+203
-1
lines changed

10 files changed

+203
-1
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,4 +248,5 @@ Release/
248248
/git.VC.db
249249
*.dSYM
250250
/contrib/buildsystems/out
251+
/contrib/libgit-rs/target
251252
/contrib/libgit-rs/libgit-sys/target

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3723,7 +3723,7 @@ clean: profile-clean coverage-clean cocciclean
37233723
$(RM) $(htmldocs).tar.gz $(manpages).tar.gz
37243724
$(MAKE) -C Documentation/ clean
37253725
$(RM) Documentation/GIT-EXCLUDED-PROGRAMS
3726-
$(RM) -r contrib/libgit-rs/libgit-sys/target
3726+
$(RM) -r contrib/libgit-rs/target contrib/libgit-rs/libgit-sys/target
37273727
ifndef NO_PERL
37283728
$(RM) -r perl/build/
37293729
endif

contrib/libgit-rs/Cargo.lock

Lines changed: 77 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

contrib/libgit-rs/Cargo.toml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[package]
2+
name = "libgit"
3+
version = "0.1.0"
4+
edition = "2021"
5+
build = "build.rs"
6+
rust-version = "1.63"
7+
8+
[lib]
9+
path = "src/lib.rs"
10+
11+
[dependencies]
12+
libgit-sys = { version = "0.1.0", path = "libgit-sys" }
13+
14+
[build-dependencies]
15+
autocfg = "1.4.0"

contrib/libgit-rs/build.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
pub fn main() {
2+
let ac = autocfg::new();
3+
ac.emit_has_path("std::ffi::c_char");
4+
}

contrib/libgit-rs/libgit-sys/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::ffi::c_void;
2+
13
#[cfg(has_std__ffi__c_char)]
24
use std::ffi::{c_char, c_int};
35

@@ -19,6 +21,8 @@ pub struct libgit_config_set {
1921
}
2022

2123
extern "C" {
24+
pub fn free(ptr: *mut c_void);
25+
2226
pub fn libgit_user_agent() -> *const c_char;
2327
pub fn libgit_user_agent_sanitized() -> *const c_char;
2428

contrib/libgit-rs/src/lib.rs

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
use std::ffi::{c_void, CStr, CString};
2+
use std::path::Path;
3+
4+
#[cfg(has_std__ffi__c_char)]
5+
use std::ffi::{c_char, c_int};
6+
7+
#[cfg(not(has_std__ffi__c_char))]
8+
#[allow(non_camel_case_types)]
9+
pub type c_char = i8;
10+
11+
#[cfg(not(has_std__ffi__c_char))]
12+
#[allow(non_camel_case_types)]
13+
pub type c_int = i32;
14+
15+
use libgit_sys::*;
16+
17+
pub struct ConfigSet(*mut libgit_config_set);
18+
impl ConfigSet {
19+
pub fn new() -> Self {
20+
unsafe { ConfigSet(libgit_configset_alloc()) }
21+
}
22+
23+
pub fn add_files(&mut self, files: &[&Path]) {
24+
for file in files {
25+
let pstr = file.to_str().expect("Invalid UTF-8");
26+
let rs = CString::new(pstr).expect("Couldn't convert to CString");
27+
unsafe {
28+
libgit_configset_add_file(self.0, rs.as_ptr());
29+
}
30+
}
31+
}
32+
33+
pub fn get_int(&mut self, key: &str) -> Option<c_int> {
34+
let key = CString::new(key).expect("Couldn't convert to CString");
35+
let mut val: c_int = 0;
36+
unsafe {
37+
if libgit_configset_get_int(self.0, key.as_ptr(), &mut val as *mut c_int) != 0 {
38+
return None;
39+
}
40+
}
41+
42+
Some(val)
43+
}
44+
45+
pub fn get_string(&mut self, key: &str) -> Option<String> {
46+
let key = CString::new(key).expect("Couldn't convert key to CString");
47+
let mut val: *mut c_char = std::ptr::null_mut();
48+
unsafe {
49+
if libgit_configset_get_string(self.0, key.as_ptr(), &mut val as *mut *mut c_char) != 0
50+
{
51+
return None;
52+
}
53+
let borrowed_str = CStr::from_ptr(val);
54+
let owned_str =
55+
String::from(borrowed_str.to_str().expect("Couldn't convert val to str"));
56+
free(val as *mut c_void); // Free the xstrdup()ed pointer from the C side
57+
Some(owned_str)
58+
}
59+
}
60+
}
61+
62+
impl Default for ConfigSet {
63+
fn default() -> Self {
64+
Self::new()
65+
}
66+
}
67+
68+
impl Drop for ConfigSet {
69+
fn drop(&mut self) {
70+
unsafe {
71+
libgit_configset_free(self.0);
72+
}
73+
}
74+
}
75+
76+
#[cfg(test)]
77+
mod tests {
78+
use super::*;
79+
80+
#[test]
81+
fn load_configs_via_configset() {
82+
let mut cs = ConfigSet::new();
83+
cs.add_files(&[
84+
Path::new("testdata/config1"),
85+
Path::new("testdata/config2"),
86+
Path::new("testdata/config3"),
87+
]);
88+
// ConfigSet retrieves correct value
89+
assert_eq!(cs.get_int("trace2.eventTarget"), Some(1));
90+
// ConfigSet respects last config value set
91+
assert_eq!(cs.get_int("trace2.eventNesting"), Some(3));
92+
// ConfigSet returns None for missing key
93+
assert_eq!(cs.get_string("foo.bar"), None);
94+
}
95+
}

contrib/libgit-rs/testdata/config1

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[trace2]
2+
eventNesting = 1

contrib/libgit-rs/testdata/config2

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[trace2]
2+
eventTarget = 1

contrib/libgit-rs/testdata/config3

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[trace2]
2+
eventNesting = 3

0 commit comments

Comments
 (0)