gix_config/file/init/
from_paths.rs

1use std::collections::BTreeSet;
2
3use 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)]
11pub enum Error {
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    },
17    #[error(transparent)]
18    Init(#[from] init::Error),
19}
20
21/// Instantiation from one or more paths
22impl 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.
26    pub fn from_path_no_includes(path: std::path::PathBuf, source: crate::Source) -> Result<Self, Error> {
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        };
31
32        let mut buf = Vec::new();
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        }
43
44        Ok(File::from_bytes_owned(
45            &mut buf,
46            Metadata::from(source).at(path).with(trust),
47            Default::default(),
48        )?)
49    }
50
51    /// Constructs a `git-config` file from the provided metadata, which must include a path to read from or be ignored.
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;
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        )
68    }
69
70    /// Like [`from_paths_metadata()`][Self::from_paths_metadata()], but will use `buf` to temporarily store the config file
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(
75        path_meta: &mut dyn Iterator<Item = Metadata>,
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();
82        for (path, mut meta) in path_meta.filter_map(|mut meta| meta.path.take().map(|p| (p, meta))) {
83            if !seen.insert(path.clone()) {
84                continue;
85            }
86
87            buf.clear();
88            match std::io::copy(
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,
92                    Err(err) => {
93                        let err = Error::Io { source: err, path };
94                        if options.ignore_io_errors {
95                            gix_features::trace::warn!("ignoring: {err:#?}");
96                            continue;
97                        } else {
98                            return Err(err);
99                        }
100                    }
101                },
102                buf,
103            ) {
104                Ok(_) => {}
105                Err(err) => {
106                    if options.ignore_io_errors {
107                        gix_features::trace::warn!(
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            };
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}