gix_config/file/
impls.rs

1use std::{borrow::Cow, fmt::Display, str::FromStr};
2
3use bstr::{BStr, BString, ByteVec};
4
5use crate::{
6    file::Metadata,
7    parse,
8    parse::{section, Event},
9    value::normalize,
10    File,
11};
12
13impl FromStr for File<'static> {
14    type Err = parse::Error;
15
16    fn from_str(s: &str) -> Result<Self, Self::Err> {
17        parse::Events::from_bytes_owned(s.as_bytes(), None)
18            .map(|events| File::from_parse_events_no_includes(events, Metadata::api()))
19    }
20}
21
22impl<'a> TryFrom<&'a str> for File<'a> {
23    type Error = parse::Error;
24
25    /// Convenience constructor. Attempts to parse the provided string into a
26    /// [`File`]. See [`Events::from_str()`][crate::parse::Events::from_str()] for more information.
27    fn try_from(s: &'a str) -> Result<File<'a>, Self::Error> {
28        parse::Events::from_str(s).map(|events| Self::from_parse_events_no_includes(events, Metadata::api()))
29    }
30}
31
32impl<'a> TryFrom<&'a BStr> for File<'a> {
33    type Error = parse::Error;
34
35    /// Convenience constructor. Attempts to parse the provided byte string into
36    /// a [`File`]. See [`Events::from_bytes()`][parse::Events::from_bytes()] for more information.
37    fn try_from(value: &'a BStr) -> Result<File<'a>, Self::Error> {
38        parse::Events::from_bytes(value, None)
39            .map(|events| Self::from_parse_events_no_includes(events, Metadata::api()))
40    }
41}
42
43impl From<File<'_>> for BString {
44    fn from(c: File<'_>) -> Self {
45        c.to_bstring()
46    }
47}
48
49impl Display for File<'_> {
50    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
51        Display::fmt(&self.to_bstring(), f)
52    }
53}
54
55impl PartialEq for File<'_> {
56    fn eq(&self, other: &Self) -> bool {
57        fn find_key<'a>(mut it: impl Iterator<Item = &'a Event<'a>>) -> Option<&'a section::ValueName<'a>> {
58            it.find_map(|e| match e {
59                Event::SectionValueName(k) => Some(k),
60                _ => None,
61            })
62        }
63        fn collect_value<'a>(it: impl Iterator<Item = &'a Event<'a>>) -> Cow<'a, BStr> {
64            let mut partial_value = BString::default();
65            let mut value = None;
66
67            for event in it {
68                match event {
69                    Event::SectionValueName(_) => break,
70                    Event::Value(v) => {
71                        value = v.clone().into();
72                        break;
73                    }
74                    Event::ValueNotDone(v) => partial_value.push_str(v.as_ref()),
75                    Event::ValueDone(v) => {
76                        partial_value.push_str(v.as_ref());
77                        value = Some(partial_value.into());
78                        break;
79                    }
80                    _ => (),
81                }
82            }
83            value.map(normalize).unwrap_or_default()
84        }
85        if self.section_order.len() != other.section_order.len() {
86            return false;
87        }
88
89        for (lhs, rhs) in self
90            .section_order
91            .iter()
92            .zip(&other.section_order)
93            .map(|(lhs, rhs)| (&self.sections[lhs], &other.sections[rhs]))
94        {
95            if !(lhs.header.name == rhs.header.name && lhs.header.subsection_name == rhs.header.subsection_name) {
96                return false;
97            }
98
99            let (mut lhs, mut rhs) = (lhs.body.0.iter(), rhs.body.0.iter());
100            while let (Some(lhs_key), Some(rhs_key)) = (find_key(&mut lhs), find_key(&mut rhs)) {
101                if lhs_key != rhs_key {
102                    return false;
103                }
104                if collect_value(&mut lhs) != collect_value(&mut rhs) {
105                    return false;
106                }
107            }
108        }
109        true
110    }
111}