3 use std
::path
::{Path, PathBuf}
;
8 use {raw, Error, ConfigLevel, Buf, IntoCString}
;
9 use util
::{self, Binding}
;
11 /// A structure representing a git configuration key/value store
13 raw
: *mut raw
::git_config
,
16 /// A struct representing a certain entry owned by a `Config` instance.
18 /// An entry has a name, a value, and a level it applies to.
19 pub struct ConfigEntry
<'cfg
> {
20 raw
: *mut raw
::git_config_entry
,
21 _marker
: marker
::PhantomData
<&'cfg Config
>,
25 /// An iterator over the `ConfigEntry` values of a `Config` structure.
26 pub struct ConfigEntries
<'cfg
> {
27 raw
: *mut raw
::git_config_iterator
,
28 _marker
: marker
::PhantomData
<&'cfg Config
>,
32 /// Allocate a new configuration object
34 /// This object is empty, so you have to add a file to it before you can do
36 pub fn new() -> Result
<Config
, Error
> {
38 let mut raw
= ptr
::null_mut();
40 try_call
!(raw
::git_config_new(&mut raw
));
41 Ok(Binding
::from_raw(raw
))
45 /// Create a new config instance containing a single on-disk file
46 pub fn open(path
: &Path
) -> Result
<Config
, Error
> {
48 let mut raw
= ptr
::null_mut();
49 let path
= try
!(path
.into_c_string());
51 try_call
!(raw
::git_config_open_ondisk(&mut raw
, path
));
52 Ok(Binding
::from_raw(raw
))
56 /// Open the global, XDG and system configuration files
58 /// Utility wrapper that finds the global, XDG and system configuration
59 /// files and opens them into a single prioritized config object that can
60 /// be used when accessing default config data outside a repository.
61 pub fn open_default() -> Result
<Config
, Error
> {
63 let mut raw
= ptr
::null_mut();
65 try_call
!(raw
::git_config_open_default(&mut raw
));
66 Ok(Binding
::from_raw(raw
))
70 /// Locate the path to the global configuration file
72 /// The user or global configuration file is usually located in
73 /// `$HOME/.gitconfig`.
75 /// This method will try to guess the full path to that file, if the file
76 /// exists. The returned path may be used on any method call to load
77 /// the global configuration file.
79 /// This method will not guess the path to the xdg compatible config file
80 /// (`.config/git/config`).
81 pub fn find_global() -> Result
<PathBuf
, Error
> {
84 unsafe { try_call!(raw::git_config_find_global(buf.raw())); }
85 Ok(util
::bytes2path(&buf
).to_path_buf())
88 /// Locate the path to the system configuration file
90 /// If /etc/gitconfig doesn't exist, it will look for %PROGRAMFILES%
91 pub fn find_system() -> Result
<PathBuf
, Error
> {
94 unsafe { try_call!(raw::git_config_find_system(buf.raw())); }
95 Ok(util
::bytes2path(&buf
).to_path_buf())
98 /// Locate the path to the global xdg compatible configuration file
100 /// The xdg compatible configuration file is usually located in
101 /// `$HOME/.config/git/config`.
102 pub fn find_xdg() -> Result
<PathBuf
, Error
> {
104 let buf
= Buf
::new();
105 unsafe { try_call!(raw::git_config_find_xdg(buf.raw())); }
106 Ok(util
::bytes2path(&buf
).to_path_buf())
109 /// Add an on-disk config file instance to an existing config
111 /// The on-disk file pointed at by path will be opened and parsed; it's
112 /// expected to be a native Git config file following the default Git config
113 /// syntax (see man git-config).
115 /// Further queries on this config object will access each of the config
116 /// file instances in order (instances with a higher priority level will be
118 pub fn add_file(&mut self, path
: &Path
, level
: ConfigLevel
,
119 force
: bool
) -> Result
<(), Error
> {
120 let path
= try
!(path
.into_c_string());
122 try_call
!(raw
::git_config_add_file_ondisk(self.raw
, path
, level
,
128 /// Delete a config variable from the config file with the highest level
129 /// (usually the local one).
130 pub fn remove(&mut self, name
: &str) -> Result
<(), Error
> {
131 let name
= try
!(CString
::new(name
));
133 try_call
!(raw
::git_config_delete_entry(self.raw
, name
));
138 /// Get the value of a boolean config variable.
140 /// All config files will be looked into, in the order of their defined
141 /// level. A higher level means a higher priority. The first occurrence of
142 /// the variable will be returned here.
143 pub fn get_bool(&self, name
: &str) -> Result
<bool
, Error
> {
144 let mut out
= 0 as libc
::c_int
;
145 let name
= try
!(CString
::new(name
));
147 try_call
!(raw
::git_config_get_bool(&mut out
, &*self.raw
, name
));
153 /// Get the value of an integer config variable.
155 /// All config files will be looked into, in the order of their defined
156 /// level. A higher level means a higher priority. The first occurrence of
157 /// the variable will be returned here.
158 pub fn get_i32(&self, name
: &str) -> Result
<i32, Error
> {
160 let name
= try
!(CString
::new(name
));
162 try_call
!(raw
::git_config_get_int32(&mut out
, &*self.raw
, name
));
168 /// Get the value of an integer config variable.
170 /// All config files will be looked into, in the order of their defined
171 /// level. A higher level means a higher priority. The first occurrence of
172 /// the variable will be returned here.
173 pub fn get_i64(&self, name
: &str) -> Result
<i64, Error
> {
175 let name
= try
!(CString
::new(name
));
177 try_call
!(raw
::git_config_get_int64(&mut out
, &*self.raw
, name
));
182 /// Get the value of a string config variable.
184 /// This is the same as `get_bytes` except that it may return `Err` if
185 /// the bytes are not valid utf-8.
186 pub fn get_str(&self, name
: &str) -> Result
<&str, Error
> {
187 str::from_utf8(try
!(self.get_bytes(name
))).map_err(|_
| {
188 Error
::from_str("configuration value is not valid utf8")
192 /// Get the value of a string config variable as a byte slice.
194 /// This method will return an error if this `Config` is not a snapshot.
195 pub fn get_bytes(&self, name
: &str) -> Result
<&[u8], Error
> {
196 let mut ret
= ptr
::null();
197 let name
= try
!(CString
::new(name
));
199 try_call
!(raw
::git_config_get_string(&mut ret
, &*self.raw
, name
));
200 Ok(::opt_bytes(self, ret
).unwrap())
204 /// Get the value of a string config variable as an owned string.
206 /// An error will be returned if the config value is not valid utf-8.
207 pub fn get_string(&self, name
: &str) -> Result
<String
, Error
> {
208 let ret
= Buf
::new();
209 let name
= try
!(CString
::new(name
));
211 try_call
!(raw
::git_config_get_string_buf(ret
.raw(), self.raw
, name
));
213 str::from_utf8(&ret
).map(|s
| s
.to_string()).map_err(|_
| {
214 Error
::from_str("configuration value is not valid utf8")
218 /// Get the value of a path config variable as an owned .
219 pub fn get_path(&self, name
: &str) -> Result
<PathBuf
, Error
> {
220 let ret
= Buf
::new();
221 let name
= try
!(CString
::new(name
));
223 try_call
!(raw
::git_config_get_path(ret
.raw(), self.raw
, name
));
225 Ok(::util
::bytes2path(&ret
).to_path_buf())
228 /// Get the ConfigEntry for a config variable.
229 pub fn get_entry(&self, name
: &str) -> Result
<ConfigEntry
, Error
> {
230 let mut ret
= ptr
::null_mut();
231 let name
= try
!(CString
::new(name
));
233 try_call
!(raw
::git_config_get_entry(&mut ret
, self.raw
, name
));
234 Ok(Binding
::from_raw(ret
))
238 /// Iterate over all the config variables
240 /// If `glob` is `Some`, then the iterator will only iterate over all
241 /// variables whose name matches the pattern.
246 /// # #![allow(unstable)]
247 /// use git2::Config;
249 /// let cfg = Config::new().unwrap();
251 /// for entry in &cfg.entries(None).unwrap() {
252 /// let entry = entry.unwrap();
253 /// println!("{} => {}", entry.name().unwrap(), entry.value().unwrap());
256 pub fn entries(&self, glob
: Option
<&str>) -> Result
<ConfigEntries
, Error
> {
257 let mut ret
= ptr
::null_mut();
261 let s
= try
!(CString
::new(s
));
262 try_call
!(raw
::git_config_iterator_glob_new(&mut ret
,
267 try_call
!(raw
::git_config_iterator_new(&mut ret
, &*self.raw
));
270 Ok(Binding
::from_raw(ret
))
274 /// Open the global/XDG configuration file according to git's rules
276 /// Git allows you to store your global configuration at `$HOME/.config` or
277 /// `$XDG_CONFIG_HOME/git/config`. For backwards compatability, the XDG file
278 /// shouldn't be used unless the use has created it explicitly. With this
279 /// function you'll open the correct one to write to.
280 pub fn open_global(&mut self) -> Result
<Config
, Error
> {
281 let mut raw
= ptr
::null_mut();
283 try_call
!(raw
::git_config_open_global(&mut raw
, self.raw
));
284 Ok(Binding
::from_raw(raw
))
288 /// Build a single-level focused config object from a multi-level one.
290 /// The returned config object can be used to perform get/set/delete
291 /// operations on a single specific level.
292 pub fn open_level(&self, level
: ConfigLevel
) -> Result
<Config
, Error
> {
293 let mut raw
= ptr
::null_mut();
295 try_call
!(raw
::git_config_open_level(&mut raw
, &*self.raw
, level
));
296 Ok(Binding
::from_raw(raw
))
300 /// Set the value of a boolean config variable in the config file with the
301 /// highest level (usually the local one).
302 pub fn set_bool(&mut self, name
: &str, value
: bool
) -> Result
<(), Error
> {
303 let name
= try
!(CString
::new(name
));
305 try_call
!(raw
::git_config_set_bool(self.raw
, name
, value
));
310 /// Set the value of an integer config variable in the config file with the
311 /// highest level (usually the local one).
312 pub fn set_i32(&mut self, name
: &str, value
: i32) -> Result
<(), Error
> {
313 let name
= try
!(CString
::new(name
));
315 try_call
!(raw
::git_config_set_int32(self.raw
, name
, value
));
320 /// Set the value of an integer config variable in the config file with the
321 /// highest level (usually the local one).
322 pub fn set_i64(&mut self, name
: &str, value
: i64) -> Result
<(), Error
> {
323 let name
= try
!(CString
::new(name
));
325 try_call
!(raw
::git_config_set_int64(self.raw
, name
, value
));
330 /// Set the value of an multivar config variable in the config file with the
331 /// highest level (usually the local one).
332 pub fn set_multivar(&mut self, name
: &str, regexp
: &str, value
: &str) -> Result
<(), Error
> {
333 let name
= try
!(CString
::new(name
));
334 let regexp
= try
!(CString
::new(regexp
));
335 let value
= try
!(CString
::new(value
));
337 try_call
!(raw
::git_config_set_multivar(self.raw
, name
, regexp
, value
));
342 /// Set the value of a string config variable in the config file with the
343 /// highest level (usually the local one).
344 pub fn set_str(&mut self, name
: &str, value
: &str) -> Result
<(), Error
> {
345 let name
= try
!(CString
::new(name
));
346 let value
= try
!(CString
::new(value
));
348 try_call
!(raw
::git_config_set_string(self.raw
, name
, value
));
353 /// Create a snapshot of the configuration
355 /// Create a snapshot of the current state of a configuration, which allows
356 /// you to look into a consistent view of the configuration for looking up
357 /// complex values (e.g. a remote, submodule).
358 pub fn snapshot(&mut self) -> Result
<Config
, Error
> {
359 let mut ret
= ptr
::null_mut();
361 try_call
!(raw
::git_config_snapshot(&mut ret
, self.raw
));
362 Ok(Binding
::from_raw(ret
))
366 /// Parse a string as a bool.
367 /// Interprets "true", "yes", "on", 1, or any non-zero number as true.
368 /// Interprets "false", "no", "off", 0, or an empty string as false.
369 pub fn parse_bool
<S
: IntoCString
>(s
: S
) -> Result
<bool
, Error
> {
370 let s
= try
!(s
.into_c_string());
374 try_call
!(raw
::git_config_parse_bool(&mut out
, s
));
379 /// Parse a string as an i32; handles suffixes like k, M, or G, and
380 /// multiplies by the appropriate power of 1024.
381 pub fn parse_i32
<S
: IntoCString
>(s
: S
) -> Result
<i32, Error
> {
382 let s
= try
!(s
.into_c_string());
386 try_call
!(raw
::git_config_parse_int32(&mut out
, s
));
391 /// Parse a string as an i64; handles suffixes like k, M, or G, and
392 /// multiplies by the appropriate power of 1024.
393 pub fn parse_i64
<S
: IntoCString
>(s
: S
) -> Result
<i64, Error
> {
394 let s
= try
!(s
.into_c_string());
398 try_call
!(raw
::git_config_parse_int64(&mut out
, s
));
404 impl Binding
for Config
{
405 type Raw
= *mut raw
::git_config
;
406 unsafe fn from_raw(raw
: *mut raw
::git_config
) -> Config
{
409 fn raw(&self) -> *mut raw
::git_config { self.raw }
412 impl Drop
for Config
{
414 unsafe { raw::git_config_free(self.raw) }
418 impl<'cfg
> ConfigEntry
<'cfg
> {
419 /// Gets the name of this entry.
421 /// May return `None` if the name is not valid utf-8
422 pub fn name(&self) -> Option
<&str> { str::from_utf8(self.name_bytes()).ok() }
424 /// Gets the name of this entry as a byte slice.
425 pub fn name_bytes(&self) -> &[u8] {
426 unsafe { ::opt_bytes(self, (*self.raw).name).unwrap() }
429 /// Gets the value of this entry.
431 /// May return `None` if the value is not valid utf-8
432 pub fn value(&self) -> Option
<&str> { str::from_utf8(self.value_bytes()).ok() }
434 /// Gets the value of this entry as a byte slice.
435 pub fn value_bytes(&self) -> &[u8] {
436 unsafe { ::opt_bytes(self, (*self.raw).value).unwrap() }
439 /// Gets the configuration level of this entry.
440 pub fn level(&self) -> ConfigLevel
{
441 unsafe { ConfigLevel::from_raw((*self.raw).level) }
445 impl<'cfg
> Binding
for ConfigEntry
<'cfg
> {
446 type Raw
= *mut raw
::git_config_entry
;
448 unsafe fn from_raw(raw
: *mut raw
::git_config_entry
)
449 -> ConfigEntry
<'cfg
> {
452 _marker
: marker
::PhantomData
,
456 fn raw(&self) -> *mut raw
::git_config_entry { self.raw }
459 impl<'cfg
> Binding
for ConfigEntries
<'cfg
> {
460 type Raw
= *mut raw
::git_config_iterator
;
462 unsafe fn from_raw(raw
: *mut raw
::git_config_iterator
)
463 -> ConfigEntries
<'cfg
> {
466 _marker
: marker
::PhantomData
,
469 fn raw(&self) -> *mut raw
::git_config_iterator { self.raw }
472 // entries are only valid until the iterator is freed, so this impl is for
473 // `&'b T` instead of `T` to have a lifetime to tie them to.
475 // It's also not implemented for `&'b mut T` so we can have multiple entries
477 impl<'cfg
, 'b
> Iterator
for &'b ConfigEntries
<'cfg
> {
478 type Item
= Result
<ConfigEntry
<'b
>, Error
>;
479 fn next(&mut self) -> Option
<Result
<ConfigEntry
<'b
>, Error
>> {
480 let mut raw
= ptr
::null_mut();
482 try_call_iter
!(raw
::git_config_next(&mut raw
, self.raw
));
483 Some(Ok(ConfigEntry
{
486 _marker
: marker
::PhantomData
,
492 impl<'cfg
> Drop
for ConfigEntries
<'cfg
> {
494 unsafe { raw::git_config_iterator_free(self.raw) }
498 impl<'cfg
> Drop
for ConfigEntry
<'cfg
> {
501 unsafe { raw::git_config_entry_free(self.raw) }
509 use tempdir
::TempDir
;
515 let _cfg
= Config
::new().unwrap();
516 let _
= Config
::find_global();
517 let _
= Config
::find_system();
518 let _
= Config
::find_xdg();
523 let td
= TempDir
::new("test").unwrap();
524 let path
= td
.path().join("foo");
525 File
::create(&path
).unwrap();
527 let mut cfg
= Config
::open(&path
).unwrap();
528 assert
!(cfg
.get_bool("foo.bar").is_err());
529 cfg
.set_bool("foo.k1", true).unwrap();
530 cfg
.set_i32("foo.k2", 1).unwrap();
531 cfg
.set_i64("foo.k3", 2).unwrap();
532 cfg
.set_str("foo.k4", "bar").unwrap();
533 cfg
.snapshot().unwrap();
536 let cfg
= Config
::open(&path
).unwrap().snapshot().unwrap();
537 assert_eq
!(cfg
.get_bool("foo.k1").unwrap(), true);
538 assert_eq
!(cfg
.get_i32("foo.k2").unwrap(), 1);
539 assert_eq
!(cfg
.get_i64("foo.k3").unwrap(), 2);
540 assert_eq
!(cfg
.get_str("foo.k4").unwrap(), "bar");
542 for entry
in &cfg
.entries(None
).unwrap() {
543 let entry
= entry
.unwrap();
552 let td
= TempDir
::new("test").unwrap();
553 let path
= td
.path().join("foo");
554 File
::create(&path
).unwrap();
556 let mut cfg
= Config
::open(&path
).unwrap();
557 cfg
.set_multivar("foo.bar", "^$", "baz").unwrap();
558 cfg
.set_multivar("foo.bar", "^$", "qux").unwrap();
560 let mut values
: Vec
<String
> = cfg
.entries(None
)
563 .map(|entry
| entry
.unwrap().value().unwrap().into())
566 assert_eq
!(values
, ["baz", "qux"]);
571 assert_eq
!(Config
::parse_bool("").unwrap(), false);
572 assert_eq
!(Config
::parse_bool("false").unwrap(), false);
573 assert_eq
!(Config
::parse_bool("no").unwrap(), false);
574 assert_eq
!(Config
::parse_bool("off").unwrap(), false);
575 assert_eq
!(Config
::parse_bool("0").unwrap(), false);
577 assert_eq
!(Config
::parse_bool("true").unwrap(), true);
578 assert_eq
!(Config
::parse_bool("yes").unwrap(), true);
579 assert_eq
!(Config
::parse_bool("on").unwrap(), true);
580 assert_eq
!(Config
::parse_bool("1").unwrap(), true);
581 assert_eq
!(Config
::parse_bool("42").unwrap(), true);
583 assert
!(Config
::parse_bool(" ").is_err());
584 assert
!(Config
::parse_bool("some-string").is_err());
585 assert
!(Config
::parse_bool("-").is_err());
587 assert_eq
!(Config
::parse_i32("0").unwrap(), 0);
588 assert_eq
!(Config
::parse_i32("1").unwrap(), 1);
589 assert_eq
!(Config
::parse_i32("100").unwrap(), 100);
590 assert_eq
!(Config
::parse_i32("-1").unwrap(), -1);
591 assert_eq
!(Config
::parse_i32("-100").unwrap(), -100);
592 assert_eq
!(Config
::parse_i32("1k").unwrap(), 1024);
593 assert_eq
!(Config
::parse_i32("4k").unwrap(), 4096);
594 assert_eq
!(Config
::parse_i32("1M").unwrap(), 1048576);
595 assert_eq
!(Config
::parse_i32("1G").unwrap(), 1024*1024*1024);
597 assert_eq
!(Config
::parse_i64("0").unwrap(), 0);
598 assert_eq
!(Config
::parse_i64("1").unwrap(), 1);
599 assert_eq
!(Config
::parse_i64("100").unwrap(), 100);
600 assert_eq
!(Config
::parse_i64("-1").unwrap(), -1);
601 assert_eq
!(Config
::parse_i64("-100").unwrap(), -100);
602 assert_eq
!(Config
::parse_i64("1k").unwrap(), 1024);
603 assert_eq
!(Config
::parse_i64("4k").unwrap(), 4096);
604 assert_eq
!(Config
::parse_i64("1M").unwrap(), 1048576);
605 assert_eq
!(Config
::parse_i64("1G").unwrap(), 1024*1024*1024);
606 assert_eq
!(Config
::parse_i64("100G").unwrap(), 100*1024*1024*1024);