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 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 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 {}