azul_css/props/style/
azul_exclusion.rs1use std::num::ParseFloatError;
7
8#[cfg(feature = "parser")]
9use crate::macros::*;
10use crate::{
11 corety::AzString,
12 format_rust_code::FormatAsRustCode,
13 props::{
14 basic::{length::parse_float_value, FloatValue},
15 formatter::{FormatAsCssValue, PrintAsCssValue},
16 },
17};
18
19#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
31#[repr(C)]
32pub struct StyleExclusionMargin {
33 pub inner: FloatValue,
34}
35
36impl Default for StyleExclusionMargin {
37 fn default() -> Self {
38 Self {
39 inner: FloatValue::const_new(0),
40 }
41 }
42}
43
44impl StyleExclusionMargin {
45 pub fn is_initial(&self) -> bool {
46 self.inner.number == 0
47 }
48}
49
50impl PrintAsCssValue for StyleExclusionMargin {
51 fn print_as_css_value(&self) -> String {
52 format!("{}", self.inner.get())
53 }
54}
55
56impl FormatAsCssValue for StyleExclusionMargin {
57 fn format_as_css_value(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
58 write!(f, "{}", self.inner.get())
59 }
60}
61
62impl FormatAsRustCode for StyleExclusionMargin {
63 fn format_as_rust_code(&self, _tabs: usize) -> String {
64 format!(
65 "StyleExclusionMargin {{ inner: FloatValue::const_new({}) }}",
66 self.inner.get()
67 )
68 }
69}
70
71#[cfg(feature = "parser")]
72#[derive(Clone, PartialEq)]
73pub enum StyleExclusionMarginParseError {
74 FloatValue(ParseFloatError),
75}
76
77#[cfg(feature = "parser")]
78impl_debug_as_display!(StyleExclusionMarginParseError);
79
80#[cfg(feature = "parser")]
81impl_display! { StyleExclusionMarginParseError, {
82 FloatValue(e) => format!("Invalid -azul-exclusion-margin value: {}", e),
83}}
84
85#[cfg(feature = "parser")]
86impl_from!(ParseFloatError, StyleExclusionMarginParseError::FloatValue);
87
88#[cfg(feature = "parser")]
89#[derive(Debug, Clone, PartialEq)]
90#[repr(C, u8)]
91pub enum StyleExclusionMarginParseErrorOwned {
92 FloatValue(AzString),
93}
94
95#[cfg(feature = "parser")]
96impl StyleExclusionMarginParseError {
97 pub fn to_contained(&self) -> StyleExclusionMarginParseErrorOwned {
98 match self {
99 Self::FloatValue(e) => {
100 StyleExclusionMarginParseErrorOwned::FloatValue(format!("{}", e).into())
101 }
102 }
103 }
104}
105
106#[cfg(feature = "parser")]
107impl StyleExclusionMarginParseErrorOwned {
108 pub fn to_shared(&self) -> StyleExclusionMarginParseError {
109 match self {
110 Self::FloatValue(_) => {
111 StyleExclusionMarginParseError::FloatValue("".parse::<f32>().unwrap_err())
114 }
115 }
116 }
117}
118
119#[cfg(feature = "parser")]
120pub fn parse_style_exclusion_margin(
121 input: &str,
122) -> Result<StyleExclusionMargin, StyleExclusionMarginParseError> {
123 parse_float_value(input)
124 .map(|inner| StyleExclusionMargin { inner })
125 .map_err(StyleExclusionMarginParseError::FloatValue)
126}
127
128#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
140#[repr(C)]
141pub struct StyleHyphenationLanguage {
142 pub inner: AzString,
143}
144
145impl Default for StyleHyphenationLanguage {
146 fn default() -> Self {
147 Self {
148 inner: AzString::from_const_str("en-US"),
149 }
150 }
151}
152
153impl StyleHyphenationLanguage {
154 pub fn is_initial(&self) -> bool {
155 self.inner.as_str() == "en-US"
156 }
157}
158
159impl PrintAsCssValue for StyleHyphenationLanguage {
160 fn print_as_css_value(&self) -> String {
161 format!("\"{}\"", self.inner.as_str())
162 }
163}
164
165impl FormatAsCssValue for StyleHyphenationLanguage {
166 fn format_as_css_value(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
167 write!(f, "\"{}\"", self.inner.as_str())
168 }
169}
170
171impl FormatAsRustCode for StyleHyphenationLanguage {
172 fn format_as_rust_code(&self, _tabs: usize) -> String {
173 format!(
174 "StyleHyphenationLanguage {{ inner: AzString::from_const_str(\"{}\") }}",
175 self.inner.as_str()
176 )
177 }
178}
179
180#[cfg(feature = "parser")]
181#[derive(Clone, PartialEq)]
182pub enum StyleHyphenationLanguageParseError {
183 InvalidString(String),
184}
185
186#[cfg(feature = "parser")]
187impl_debug_as_display!(StyleHyphenationLanguageParseError);
188
189#[cfg(feature = "parser")]
190impl_display! { StyleHyphenationLanguageParseError, {
191 InvalidString(e) => format!("Invalid -azul-hyphenation-language value: {}", e),
192}}
193
194#[cfg(feature = "parser")]
195#[derive(Debug, Clone, PartialEq)]
196#[repr(C, u8)]
197pub enum StyleHyphenationLanguageParseErrorOwned {
198 InvalidString(AzString),
199}
200
201#[cfg(feature = "parser")]
202impl StyleHyphenationLanguageParseError {
203 pub fn to_contained(&self) -> StyleHyphenationLanguageParseErrorOwned {
204 match self {
205 Self::InvalidString(e) => {
206 StyleHyphenationLanguageParseErrorOwned::InvalidString(e.clone().into())
207 }
208 }
209 }
210}
211
212#[cfg(feature = "parser")]
213impl StyleHyphenationLanguageParseErrorOwned {
214 pub fn to_shared(&self) -> StyleHyphenationLanguageParseError {
215 match self {
216 Self::InvalidString(e) => StyleHyphenationLanguageParseError::InvalidString(e.to_string()),
217 }
218 }
219}
220
221#[cfg(feature = "parser")]
222pub fn parse_style_hyphenation_language(
223 input: &str,
224) -> Result<StyleHyphenationLanguage, StyleHyphenationLanguageParseError> {
225 let trimmed = input.trim();
227 let unquoted = if (trimmed.starts_with('"') && trimmed.ends_with('"'))
228 || (trimmed.starts_with('\'') && trimmed.ends_with('\''))
229 {
230 &trimmed[1..trimmed.len() - 1]
231 } else {
232 trimmed
233 };
234
235 if unquoted.is_empty()
237 || !unquoted.bytes().all(|b| b.is_ascii_alphanumeric() || b == b'-')
238 || unquoted.starts_with('-')
239 || unquoted.ends_with('-')
240 {
241 return Err(StyleHyphenationLanguageParseError::InvalidString(
242 unquoted.to_string(),
243 ));
244 }
245
246 Ok(StyleHyphenationLanguage {
247 inner: AzString::from_string(unquoted.to_string()),
248 })
249}
250
251#[cfg(test)]
252mod tests {
253 use super::*;
254
255 #[test]
256 fn test_parse_exclusion_margin() {
257 let margin = parse_style_exclusion_margin("10.5").unwrap();
258 assert_eq!(margin.inner.get(), 10.5);
259
260 let margin = parse_style_exclusion_margin("0").unwrap();
261 assert_eq!(margin.inner.get(), 0.0);
262 }
263
264 #[test]
265 fn test_parse_hyphenation_language() {
266 let lang = parse_style_hyphenation_language("\"en-US\"").unwrap();
267 assert_eq!(lang.inner.as_str(), "en-US");
268
269 let lang = parse_style_hyphenation_language("'de-DE'").unwrap();
270 assert_eq!(lang.inner.as_str(), "de-DE");
271
272 let lang = parse_style_hyphenation_language("fr-FR").unwrap();
273 assert_eq!(lang.inner.as_str(), "fr-FR");
274
275 let lang = parse_style_hyphenation_language("zh").unwrap();
276 assert_eq!(lang.inner.as_str(), "zh");
277
278 let lang = parse_style_hyphenation_language("sr-Latn-RS").unwrap();
279 assert_eq!(lang.inner.as_str(), "sr-Latn-RS");
280
281 let lang = parse_style_hyphenation_language("en--US").unwrap();
283 assert_eq!(lang.inner.as_str(), "en--US");
284 }
285
286 #[test]
287 fn test_parse_hyphenation_language_invalid() {
288 assert!(matches!(
289 parse_style_hyphenation_language(""),
290 Err(StyleHyphenationLanguageParseError::InvalidString(_))
291 ));
292 assert!(matches!(
293 parse_style_hyphenation_language("-en"),
294 Err(StyleHyphenationLanguageParseError::InvalidString(_))
295 ));
296 assert!(matches!(
297 parse_style_hyphenation_language("en-"),
298 Err(StyleHyphenationLanguageParseError::InvalidString(_))
299 ));
300 assert!(matches!(
301 parse_style_hyphenation_language("en_US"),
302 Err(StyleHyphenationLanguageParseError::InvalidString(_))
303 ));
304 assert!(matches!(
305 parse_style_hyphenation_language("日本語"),
306 Err(StyleHyphenationLanguageParseError::InvalidString(_))
307 ));
308 }
309
310 #[test]
311 fn test_exclusion_margin_default() {
312 let margin = StyleExclusionMargin::default();
313 assert_eq!(margin.inner.get(), 0.0);
314 assert!(margin.is_initial());
315 }
316
317 #[test]
318 fn test_hyphenation_language_default() {
319 let lang = StyleHyphenationLanguage::default();
320 assert_eq!(lang.inner.as_str(), "en-US");
321 }
322}