gix_config/file/access/
comfort.rs

1use std::borrow::Cow;
2
3use bstr::BStr;
4
5use crate::file::Metadata;
6use crate::{value, AsKey, File};
7
8/// Comfortable API for accessing values
9impl File<'_> {
10    /// Like [`string_by()`](File::string_by()), but suitable for statically known `key`s like `remote.origin.url`.
11    pub fn string(&self, key: impl AsKey) -> Option<Cow<'_, BStr>> {
12        self.string_filter(key, |_| true)
13    }
14
15    /// Like [`value()`](File::value()), but returning `None` if the string wasn't found.
16    ///
17    /// As strings perform no conversions, this will never fail.
18    pub fn string_by(
19        &self,
20        section_name: impl AsRef<str>,
21        subsection_name: Option<&BStr>,
22        value_name: impl AsRef<str>,
23    ) -> Option<Cow<'_, BStr>> {
24        self.string_filter_by(section_name.as_ref(), subsection_name, value_name.as_ref(), |_| true)
25    }
26
27    /// Like [`string_filter_by()`](File::string_filter_by()), but suitable for statically known `key`s like `remote.origin.url`.
28    pub fn string_filter(&self, key: impl AsKey, filter: impl FnMut(&Metadata) -> bool) -> Option<Cow<'_, BStr>> {
29        let key = key.try_as_key()?;
30        self.raw_value_filter_by(key.section_name, key.subsection_name, key.value_name, filter)
31            .ok()
32    }
33
34    /// Like [`string()`](File::string()), but the section containing the returned value must pass `filter` as well.
35    pub fn string_filter_by(
36        &self,
37        section_name: impl AsRef<str>,
38        subsection_name: Option<&BStr>,
39        value_name: impl AsRef<str>,
40        filter: impl FnMut(&Metadata) -> bool,
41    ) -> Option<Cow<'_, BStr>> {
42        self.raw_value_filter_by(section_name.as_ref(), subsection_name, value_name.as_ref(), filter)
43            .ok()
44    }
45
46    /// Like [`path_by()`](File::path_by()), but suitable for statically known `key`s like `remote.origin.url`.
47    pub fn path(&self, key: impl AsKey) -> Option<crate::Path<'_>> {
48        self.path_filter(key, |_| true)
49    }
50
51    /// Like [`value()`](File::value()), but returning `None` if the path wasn't found.
52    ///
53    /// Note that this path is not vetted and should only point to resources which can't be used
54    /// to pose a security risk. Prefer using [`path_filter()`](File::path_filter()) instead.
55    ///
56    /// As paths perform no conversions, this will never fail.
57    pub fn path_by(
58        &self,
59        section_name: impl AsRef<str>,
60        subsection_name: Option<&BStr>,
61        value_name: impl AsRef<str>,
62    ) -> Option<crate::Path<'_>> {
63        self.path_filter_by(section_name.as_ref(), subsection_name, value_name.as_ref(), |_| true)
64    }
65
66    /// Like [`path_filter_by()`](File::path_filter_by()), but suitable for statically known `key`s like `remote.origin.url`.
67    pub fn path_filter(&self, key: impl AsKey, filter: impl FnMut(&Metadata) -> bool) -> Option<crate::Path<'_>> {
68        let key = key.try_as_key()?;
69        self.path_filter_by(key.section_name, key.subsection_name, key.value_name, filter)
70    }
71
72    /// Like [`path()`](File::path()), but the section containing the returned value must pass `filter` as well.
73    ///
74    /// This should be the preferred way of accessing paths as those from untrusted
75    /// locations can be
76    ///
77    /// As paths perform no conversions, this will never fail.
78    pub fn path_filter_by(
79        &self,
80        section_name: impl AsRef<str>,
81        subsection_name: Option<&BStr>,
82        value_name: impl AsRef<str>,
83        filter: impl FnMut(&Metadata) -> bool,
84    ) -> Option<crate::Path<'_>> {
85        self.raw_value_filter_by(section_name.as_ref(), subsection_name, value_name.as_ref(), filter)
86            .ok()
87            .map(crate::Path::from)
88    }
89
90    /// Like [`boolean_by()`](File::boolean_by()), but suitable for statically known `key`s like `remote.origin.url`.
91    pub fn boolean(&self, key: impl AsKey) -> Option<Result<bool, value::Error>> {
92        self.boolean_filter(key, |_| true)
93    }
94
95    /// Like [`value()`](File::value()), but returning `None` if the boolean value wasn't found.
96    pub fn boolean_by(
97        &self,
98        section_name: impl AsRef<str>,
99        subsection_name: Option<&BStr>,
100        value_name: impl AsRef<str>,
101    ) -> Option<Result<bool, value::Error>> {
102        self.boolean_filter_by(section_name.as_ref(), subsection_name, value_name.as_ref(), |_| true)
103    }
104
105    /// Like [`boolean_filter_by()`](File::boolean_filter_by()), but suitable for statically known `key`s like `remote.origin.url`.
106    pub fn boolean_filter(
107        &self,
108        key: impl AsKey,
109        filter: impl FnMut(&Metadata) -> bool,
110    ) -> Option<Result<bool, value::Error>> {
111        let key = key.try_as_key()?;
112        self.boolean_filter_by(key.section_name, key.subsection_name, key.value_name, filter)
113    }
114
115    /// Like [`boolean_by()`](File::boolean_by()), but the section containing the returned value must pass `filter` as well.
116    pub fn boolean_filter_by(
117        &self,
118        section_name: impl AsRef<str>,
119        subsection_name: Option<&BStr>,
120        value_name: impl AsRef<str>,
121        mut filter: impl FnMut(&Metadata) -> bool,
122    ) -> Option<Result<bool, value::Error>> {
123        let section_name = section_name.as_ref();
124        let section_ids = self
125            .section_ids_by_name_and_subname(section_name, subsection_name)
126            .ok()?;
127        let key = value_name.as_ref();
128        for section_id in section_ids.rev() {
129            let section = self.sections.get(&section_id).expect("known section id");
130            if !filter(section.meta()) {
131                continue;
132            }
133            match section.value_implicit(key) {
134                Some(Some(v)) => return Some(crate::Boolean::try_from(v).map(Into::into)),
135                Some(None) => return Some(Ok(true)),
136                None => continue,
137            }
138        }
139        None
140    }
141
142    /// Like [`integer_by()`](File::integer_by()), but suitable for statically known `key`s like `remote.origin.url`.
143    pub fn integer(&self, key: impl AsKey) -> Option<Result<i64, value::Error>> {
144        self.integer_filter(key, |_| true)
145    }
146
147    /// Like [`value()`](File::value()), but returning an `Option` if the integer wasn't found.
148    pub fn integer_by(
149        &self,
150        section_name: impl AsRef<str>,
151        subsection_name: Option<&BStr>,
152        value_name: impl AsRef<str>,
153    ) -> Option<Result<i64, value::Error>> {
154        self.integer_filter_by(section_name, subsection_name, value_name, |_| true)
155    }
156
157    /// Like [`integer_filter_by()`](File::integer_filter_by()), but suitable for statically known `key`s like `remote.origin.url`.
158    pub fn integer_filter(
159        &self,
160        key: impl AsKey,
161        filter: impl FnMut(&Metadata) -> bool,
162    ) -> Option<Result<i64, value::Error>> {
163        let key = key.try_as_key()?;
164        self.integer_filter_by(key.section_name, key.subsection_name, key.value_name, filter)
165    }
166
167    /// Like [`integer_by()`](File::integer_by()), but the section containing the returned value must pass `filter` as well.
168    pub fn integer_filter_by(
169        &self,
170        section_name: impl AsRef<str>,
171        subsection_name: Option<&BStr>,
172        value_name: impl AsRef<str>,
173        filter: impl FnMut(&Metadata) -> bool,
174    ) -> Option<Result<i64, value::Error>> {
175        let int = self
176            .raw_value_filter_by(section_name.as_ref(), subsection_name, value_name.as_ref(), filter)
177            .ok()?;
178        Some(crate::Integer::try_from(int.as_ref()).and_then(|b| {
179            b.to_decimal()
180                .ok_or_else(|| value::Error::new("Integer overflow", int.into_owned()))
181        }))
182    }
183
184    /// Like [`strings_by()`](File::strings_by()), but suitable for statically known `key`s like `remote.origin.url`.
185    pub fn strings(&self, key: impl AsKey) -> Option<Vec<Cow<'_, BStr>>> {
186        let key = key.try_as_key()?;
187        self.strings_by(key.section_name, key.subsection_name, key.value_name)
188    }
189
190    /// Similar to [`values_by(…)`](File::values_by()) but returning strings if at least one of them was found.
191    pub fn strings_by(
192        &self,
193        section_name: impl AsRef<str>,
194        subsection_name: Option<&BStr>,
195        value_name: impl AsRef<str>,
196    ) -> Option<Vec<Cow<'_, BStr>>> {
197        self.raw_values_by(section_name.as_ref(), subsection_name, value_name.as_ref())
198            .ok()
199    }
200
201    /// Like [`strings_filter_by()`](File::strings_filter_by()), but suitable for statically known `key`s like `remote.origin.url`.
202    pub fn strings_filter(&self, key: impl AsKey, filter: impl FnMut(&Metadata) -> bool) -> Option<Vec<Cow<'_, BStr>>> {
203        let key = key.try_as_key()?;
204        self.strings_filter_by(key.section_name, key.subsection_name, key.value_name, filter)
205    }
206
207    /// Similar to [`strings_by(…)`](File::strings_by()), but all values are in sections that passed `filter`.
208    pub fn strings_filter_by(
209        &self,
210        section_name: impl AsRef<str>,
211        subsection_name: Option<&BStr>,
212        value_name: impl AsRef<str>,
213        filter: impl FnMut(&Metadata) -> bool,
214    ) -> Option<Vec<Cow<'_, BStr>>> {
215        self.raw_values_filter_by(section_name.as_ref(), subsection_name, value_name.as_ref(), filter)
216            .ok()
217    }
218
219    /// Like [`integers()`](File::integers()), but suitable for statically known `key`s like `remote.origin.url`.
220    pub fn integers(&self, key: impl AsKey) -> Option<Result<Vec<i64>, value::Error>> {
221        self.integers_filter(key, |_| true)
222    }
223
224    /// Similar to [`values_by(…)`](File::values_by()) but returning integers if at least one of them was found
225    /// and if none of them overflows.
226    pub fn integers_by(
227        &self,
228        section_name: impl AsRef<str>,
229        subsection_name: Option<&BStr>,
230        value_name: impl AsRef<str>,
231    ) -> Option<Result<Vec<i64>, value::Error>> {
232        self.integers_filter_by(section_name.as_ref(), subsection_name, value_name.as_ref(), |_| true)
233    }
234
235    /// Like [`integers_filter_by()`](File::integers_filter_by()), but suitable for statically known `key`s like `remote.origin.url`.
236    pub fn integers_filter(
237        &self,
238        key: impl AsKey,
239        filter: impl FnMut(&Metadata) -> bool,
240    ) -> Option<Result<Vec<i64>, value::Error>> {
241        let key = key.try_as_key()?;
242        self.integers_filter_by(key.section_name, key.subsection_name, key.value_name, filter)
243    }
244
245    /// Similar to [`integers_by(…)`](File::integers_by()) but all integers are in sections that passed `filter`
246    /// and that are not overflowing.
247    pub fn integers_filter_by(
248        &self,
249        section_name: impl AsRef<str>,
250        subsection_name: Option<&BStr>,
251        value_name: impl AsRef<str>,
252        filter: impl FnMut(&Metadata) -> bool,
253    ) -> Option<Result<Vec<i64>, value::Error>> {
254        self.raw_values_filter_by(section_name.as_ref(), subsection_name, value_name.as_ref(), filter)
255            .ok()
256            .map(|values| {
257                values
258                    .into_iter()
259                    .map(|v| {
260                        crate::Integer::try_from(v.as_ref()).and_then(|int| {
261                            int.to_decimal()
262                                .ok_or_else(|| value::Error::new("Integer overflow", v.into_owned()))
263                        })
264                    })
265                    .collect()
266            })
267    }
268}