]>
Commit | Line | Data |
---|---|---|
0a29b90c FG |
1 | use std::{borrow::Cow, convert::TryFrom, fmt::Display, str::FromStr}; |
2 | ||
3 | use bstr::{BStr, BString, ByteVec}; | |
4 | ||
5 | use crate::{ | |
6 | file::Metadata, | |
7 | parse, | |
8 | parse::{section, Event}, | |
9 | value::normalize, | |
10 | File, | |
11 | }; | |
12 | ||
13 | impl 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 | ||
22 | impl<'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 | ||
32 | impl<'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 | ||
43 | impl From<File<'_>> for BString { | |
44 | fn from(c: File<'_>) -> Self { | |
45 | c.into() | |
46 | } | |
47 | } | |
48 | ||
49 | impl 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 | ||
55 | impl 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::Key<'a>> { | |
58 | it.find_map(|e| match e { | |
59 | Event::SectionKey(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::SectionKey(_) => 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 | } |