Skip to main content

gix_config/file/init/
from_paths.rs

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