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
use std::collections::BTreeSet;
use gix_error::ExnMessageResult;
use crate::{
File,
file::{Metadata, init::Options},
};
/// Instantiation from one or more paths
impl File {
/// Load the single file at `path` with `source` without following include directives.
///
/// Note that the path will be checked for ownership to derive trust.
pub fn from_path_no_includes(path: std::path::PathBuf, source: crate::Source) -> ExnMessageResult<Self> {
use gix_error::{ResultExt, message};
let trust = gix_sec::Trust::from_path_ownership(&path).or_raise(|| {
message!(
"The configuration file at \"{}\" could not be inspected",
path.display()
)
})?;
let mut buf = Vec::new();
let mut file = std::fs::File::open(&path)
.or_raise(|| message!("The configuration file at \"{}\" could not be read", path.display()))?;
std::io::copy(&mut file, &mut buf)
.or_raise(|| message!("The configuration file at \"{}\" could not be read", path.display()))?;
File::from_bytes_owned(
&mut buf,
Metadata::from(source).at(path).with(trust),
Default::default(),
)
.or_raise(|| message("Could not initialize configuration from a path"))
}
/// Constructs a `git-config` file from the provided metadata, which must include a path to read from or be ignored.
/// Returns `Ok(None)` if there was not a single input path provided, which is a possibility due to
/// [`Metadata::path`] being an `Option`.
/// If an input path doesn't exist, the entire operation will abort. See [`from_paths_metadata_buf()`][Self::from_paths_metadata_buf()]
/// for a more powerful version of this method.
pub fn from_paths_metadata(
path_meta: impl IntoIterator<Item = impl Into<Metadata>>,
options: Options<'_>,
) -> ExnMessageResult<Option<Self>> {
let mut buf = Vec::with_capacity(512);
let err_on_nonexisting_paths = true;
Self::from_paths_metadata_buf(
&mut path_meta.into_iter().map(Into::into),
&mut buf,
err_on_nonexisting_paths,
options,
)
}
/// Like [`from_paths_metadata()`][Self::from_paths_metadata()], but will use `buf` to temporarily store the config file
/// contents for parsing instead of allocating an own buffer.
///
/// If `err_on_nonexisting_paths` is false, instead of aborting with error, we will continue to the next path instead.
pub fn from_paths_metadata_buf(
path_meta: &mut dyn Iterator<Item = Metadata>,
buf: &mut Vec<u8>,
err_on_non_existing_paths: bool,
options: Options<'_>,
) -> ExnMessageResult<Option<Self>> {
use gix_error::{ErrorExt, ResultExt, message};
let mut target = None;
let mut seen = BTreeSet::default();
for (path, mut meta) in path_meta.filter_map(|mut meta| meta.path.take().map(|p| (p, meta))) {
if !seen.insert(path.clone()) {
continue;
}
buf.clear();
match std::io::copy(
&mut match std::fs::File::open(&path) {
Ok(f) => f,
Err(err) if !err_on_non_existing_paths && err.kind() == std::io::ErrorKind::NotFound => continue,
Err(err) => {
let err = err.and_raise(message!(
"The configuration file at \"{}\" could not be read",
path.display()
));
if options.ignore_io_errors {
gix_features::trace::warn!("ignoring: {err:#?}");
continue;
} else {
return Err(err);
}
}
},
buf,
) {
Ok(_) => {}
Err(err) => {
let err = err.and_raise(message!(
"The configuration file at \"{}\" could not be read",
path.display()
));
if options.ignore_io_errors {
gix_features::trace::warn!("ignoring: {err:#?}");
buf.clear();
} else {
return Err(err);
}
}
}
meta.path = Some(path);
let config = Self::from_bytes_owned(buf, meta, options)
.or_raise(|| message("Could not initialize configuration from a path"))?;
match &mut target {
None => {
target = Some(config);
}
Some(target) => {
target
.append(config)
.or_raise(|| message("Could not append configuration from a path"))?;
}
}
}
Ok(target)
}
}