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
10pub 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}