]> git.proxmox.com Git - cargo.git/blob - vendor/git2-0.6.8/src/config.rs
New upstream version 0.23.0
[cargo.git] / vendor / git2-0.6.8 / src / config.rs
1 use std::ffi::CString;
2 use std::marker;
3 use std::path::{Path, PathBuf};
4 use std::ptr;
5 use std::str;
6 use libc;
7
8 use {raw, Error, ConfigLevel, Buf, IntoCString};
9 use util::{self, Binding};
10
11 /// A structure representing a git configuration key/value store
12 pub struct Config {
13 raw: *mut raw::git_config,
14 }
15
16 /// A struct representing a certain entry owned by a `Config` instance.
17 ///
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>,
22 owned: bool,
23 }
24
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>,
29 }
30
31 impl Config {
32 /// Allocate a new configuration object
33 ///
34 /// This object is empty, so you have to add a file to it before you can do
35 /// anything with it.
36 pub fn new() -> Result<Config, Error> {
37 ::init();
38 let mut raw = ptr::null_mut();
39 unsafe {
40 try_call!(raw::git_config_new(&mut raw));
41 Ok(Binding::from_raw(raw))
42 }
43 }
44
45 /// Create a new config instance containing a single on-disk file
46 pub fn open(path: &Path) -> Result<Config, Error> {
47 ::init();
48 let mut raw = ptr::null_mut();
49 let path = try!(path.into_c_string());
50 unsafe {
51 try_call!(raw::git_config_open_ondisk(&mut raw, path));
52 Ok(Binding::from_raw(raw))
53 }
54 }
55
56 /// Open the global, XDG and system configuration files
57 ///
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> {
62 ::init();
63 let mut raw = ptr::null_mut();
64 unsafe {
65 try_call!(raw::git_config_open_default(&mut raw));
66 Ok(Binding::from_raw(raw))
67 }
68 }
69
70 /// Locate the path to the global configuration file
71 ///
72 /// The user or global configuration file is usually located in
73 /// `$HOME/.gitconfig`.
74 ///
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.
78 ///
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> {
82 ::init();
83 let buf = Buf::new();
84 unsafe { try_call!(raw::git_config_find_global(buf.raw())); }
85 Ok(util::bytes2path(&buf).to_path_buf())
86 }
87
88 /// Locate the path to the system configuration file
89 ///
90 /// If /etc/gitconfig doesn't exist, it will look for %PROGRAMFILES%
91 pub fn find_system() -> Result<PathBuf, Error> {
92 ::init();
93 let buf = Buf::new();
94 unsafe { try_call!(raw::git_config_find_system(buf.raw())); }
95 Ok(util::bytes2path(&buf).to_path_buf())
96 }
97
98 /// Locate the path to the global xdg compatible configuration file
99 ///
100 /// The xdg compatible configuration file is usually located in
101 /// `$HOME/.config/git/config`.
102 pub fn find_xdg() -> Result<PathBuf, Error> {
103 ::init();
104 let buf = Buf::new();
105 unsafe { try_call!(raw::git_config_find_xdg(buf.raw())); }
106 Ok(util::bytes2path(&buf).to_path_buf())
107 }
108
109 /// Add an on-disk config file instance to an existing config
110 ///
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).
114 ///
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
117 /// accessed first).
118 pub fn add_file(&mut self, path: &Path, level: ConfigLevel,
119 force: bool) -> Result<(), Error> {
120 let path = try!(path.into_c_string());
121 unsafe {
122 try_call!(raw::git_config_add_file_ondisk(self.raw, path, level,
123 force));
124 Ok(())
125 }
126 }
127
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));
132 unsafe {
133 try_call!(raw::git_config_delete_entry(self.raw, name));
134 Ok(())
135 }
136 }
137
138 /// Get the value of a boolean config variable.
139 ///
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));
146 unsafe {
147 try_call!(raw::git_config_get_bool(&mut out, &*self.raw, name));
148
149 }
150 Ok(!(out == 0))
151 }
152
153 /// Get the value of an integer config variable.
154 ///
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> {
159 let mut out = 0i32;
160 let name = try!(CString::new(name));
161 unsafe {
162 try_call!(raw::git_config_get_int32(&mut out, &*self.raw, name));
163
164 }
165 Ok(out)
166 }
167
168 /// Get the value of an integer config variable.
169 ///
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> {
174 let mut out = 0i64;
175 let name = try!(CString::new(name));
176 unsafe {
177 try_call!(raw::git_config_get_int64(&mut out, &*self.raw, name));
178 }
179 Ok(out)
180 }
181
182 /// Get the value of a string config variable.
183 ///
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")
189 })
190 }
191
192 /// Get the value of a string config variable as a byte slice.
193 ///
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));
198 unsafe {
199 try_call!(raw::git_config_get_string(&mut ret, &*self.raw, name));
200 Ok(::opt_bytes(self, ret).unwrap())
201 }
202 }
203
204 /// Get the value of a string config variable as an owned string.
205 ///
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));
210 unsafe {
211 try_call!(raw::git_config_get_string_buf(ret.raw(), self.raw, name));
212 }
213 str::from_utf8(&ret).map(|s| s.to_string()).map_err(|_| {
214 Error::from_str("configuration value is not valid utf8")
215 })
216 }
217
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));
222 unsafe {
223 try_call!(raw::git_config_get_path(ret.raw(), self.raw, name));
224 }
225 Ok(::util::bytes2path(&ret).to_path_buf())
226 }
227
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));
232 unsafe {
233 try_call!(raw::git_config_get_entry(&mut ret, self.raw, name));
234 Ok(Binding::from_raw(ret))
235 }
236 }
237
238 /// Iterate over all the config variables
239 ///
240 /// If `glob` is `Some`, then the iterator will only iterate over all
241 /// variables whose name matches the pattern.
242 ///
243 /// # Example
244 ///
245 /// ```
246 /// # #![allow(unstable)]
247 /// use git2::Config;
248 ///
249 /// let cfg = Config::new().unwrap();
250 ///
251 /// for entry in &cfg.entries(None).unwrap() {
252 /// let entry = entry.unwrap();
253 /// println!("{} => {}", entry.name().unwrap(), entry.value().unwrap());
254 /// }
255 /// ```
256 pub fn entries(&self, glob: Option<&str>) -> Result<ConfigEntries, Error> {
257 let mut ret = ptr::null_mut();
258 unsafe {
259 match glob {
260 Some(s) => {
261 let s = try!(CString::new(s));
262 try_call!(raw::git_config_iterator_glob_new(&mut ret,
263 &*self.raw,
264 s));
265 }
266 None => {
267 try_call!(raw::git_config_iterator_new(&mut ret, &*self.raw));
268 }
269 }
270 Ok(Binding::from_raw(ret))
271 }
272 }
273
274 /// Open the global/XDG configuration file according to git's rules
275 ///
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();
282 unsafe {
283 try_call!(raw::git_config_open_global(&mut raw, self.raw));
284 Ok(Binding::from_raw(raw))
285 }
286 }
287
288 /// Build a single-level focused config object from a multi-level one.
289 ///
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();
294 unsafe {
295 try_call!(raw::git_config_open_level(&mut raw, &*self.raw, level));
296 Ok(Binding::from_raw(raw))
297 }
298 }
299
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));
304 unsafe {
305 try_call!(raw::git_config_set_bool(self.raw, name, value));
306 }
307 Ok(())
308 }
309
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));
314 unsafe {
315 try_call!(raw::git_config_set_int32(self.raw, name, value));
316 }
317 Ok(())
318 }
319
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));
324 unsafe {
325 try_call!(raw::git_config_set_int64(self.raw, name, value));
326 }
327 Ok(())
328 }
329
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));
336 unsafe {
337 try_call!(raw::git_config_set_multivar(self.raw, name, regexp, value));
338 }
339 Ok(())
340 }
341
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));
347 unsafe {
348 try_call!(raw::git_config_set_string(self.raw, name, value));
349 }
350 Ok(())
351 }
352
353 /// Create a snapshot of the configuration
354 ///
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();
360 unsafe {
361 try_call!(raw::git_config_snapshot(&mut ret, self.raw));
362 Ok(Binding::from_raw(ret))
363 }
364 }
365
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());
371 let mut out = 0;
372 ::init();
373 unsafe {
374 try_call!(raw::git_config_parse_bool(&mut out, s));
375 }
376 Ok(out != 0)
377 }
378
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());
383 let mut out = 0;
384 ::init();
385 unsafe {
386 try_call!(raw::git_config_parse_int32(&mut out, s));
387 }
388 Ok(out)
389 }
390
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());
395 let mut out = 0;
396 ::init();
397 unsafe {
398 try_call!(raw::git_config_parse_int64(&mut out, s));
399 }
400 Ok(out)
401 }
402 }
403
404 impl Binding for Config {
405 type Raw = *mut raw::git_config;
406 unsafe fn from_raw(raw: *mut raw::git_config) -> Config {
407 Config { raw: raw }
408 }
409 fn raw(&self) -> *mut raw::git_config { self.raw }
410 }
411
412 impl Drop for Config {
413 fn drop(&mut self) {
414 unsafe { raw::git_config_free(self.raw) }
415 }
416 }
417
418 impl<'cfg> ConfigEntry<'cfg> {
419 /// Gets the name of this entry.
420 ///
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() }
423
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() }
427 }
428
429 /// Gets the value of this entry.
430 ///
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() }
433
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() }
437 }
438
439 /// Gets the configuration level of this entry.
440 pub fn level(&self) -> ConfigLevel {
441 unsafe { ConfigLevel::from_raw((*self.raw).level) }
442 }
443 }
444
445 impl<'cfg> Binding for ConfigEntry<'cfg> {
446 type Raw = *mut raw::git_config_entry;
447
448 unsafe fn from_raw(raw: *mut raw::git_config_entry)
449 -> ConfigEntry<'cfg> {
450 ConfigEntry {
451 raw: raw,
452 _marker: marker::PhantomData,
453 owned: true,
454 }
455 }
456 fn raw(&self) -> *mut raw::git_config_entry { self.raw }
457 }
458
459 impl<'cfg> Binding for ConfigEntries<'cfg> {
460 type Raw = *mut raw::git_config_iterator;
461
462 unsafe fn from_raw(raw: *mut raw::git_config_iterator)
463 -> ConfigEntries<'cfg> {
464 ConfigEntries {
465 raw: raw,
466 _marker: marker::PhantomData,
467 }
468 }
469 fn raw(&self) -> *mut raw::git_config_iterator { self.raw }
470 }
471
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.
474 //
475 // It's also not implemented for `&'b mut T` so we can have multiple entries
476 // (ok).
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();
481 unsafe {
482 try_call_iter!(raw::git_config_next(&mut raw, self.raw));
483 Some(Ok(ConfigEntry {
484 owned: false,
485 raw: raw,
486 _marker: marker::PhantomData,
487 }))
488 }
489 }
490 }
491
492 impl<'cfg> Drop for ConfigEntries<'cfg> {
493 fn drop(&mut self) {
494 unsafe { raw::git_config_iterator_free(self.raw) }
495 }
496 }
497
498 impl<'cfg> Drop for ConfigEntry<'cfg> {
499 fn drop(&mut self) {
500 if self.owned {
501 unsafe { raw::git_config_entry_free(self.raw) }
502 }
503 }
504 }
505
506 #[cfg(test)]
507 mod tests {
508 use std::fs::File;
509 use tempdir::TempDir;
510
511 use Config;
512
513 #[test]
514 fn smoke() {
515 let _cfg = Config::new().unwrap();
516 let _ = Config::find_global();
517 let _ = Config::find_system();
518 let _ = Config::find_xdg();
519 }
520
521 #[test]
522 fn persisted() {
523 let td = TempDir::new("test").unwrap();
524 let path = td.path().join("foo");
525 File::create(&path).unwrap();
526
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();
534 drop(cfg);
535
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");
541
542 for entry in &cfg.entries(None).unwrap() {
543 let entry = entry.unwrap();
544 entry.name();
545 entry.value();
546 entry.level();
547 }
548 }
549
550 #[test]
551 fn multivar() {
552 let td = TempDir::new("test").unwrap();
553 let path = td.path().join("foo");
554 File::create(&path).unwrap();
555
556 let mut cfg = Config::open(&path).unwrap();
557 cfg.set_multivar("foo.bar", "^$", "baz").unwrap();
558 cfg.set_multivar("foo.bar", "^$", "qux").unwrap();
559
560 let mut values: Vec<String> = cfg.entries(None)
561 .unwrap()
562 .into_iter()
563 .map(|entry| entry.unwrap().value().unwrap().into())
564 .collect();
565 values.sort();
566 assert_eq!(values, ["baz", "qux"]);
567 }
568
569 #[test]
570 fn parse() {
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);
576
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);
582
583 assert!(Config::parse_bool(" ").is_err());
584 assert!(Config::parse_bool("some-string").is_err());
585 assert!(Config::parse_bool("-").is_err());
586
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);
596
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);
607 }
608 }