4 use std
::path
::{Path, PathBuf}
;
8 use crate::util
::{self, Binding}
;
9 use crate::{raw, Buf, ConfigLevel, Error, IntoCString}
;
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 // Normal file path OK (does not need Windows conversion).
50 let path
= path
.into_c_string()?
;
52 try_call
!(raw
::git_config_open_ondisk(&mut raw
, path
));
53 Ok(Binding
::from_raw(raw
))
57 /// Open the global, XDG and system configuration files
59 /// Utility wrapper that finds the global, XDG and system configuration
60 /// files and opens them into a single prioritized config object that can
61 /// be used when accessing default config data outside a repository.
62 pub fn open_default() -> Result
<Config
, Error
> {
64 let mut raw
= ptr
::null_mut();
66 try_call
!(raw
::git_config_open_default(&mut raw
));
67 Ok(Binding
::from_raw(raw
))
71 /// Locate the path to the global configuration file
73 /// The user or global configuration file is usually located in
74 /// `$HOME/.gitconfig`.
76 /// This method will try to guess the full path to that file, if the file
77 /// exists. The returned path may be used on any method call to load
78 /// the global configuration file.
80 /// This method will not guess the path to the xdg compatible config file
81 /// (`.config/git/config`).
82 pub fn find_global() -> Result
<PathBuf
, Error
> {
86 try_call
!(raw
::git_config_find_global(buf
.raw()));
88 Ok(util
::bytes2path(&buf
).to_path_buf())
91 /// Locate the path to the system configuration file
93 /// If /etc/gitconfig doesn't exist, it will look for %PROGRAMFILES%
94 pub fn find_system() -> Result
<PathBuf
, Error
> {
98 try_call
!(raw
::git_config_find_system(buf
.raw()));
100 Ok(util
::bytes2path(&buf
).to_path_buf())
103 /// Locate the path to the global xdg compatible configuration file
105 /// The xdg compatible configuration file is usually located in
106 /// `$HOME/.config/git/config`.
107 pub fn find_xdg() -> Result
<PathBuf
, Error
> {
109 let buf
= Buf
::new();
111 try_call
!(raw
::git_config_find_xdg(buf
.raw()));
113 Ok(util
::bytes2path(&buf
).to_path_buf())
116 /// Add an on-disk config file instance to an existing config
118 /// The on-disk file pointed at by path will be opened and parsed; it's
119 /// expected to be a native Git config file following the default Git config
120 /// syntax (see man git-config).
122 /// Further queries on this config object will access each of the config
123 /// file instances in order (instances with a higher priority level will be
125 pub fn add_file(&mut self, path
: &Path
, level
: ConfigLevel
, force
: bool
) -> Result
<(), Error
> {
126 // Normal file path OK (does not need Windows conversion).
127 let path
= path
.into_c_string()?
;
129 try_call
!(raw
::git_config_add_file_ondisk(
140 /// Delete a config variable from the config file with the highest level
141 /// (usually the local one).
142 pub fn remove(&mut self, name
: &str) -> Result
<(), Error
> {
143 let name
= CString
::new(name
)?
;
145 try_call
!(raw
::git_config_delete_entry(self.raw
, name
));
150 /// Remove multivar config variables in the config file with the highest level (usually the
152 pub fn remove_multivar(&mut self, name
: &str, regexp
: &str) -> Result
<(), Error
> {
153 let name
= CString
::new(name
)?
;
154 let regexp
= CString
::new(regexp
)?
;
156 try_call
!(raw
::git_config_delete_multivar(self.raw
, name
, regexp
));
161 /// Get the value of a boolean config variable.
163 /// All config files will be looked into, in the order of their defined
164 /// level. A higher level means a higher priority. The first occurrence of
165 /// the variable will be returned here.
166 pub fn get_bool(&self, name
: &str) -> Result
<bool
, Error
> {
167 let mut out
= 0 as libc
::c_int
;
168 let name
= CString
::new(name
)?
;
170 try_call
!(raw
::git_config_get_bool(&mut out
, &*self.raw
, name
));
175 /// Get the value of an integer config variable.
177 /// All config files will be looked into, in the order of their defined
178 /// level. A higher level means a higher priority. The first occurrence of
179 /// the variable will be returned here.
180 pub fn get_i32(&self, name
: &str) -> Result
<i32, Error
> {
182 let name
= CString
::new(name
)?
;
184 try_call
!(raw
::git_config_get_int32(&mut out
, &*self.raw
, name
));
189 /// Get the value of an integer config variable.
191 /// All config files will be looked into, in the order of their defined
192 /// level. A higher level means a higher priority. The first occurrence of
193 /// the variable will be returned here.
194 pub fn get_i64(&self, name
: &str) -> Result
<i64, Error
> {
196 let name
= CString
::new(name
)?
;
198 try_call
!(raw
::git_config_get_int64(&mut out
, &*self.raw
, name
));
203 /// Get the value of a string config variable.
205 /// This is the same as `get_bytes` except that it may return `Err` if
206 /// the bytes are not valid utf-8.
207 pub fn get_str(&self, name
: &str) -> Result
<&str, Error
> {
208 str::from_utf8(self.get_bytes(name
)?
)
209 .map_err(|_
| Error
::from_str("configuration value is not valid utf8"))
212 /// Get the value of a string config variable as a byte slice.
214 /// This method will return an error if this `Config` is not a snapshot.
215 pub fn get_bytes(&self, name
: &str) -> Result
<&[u8], Error
> {
216 let mut ret
= ptr
::null();
217 let name
= CString
::new(name
)?
;
219 try_call
!(raw
::git_config_get_string(&mut ret
, &*self.raw
, name
));
220 Ok(crate::opt_bytes(self, ret
).unwrap())
224 /// Get the value of a string config variable as an owned string.
226 /// An error will be returned if the config value is not valid utf-8.
227 pub fn get_string(&self, name
: &str) -> Result
<String
, Error
> {
228 let ret
= Buf
::new();
229 let name
= CString
::new(name
)?
;
231 try_call
!(raw
::git_config_get_string_buf(ret
.raw(), self.raw
, name
));
234 .map(|s
| s
.to_string())
235 .map_err(|_
| Error
::from_str("configuration value is not valid utf8"))
238 /// Get the value of a path config variable as an owned .
239 pub fn get_path(&self, name
: &str) -> Result
<PathBuf
, Error
> {
240 let ret
= Buf
::new();
241 let name
= CString
::new(name
)?
;
243 try_call
!(raw
::git_config_get_path(ret
.raw(), self.raw
, name
));
245 Ok(crate::util
::bytes2path(&ret
).to_path_buf())
248 /// Get the ConfigEntry for a config variable.
249 pub fn get_entry(&self, name
: &str) -> Result
<ConfigEntry
<'_
>, Error
> {
250 let mut ret
= ptr
::null_mut();
251 let name
= CString
::new(name
)?
;
253 try_call
!(raw
::git_config_get_entry(&mut ret
, self.raw
, name
));
254 Ok(Binding
::from_raw(ret
))
258 /// Iterate over all the config variables
260 /// If `glob` is `Some`, then the iterator will only iterate over all
261 /// variables whose name matches the pattern.
266 /// # #![allow(unstable)]
267 /// use git2::Config;
269 /// let cfg = Config::new().unwrap();
271 /// for entry in &cfg.entries(None).unwrap() {
272 /// let entry = entry.unwrap();
273 /// println!("{} => {}", entry.name().unwrap(), entry.value().unwrap());
276 pub fn entries(&self, glob
: Option
<&str>) -> Result
<ConfigEntries
<'_
>, Error
> {
277 let mut ret
= ptr
::null_mut();
281 let s
= CString
::new(s
)?
;
282 try_call
!(raw
::git_config_iterator_glob_new(&mut ret
, &*self.raw
, s
));
285 try_call
!(raw
::git_config_iterator_new(&mut ret
, &*self.raw
));
288 Ok(Binding
::from_raw(ret
))
292 /// Iterate over the values of a multivar
294 /// If `regexp` is `Some`, then the iterator will only iterate over all
295 /// values which match the pattern.
296 pub fn multivar(&self, name
: &str, regexp
: Option
<&str>) -> Result
<ConfigEntries
<'_
>, Error
> {
297 let mut ret
= ptr
::null_mut();
298 let name
= CString
::new(name
)?
;
299 let regexp
= regexp
.map(CString
::new
).transpose()?
;
301 try_call
!(raw
::git_config_multivar_iterator_new(
302 &mut ret
, &*self.raw
, name
, regexp
304 Ok(Binding
::from_raw(ret
))
308 /// Open the global/XDG configuration file according to git's rules
310 /// Git allows you to store your global configuration at `$HOME/.config` or
311 /// `$XDG_CONFIG_HOME/git/config`. For backwards compatability, the XDG file
312 /// shouldn't be used unless the use has created it explicitly. With this
313 /// function you'll open the correct one to write to.
314 pub fn open_global(&mut self) -> Result
<Config
, Error
> {
315 let mut raw
= ptr
::null_mut();
317 try_call
!(raw
::git_config_open_global(&mut raw
, self.raw
));
318 Ok(Binding
::from_raw(raw
))
322 /// Build a single-level focused config object from a multi-level one.
324 /// The returned config object can be used to perform get/set/delete
325 /// operations on a single specific level.
326 pub fn open_level(&self, level
: ConfigLevel
) -> Result
<Config
, Error
> {
327 let mut raw
= ptr
::null_mut();
329 try_call
!(raw
::git_config_open_level(&mut raw
, &*self.raw
, level
));
330 Ok(Binding
::from_raw(raw
))
334 /// Set the value of a boolean config variable in the config file with the
335 /// highest level (usually the local one).
336 pub fn set_bool(&mut self, name
: &str, value
: bool
) -> Result
<(), Error
> {
337 let name
= CString
::new(name
)?
;
339 try_call
!(raw
::git_config_set_bool(self.raw
, name
, value
));
344 /// Set the value of an integer config variable in the config file with the
345 /// highest level (usually the local one).
346 pub fn set_i32(&mut self, name
: &str, value
: i32) -> Result
<(), Error
> {
347 let name
= CString
::new(name
)?
;
349 try_call
!(raw
::git_config_set_int32(self.raw
, name
, value
));
354 /// Set the value of an integer config variable in the config file with the
355 /// highest level (usually the local one).
356 pub fn set_i64(&mut self, name
: &str, value
: i64) -> Result
<(), Error
> {
357 let name
= CString
::new(name
)?
;
359 try_call
!(raw
::git_config_set_int64(self.raw
, name
, value
));
364 /// Set the value of an multivar config variable in the config file with the
365 /// highest level (usually the local one).
366 pub fn set_multivar(&mut self, name
: &str, regexp
: &str, value
: &str) -> Result
<(), Error
> {
367 let name
= CString
::new(name
)?
;
368 let regexp
= CString
::new(regexp
)?
;
369 let value
= CString
::new(value
)?
;
371 try_call
!(raw
::git_config_set_multivar(self.raw
, name
, regexp
, value
));
376 /// Set the value of a string config variable in the config file with the
377 /// highest level (usually the local one).
378 pub fn set_str(&mut self, name
: &str, value
: &str) -> Result
<(), Error
> {
379 let name
= CString
::new(name
)?
;
380 let value
= CString
::new(value
)?
;
382 try_call
!(raw
::git_config_set_string(self.raw
, name
, value
));
387 /// Create a snapshot of the configuration
389 /// Create a snapshot of the current state of a configuration, which allows
390 /// you to look into a consistent view of the configuration for looking up
391 /// complex values (e.g. a remote, submodule).
392 pub fn snapshot(&mut self) -> Result
<Config
, Error
> {
393 let mut ret
= ptr
::null_mut();
395 try_call
!(raw
::git_config_snapshot(&mut ret
, self.raw
));
396 Ok(Binding
::from_raw(ret
))
400 /// Parse a string as a bool.
401 /// Interprets "true", "yes", "on", 1, or any non-zero number as true.
402 /// Interprets "false", "no", "off", 0, or an empty string as false.
403 pub fn parse_bool
<S
: IntoCString
>(s
: S
) -> Result
<bool
, Error
> {
404 let s
= s
.into_c_string()?
;
408 try_call
!(raw
::git_config_parse_bool(&mut out
, s
));
413 /// Parse a string as an i32; handles suffixes like k, M, or G, and
414 /// multiplies by the appropriate power of 1024.
415 pub fn parse_i32
<S
: IntoCString
>(s
: S
) -> Result
<i32, Error
> {
416 let s
= s
.into_c_string()?
;
420 try_call
!(raw
::git_config_parse_int32(&mut out
, s
));
425 /// Parse a string as an i64; handles suffixes like k, M, or G, and
426 /// multiplies by the appropriate power of 1024.
427 pub fn parse_i64
<S
: IntoCString
>(s
: S
) -> Result
<i64, Error
> {
428 let s
= s
.into_c_string()?
;
432 try_call
!(raw
::git_config_parse_int64(&mut out
, s
));
438 impl Binding
for Config
{
439 type Raw
= *mut raw
::git_config
;
440 unsafe fn from_raw(raw
: *mut raw
::git_config
) -> Config
{
443 fn raw(&self) -> *mut raw
::git_config
{
448 impl Drop
for Config
{
450 unsafe { raw::git_config_free(self.raw) }
454 impl<'cfg
> ConfigEntry
<'cfg
> {
455 /// Gets the name of this entry.
457 /// May return `None` if the name is not valid utf-8
458 pub fn name(&self) -> Option
<&str> {
459 str::from_utf8(self.name_bytes()).ok()
462 /// Gets the name of this entry as a byte slice.
463 pub fn name_bytes(&self) -> &[u8] {
464 unsafe { crate::opt_bytes(self, (*self.raw).name).unwrap() }
467 /// Gets the value of this entry.
469 /// May return `None` if the value is not valid utf-8
473 /// Panics when no value is defined.
474 pub fn value(&self) -> Option
<&str> {
475 str::from_utf8(self.value_bytes()).ok()
478 /// Gets the value of this entry as a byte slice.
482 /// Panics when no value is defined.
483 pub fn value_bytes(&self) -> &[u8] {
484 unsafe { crate::opt_bytes(self, (*self.raw).value).unwrap() }
487 /// Returns `true` when a value is defined otherwise `false`.
489 /// No value defined is a short-hand to represent a Boolean `true`.
490 pub fn has_value(&self) -> bool
{
491 unsafe { !(*self.raw).value.is_null() }
494 /// Gets the configuration level of this entry.
495 pub fn level(&self) -> ConfigLevel
{
496 unsafe { ConfigLevel::from_raw((*self.raw).level) }
499 /// Depth of includes where this variable was found
500 pub fn include_depth(&self) -> u32 {
501 unsafe { (*self.raw).include_depth as u32 }
505 impl<'cfg
> Binding
for ConfigEntry
<'cfg
> {
506 type Raw
= *mut raw
::git_config_entry
;
508 unsafe fn from_raw(raw
: *mut raw
::git_config_entry
) -> ConfigEntry
<'cfg
> {
511 _marker
: marker
::PhantomData
,
515 fn raw(&self) -> *mut raw
::git_config_entry
{
520 impl<'cfg
> Binding
for ConfigEntries
<'cfg
> {
521 type Raw
= *mut raw
::git_config_iterator
;
523 unsafe fn from_raw(raw
: *mut raw
::git_config_iterator
) -> ConfigEntries
<'cfg
> {
526 _marker
: marker
::PhantomData
,
529 fn raw(&self) -> *mut raw
::git_config_iterator
{
534 // entries are only valid until the iterator is freed, so this impl is for
535 // `&'b T` instead of `T` to have a lifetime to tie them to.
537 // It's also not implemented for `&'b mut T` so we can have multiple entries
539 impl<'cfg
, 'b
> Iterator
for &'b ConfigEntries
<'cfg
> {
540 type Item
= Result
<ConfigEntry
<'b
>, Error
>;
541 fn next(&mut self) -> Option
<Result
<ConfigEntry
<'b
>, Error
>> {
542 let mut raw
= ptr
::null_mut();
544 try_call_iter
!(raw
::git_config_next(&mut raw
, self.raw
));
545 Some(Ok(ConfigEntry
{
548 _marker
: marker
::PhantomData
,
554 impl<'cfg
> Drop
for ConfigEntries
<'cfg
> {
556 unsafe { raw::git_config_iterator_free(self.raw) }
560 impl<'cfg
> Drop
for ConfigEntry
<'cfg
> {
563 unsafe { raw::git_config_entry_free(self.raw) }
571 use tempfile
::TempDir
;
577 let _cfg
= Config
::new().unwrap();
578 let _
= Config
::find_global();
579 let _
= Config
::find_system();
580 let _
= Config
::find_xdg();
585 let td
= TempDir
::new().unwrap();
586 let path
= td
.path().join("foo");
587 File
::create(&path
).unwrap();
589 let mut cfg
= Config
::open(&path
).unwrap();
590 assert
!(cfg
.get_bool("foo.bar").is_err());
591 cfg
.set_bool("foo.k1", true).unwrap();
592 cfg
.set_i32("foo.k2", 1).unwrap();
593 cfg
.set_i64("foo.k3", 2).unwrap();
594 cfg
.set_str("foo.k4", "bar").unwrap();
595 cfg
.snapshot().unwrap();
598 let cfg
= Config
::open(&path
).unwrap().snapshot().unwrap();
599 assert_eq
!(cfg
.get_bool("foo.k1").unwrap(), true);
600 assert_eq
!(cfg
.get_i32("foo.k2").unwrap(), 1);
601 assert_eq
!(cfg
.get_i64("foo.k3").unwrap(), 2);
602 assert_eq
!(cfg
.get_str("foo.k4").unwrap(), "bar");
604 for entry
in &cfg
.entries(None
).unwrap() {
605 let entry
= entry
.unwrap();
614 let td
= TempDir
::new().unwrap();
615 let path
= td
.path().join("foo");
616 File
::create(&path
).unwrap();
618 let mut cfg
= Config
::open(&path
).unwrap();
619 cfg
.set_multivar("foo.bar", "^$", "baz").unwrap();
620 cfg
.set_multivar("foo.bar", "^$", "qux").unwrap();
621 cfg
.set_multivar("foo.bar", "^$", "quux").unwrap();
622 cfg
.set_multivar("foo.baz", "^$", "oki").unwrap();
624 // `entries` filters by name
625 let mut entries
: Vec
<String
> = cfg
626 .entries(Some("foo.bar"))
629 .map(|entry
| entry
.unwrap().value().unwrap().into())
632 assert_eq
!(entries
, ["baz", "quux", "qux"]);
634 // which is the same as `multivar` without a regex
635 let mut multivals
: Vec
<String
> = cfg
636 .multivar("foo.bar", None
)
639 .map(|entry
| entry
.unwrap().value().unwrap().into())
642 assert_eq
!(multivals
, entries
);
644 // yet _with_ a regex, `multivar` filters by value
645 let mut quxish
: Vec
<String
> = cfg
646 .multivar("foo.bar", Some("qu.*x"))
649 .map(|entry
| entry
.unwrap().value().unwrap().into())
652 assert_eq
!(quxish
, ["quux", "qux"]);
654 cfg
.remove_multivar("foo.bar", ".*").unwrap();
656 assert_eq
!(cfg
.entries(Some("foo.bar")).unwrap().count(), 0);
657 assert_eq
!(cfg
.multivar("foo.bar", None
).unwrap().count(), 0);
662 assert_eq
!(Config
::parse_bool("").unwrap(), false);
663 assert_eq
!(Config
::parse_bool("false").unwrap(), false);
664 assert_eq
!(Config
::parse_bool("no").unwrap(), false);
665 assert_eq
!(Config
::parse_bool("off").unwrap(), false);
666 assert_eq
!(Config
::parse_bool("0").unwrap(), false);
668 assert_eq
!(Config
::parse_bool("true").unwrap(), true);
669 assert_eq
!(Config
::parse_bool("yes").unwrap(), true);
670 assert_eq
!(Config
::parse_bool("on").unwrap(), true);
671 assert_eq
!(Config
::parse_bool("1").unwrap(), true);
672 assert_eq
!(Config
::parse_bool("42").unwrap(), true);
674 assert
!(Config
::parse_bool(" ").is_err());
675 assert
!(Config
::parse_bool("some-string").is_err());
676 assert
!(Config
::parse_bool("-").is_err());
678 assert_eq
!(Config
::parse_i32("0").unwrap(), 0);
679 assert_eq
!(Config
::parse_i32("1").unwrap(), 1);
680 assert_eq
!(Config
::parse_i32("100").unwrap(), 100);
681 assert_eq
!(Config
::parse_i32("-1").unwrap(), -1);
682 assert_eq
!(Config
::parse_i32("-100").unwrap(), -100);
683 assert_eq
!(Config
::parse_i32("1k").unwrap(), 1024);
684 assert_eq
!(Config
::parse_i32("4k").unwrap(), 4096);
685 assert_eq
!(Config
::parse_i32("1M").unwrap(), 1048576);
686 assert_eq
!(Config
::parse_i32("1G").unwrap(), 1024 * 1024 * 1024);
688 assert_eq
!(Config
::parse_i64("0").unwrap(), 0);
689 assert_eq
!(Config
::parse_i64("1").unwrap(), 1);
690 assert_eq
!(Config
::parse_i64("100").unwrap(), 100);
691 assert_eq
!(Config
::parse_i64("-1").unwrap(), -1);
692 assert_eq
!(Config
::parse_i64("-100").unwrap(), -100);
693 assert_eq
!(Config
::parse_i64("1k").unwrap(), 1024);
694 assert_eq
!(Config
::parse_i64("4k").unwrap(), 4096);
695 assert_eq
!(Config
::parse_i64("1M").unwrap(), 1048576);
696 assert_eq
!(Config
::parse_i64("1G").unwrap(), 1024 * 1024 * 1024);
697 assert_eq
!(Config
::parse_i64("100G").unwrap(), 100 * 1024 * 1024 * 1024);