freya_core/
parsing.rs

1use std::str::CharIndices;
2
3use freya_native_core::prelude::OwnedAttributeView;
4
5use crate::custom_attributes::CustomAttributeValues;
6
7#[derive(Clone, Debug, PartialEq)]
8pub struct ParseError;
9
10// FromStr but we own it so we can impl it on torin and skia_safe types.
11pub trait Parse: Sized {
12    fn parse(value: &str) -> Result<Self, ParseError>;
13}
14
15pub trait ParseAttribute: Sized {
16    fn parse_attribute(
17        &mut self,
18        attr: OwnedAttributeView<CustomAttributeValues>,
19    ) -> Result<(), ParseError>;
20
21    fn parse_safe(&mut self, attr: OwnedAttributeView<CustomAttributeValues>) {
22        #[cfg(debug_assertions)]
23        {
24            let error_attr = attr.clone();
25            if self.parse_attribute(attr).is_err() {
26                panic!(
27                    "Failed to parse attribute '{:?}' with value '{:?}'",
28                    error_attr.attribute, error_attr.value
29                );
30            }
31        }
32
33        #[cfg(not(debug_assertions))]
34        self.parse_attribute(attr).ok();
35    }
36}
37
38pub trait ExtSplit {
39    fn split_excluding_group(
40        &self,
41        delimiter: char,
42        group_start: char,
43        group_end: char,
44    ) -> SplitExcludingGroup<'_>;
45    fn split_ascii_whitespace_excluding_group(
46        &self,
47        group_start: char,
48        group_end: char,
49    ) -> SplitAsciiWhitespaceExcludingGroup<'_>;
50}
51
52impl ExtSplit for str {
53    fn split_excluding_group(
54        &self,
55        delimiter: char,
56        group_start: char,
57        group_end: char,
58    ) -> SplitExcludingGroup<'_> {
59        SplitExcludingGroup {
60            text: self,
61            chars: self.char_indices(),
62            delimiter,
63            group_start,
64            group_end,
65            trailing_empty: true,
66        }
67    }
68
69    fn split_ascii_whitespace_excluding_group(
70        &self,
71        group_start: char,
72        group_end: char,
73    ) -> SplitAsciiWhitespaceExcludingGroup<'_> {
74        SplitAsciiWhitespaceExcludingGroup {
75            text: self,
76            chars: self.char_indices(),
77            group_start,
78            group_end,
79        }
80    }
81}
82
83#[derive(Clone, Debug)]
84pub struct SplitExcludingGroup<'a> {
85    pub text: &'a str,
86    pub chars: CharIndices<'a>,
87    pub delimiter: char,
88    pub group_start: char,
89    pub group_end: char,
90    pub trailing_empty: bool,
91}
92
93impl<'a> Iterator for SplitExcludingGroup<'a> {
94    type Item = &'a str;
95
96    fn next(&mut self) -> Option<&'a str> {
97        let first = self.chars.next();
98
99        let (start, mut prev) = match first {
100            None => {
101                if self.text.ends_with(self.delimiter) && self.trailing_empty {
102                    self.trailing_empty = false;
103                    return Some("");
104                }
105                return None;
106            }
107            Some((_, c)) if c == self.delimiter => return Some(""),
108            Some(v) => v,
109        };
110
111        let mut in_group = false;
112        let mut nesting = -1;
113
114        loop {
115            if prev == self.group_start {
116                if nesting == -1 {
117                    in_group = true;
118                }
119                nesting += 1;
120            } else if prev == self.group_end {
121                nesting -= 1;
122                if nesting == -1 {
123                    in_group = false;
124                }
125            }
126
127            prev = match self.chars.next() {
128                None => return Some(&self.text[start..]),
129                Some((end, c)) if c == self.delimiter && !in_group => {
130                    return Some(&self.text[start..end])
131                }
132                Some((_, c)) => c,
133            }
134        }
135    }
136}
137
138#[derive(Clone, Debug)]
139pub struct SplitAsciiWhitespaceExcludingGroup<'a> {
140    pub text: &'a str,
141    pub chars: CharIndices<'a>,
142    pub group_start: char,
143    pub group_end: char,
144}
145
146impl<'a> Iterator for SplitAsciiWhitespaceExcludingGroup<'a> {
147    type Item = &'a str;
148
149    fn next(&mut self) -> Option<&'a str> {
150        let first = self.chars.next();
151
152        let (start, mut prev) = match first {
153            None => return None,
154            Some((_, c)) if c.is_ascii_whitespace() => return self.next(),
155            Some(v) => v,
156        };
157
158        let mut in_group = false;
159        let mut nesting = -1;
160
161        loop {
162            if prev == self.group_start {
163                if nesting == -1 {
164                    in_group = true;
165                }
166                nesting += 1;
167            } else if prev == self.group_end {
168                nesting -= 1;
169                if nesting == -1 {
170                    in_group = false;
171                }
172            }
173
174            prev = match self.chars.next() {
175                None => return Some(&self.text[start..]),
176                Some((end, c)) if c.is_ascii_whitespace() && !in_group => {
177                    return Some(&self.text[start..end])
178                }
179                Some((_, c)) => c,
180            }
181        }
182    }
183}