Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions libgit2-sys/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,13 @@ pub struct git_config_backend {
pub free: Option<extern "C" fn(*mut git_config_backend)>,
}

#[repr(C)]
pub struct git_config_backend_memory_options {
pub version: c_uint,
pub backend_type: *const c_char,
pub origin_path: *const c_char,
}

pub const GIT_CONFIG_BACKEND_VERSION: c_uint = 1;

pub enum git_index {}
Expand Down Expand Up @@ -3459,6 +3466,12 @@ extern "C" {
name: *const c_char,
) -> c_int;
pub fn git_config_init_backend(backend: *mut git_config_backend, version: c_uint) -> c_int;
pub fn git_config_backend_from_string(
backend: *mut *mut git_config_backend,
cfg: *const c_char,
len: size_t,
opts: *mut git_config_backend_memory_options,
) -> c_int;
pub fn git_config_iterator_free(iter: *mut git_config_iterator);
pub fn git_config_iterator_glob_new(
out: *mut *mut git_config_iterator,
Expand Down
74 changes: 74 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,30 @@ impl Config {
}
}

/// Create a new config instance based on the provided string contents
pub fn from_str(contents: &str) -> Result<Config, Error> {
let config = Self::new()?;

let mut raw_backend = ptr::null_mut();
unsafe {
try_call!(raw::git_config_backend_from_string(
&mut raw_backend,
contents.into_c_string()?,
contents.len(),
ptr::null_mut()
));
try_call!(raw::git_config_add_backend(
config.raw,
raw_backend,
// Match the level used by git_config_open_ondisk()
ConfigLevel::Local,
ptr::null(),
0 as libc::c_int
));
}
Ok(config)
}

/// Open the global, XDG and system configuration files
///
/// Utility wrapper that finds the global, XDG and system configuration
Expand Down Expand Up @@ -740,6 +764,56 @@ mod tests {
assert_eq!(count(cfg.multivar("foo.bar", None).unwrap()), 0);
}

#[test]
fn cfg_from_str() {
// Empty string, no entries
let empty = Config::from_str("").unwrap();
let mut entries = empty.entries(None).unwrap();
// Should start with None because it is empty
assert!(entries.next().is_none());

let dump = |entries: super::ConfigEntries<'_>| -> Vec<String> {
let mut string_entries = vec![];
let _ = entries.for_each(|ent| {
string_entries.push(format!(
"{} = {}",
ent.name().unwrap(),
ent.value().unwrap()
));
});
string_entries.sort();
string_entries
};

// One entry
let signed = Config::from_str("[commit]\ngpgsign=true\n").unwrap();
assert_eq!(
dump(signed.entries(Some("commit.gpgsign")).unwrap()),
["commit.gpgsign = true"]
);

// A few entries
let big_config = Config::from_str(
"[core]\n
\tbare=false\n
\tignorecase=true\n
[remote \"origin\"]\n
\turl = https://github.com/rust-lang/rust.git\n
[branch \"main\"]\n
remote = origin",
)
.unwrap();
assert_eq!(
dump(big_config.entries(None).unwrap()),
[
"branch.main.remote = origin",
"core.bare = false",
"core.ignorecase = true",
"remote.origin.url = https://github.com/rust-lang/rust.git",
]
);
}

#[test]
fn parse() {
assert_eq!(Config::parse_bool("").unwrap(), false);
Expand Down