Skip to main content

gix_config/file/
impls.rs

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