1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
use crate::file::{from_paths, resolve_includes};
use crate::values::path::interpolate;
use crate::File;
use std::borrow::Cow;
use std::path::PathBuf;
#[derive(Debug, thiserror::Error)]
#[allow(missing_docs)]
pub enum Error {
#[error("GIT_CONFIG_COUNT was not a positive integer: {}", .input)]
ParseError { input: String },
#[error("GIT_CONFIG_KEY_{} was not set", .key_id)]
InvalidKeyId { key_id: usize },
#[error("GIT_CONFIG_KEY_{} was set to an invalid value: {}", .key_id, .key_val)]
InvalidKeyValue { key_id: usize, key_val: String },
#[error("GIT_CONFIG_VALUE_{} was not set", .value_id)]
InvalidValueId { value_id: usize },
#[error(transparent)]
PathInterpolationError(#[from] interpolate::Error),
#[error(transparent)]
FromPathsError(#[from] from_paths::Error),
}
impl<'a> File<'a> {
pub fn from_env_paths(options: from_paths::Options<'_>) -> Result<File<'static>, from_paths::Error> {
use std::env;
let mut paths = vec![];
if env::var("GIT_CONFIG_NO_SYSTEM").is_err() {
if let Some(git_config_system) = env::var_os("GIT_CONFIG_SYSTEM") {
paths.push(PathBuf::from(git_config_system))
} else {
paths.push(PathBuf::from("/etc/gitconfig"));
}
}
if let Some(git_config_global) = env::var_os("GIT_CONFIG_GLOBAL") {
paths.push(PathBuf::from(git_config_global));
} else {
if let Some(xdg_config_home) = env::var_os("XDG_CONFIG_HOME") {
paths.push(PathBuf::from(xdg_config_home).join("git/config"));
} else if let Some(home) = env::var_os("HOME") {
paths.push(PathBuf::from(home).join(".config/git/config"));
}
if let Some(home) = env::var_os("HOME") {
paths.push(PathBuf::from(home).join(".gitconfig"));
}
}
if let Some(git_dir) = env::var_os("GIT_DIR") {
paths.push(PathBuf::from(git_dir).join("config"));
}
File::from_paths(paths, options)
}
pub fn from_env(options: from_paths::Options<'_>) -> Result<Option<File<'_>>, Error> {
use std::env;
let count: usize = match env::var("GIT_CONFIG_COUNT") {
Ok(v) => v.parse().map_err(|_| Error::ParseError { input: v })?,
Err(_) => return Ok(None),
};
let mut config = File::new();
for i in 0..count {
let key = env::var(format!("GIT_CONFIG_KEY_{}", i)).map_err(|_| Error::InvalidKeyId { key_id: i })?;
let value = env::var_os(format!("GIT_CONFIG_VALUE_{}", i)).ok_or(Error::InvalidValueId { value_id: i })?;
if let Some((section_name, maybe_subsection)) = key.split_once('.') {
let (subsection, key) = if let Some((subsection, key)) = maybe_subsection.rsplit_once('.') {
(Some(subsection), key)
} else {
(None, maybe_subsection)
};
let mut section = if let Ok(section) = config.section_mut(section_name, subsection) {
section
} else {
config.new_section(
section_name.to_string(),
subsection.map(|subsection| Cow::Owned(subsection.to_string())),
)
};
section.push(
Cow::<str>::Owned(key.to_string()).into(),
Cow::Owned(git_path::into_bstr(PathBuf::from(value)).into_owned().into()),
);
} else {
return Err(Error::InvalidKeyValue {
key_id: i,
key_val: key.to_string(),
});
}
}
if config.is_empty() {
Ok(None)
} else {
resolve_includes(&mut config, None, options)?;
Ok(Some(config))
}
}
}