]>
Commit | Line | Data |
---|---|---|
0a29b90c FG |
1 | use std::collections::BTreeSet; |
2 | ||
3 | use crate::{ | |
4 | file::{init, init::Options, Metadata}, | |
5 | File, | |
6 | }; | |
7 | ||
8 | /// The error returned by [`File::from_paths_metadata()`] and [`File::from_path_no_includes()`]. | |
9 | #[derive(Debug, thiserror::Error)] | |
10 | #[allow(missing_docs)] | |
11 | pub enum Error { | |
49aad941 FG |
12 | #[error("The configuration file at \"{}\" could not be read", path.display())] |
13 | Io { | |
14 | source: std::io::Error, | |
15 | path: std::path::PathBuf, | |
16 | }, | |
0a29b90c FG |
17 | #[error(transparent)] |
18 | Init(#[from] init::Error), | |
19 | } | |
20 | ||
21 | /// Instantiation from one or more paths | |
22 | impl File<'static> { | |
23 | /// Load the single file at `path` with `source` without following include directives. | |
24 | /// | |
25 | /// Note that the path will be checked for ownership to derive trust. | |
781aab86 | 26 | pub fn from_path_no_includes(path: std::path::PathBuf, source: crate::Source) -> Result<Self, Error> { |
49aad941 FG |
27 | let trust = match gix_sec::Trust::from_path_ownership(&path) { |
28 | Ok(t) => t, | |
29 | Err(err) => return Err(Error::Io { source: err, path }), | |
30 | }; | |
0a29b90c FG |
31 | |
32 | let mut buf = Vec::new(); | |
49aad941 FG |
33 | match std::io::copy( |
34 | &mut match std::fs::File::open(&path) { | |
35 | Ok(f) => f, | |
36 | Err(err) => return Err(Error::Io { source: err, path }), | |
37 | }, | |
38 | &mut buf, | |
39 | ) { | |
40 | Ok(_) => {} | |
41 | Err(err) => return Err(Error::Io { source: err, path }), | |
42 | } | |
0a29b90c FG |
43 | |
44 | Ok(File::from_bytes_owned( | |
45 | &mut buf, | |
46 | Metadata::from(source).at(path).with(trust), | |
47 | Default::default(), | |
48 | )?) | |
49 | } | |
50 | ||
781aab86 | 51 | /// Constructs a `git-config` file from the provided metadata, which must include a path to read from or be ignored. |
0a29b90c FG |
52 | /// Returns `Ok(None)` if there was not a single input path provided, which is a possibility due to |
53 | /// [`Metadata::path`] being an `Option`. | |
54 | /// If an input path doesn't exist, the entire operation will abort. See [`from_paths_metadata_buf()`][Self::from_paths_metadata_buf()] | |
55 | /// for a more powerful version of this method. | |
56 | pub fn from_paths_metadata( | |
57 | path_meta: impl IntoIterator<Item = impl Into<Metadata>>, | |
58 | options: Options<'_>, | |
59 | ) -> Result<Option<Self>, Error> { | |
60 | let mut buf = Vec::with_capacity(512); | |
61 | let err_on_nonexisting_paths = true; | |
781aab86 FG |
62 | Self::from_paths_metadata_buf( |
63 | &mut path_meta.into_iter().map(Into::into), | |
64 | &mut buf, | |
65 | err_on_nonexisting_paths, | |
66 | options, | |
67 | ) | |
0a29b90c FG |
68 | } |
69 | ||
fe692bf9 | 70 | /// Like [`from_paths_metadata()`][Self::from_paths_metadata()], but will use `buf` to temporarily store the config file |
0a29b90c FG |
71 | /// contents for parsing instead of allocating an own buffer. |
72 | /// | |
73 | /// If `err_on_nonexisting_paths` is false, instead of aborting with error, we will continue to the next path instead. | |
74 | pub fn from_paths_metadata_buf( | |
781aab86 | 75 | path_meta: &mut dyn Iterator<Item = Metadata>, |
0a29b90c FG |
76 | buf: &mut Vec<u8>, |
77 | err_on_non_existing_paths: bool, | |
78 | options: Options<'_>, | |
79 | ) -> Result<Option<Self>, Error> { | |
80 | let mut target = None; | |
81 | let mut seen = BTreeSet::default(); | |
781aab86 | 82 | for (path, mut meta) in path_meta.filter_map(|mut meta| meta.path.take().map(|p| (p, meta))) { |
0a29b90c FG |
83 | if !seen.insert(path.clone()) { |
84 | continue; | |
85 | } | |
86 | ||
87 | buf.clear(); | |
49aad941 | 88 | match std::io::copy( |
0a29b90c FG |
89 | &mut match std::fs::File::open(&path) { |
90 | Ok(f) => f, | |
91 | Err(err) if !err_on_non_existing_paths && err.kind() == std::io::ErrorKind::NotFound => continue, | |
49aad941 FG |
92 | Err(err) => { |
93 | let err = Error::Io { source: err, path }; | |
94 | if options.ignore_io_errors { | |
781aab86 | 95 | gix_features::trace::warn!("ignoring: {err:#?}"); |
49aad941 FG |
96 | continue; |
97 | } else { | |
98 | return Err(err); | |
99 | } | |
100 | } | |
0a29b90c FG |
101 | }, |
102 | buf, | |
49aad941 FG |
103 | ) { |
104 | Ok(_) => {} | |
105 | Err(err) => { | |
106 | if options.ignore_io_errors { | |
781aab86 | 107 | gix_features::trace::warn!( |
49aad941 FG |
108 | "ignoring: {:#?}", |
109 | Error::Io { | |
110 | source: err, | |
111 | path: path.clone() | |
112 | } | |
113 | ); | |
114 | buf.clear(); | |
115 | } else { | |
116 | return Err(Error::Io { source: err, path }); | |
117 | } | |
118 | } | |
119 | }; | |
0a29b90c FG |
120 | meta.path = Some(path); |
121 | ||
122 | let config = Self::from_bytes_owned(buf, meta, options)?; | |
123 | match &mut target { | |
124 | None => { | |
125 | target = Some(config); | |
126 | } | |
127 | Some(target) => { | |
128 | target.append(config); | |
129 | } | |
130 | } | |
131 | } | |
132 | Ok(target) | |
133 | } | |
134 | } |