1use alloc::string::{String, ToString};
8
9use crate::props::formatter::PrintAsCssValue;
10
11#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
15#[repr(C)]
16#[derive(Default)]
17pub enum PageBreak {
18 #[default]
19 Auto,
20 Avoid,
21 Always,
22 All,
23 Page,
24 AvoidPage,
25 Left,
26 Right,
27 Recto,
28 Verso,
29 Column,
30 AvoidColumn,
31}
32
33
34impl PrintAsCssValue for PageBreak {
35 fn print_as_css_value(&self) -> String {
36 String::from(match self {
37 Self::Auto => "auto",
38 Self::Avoid => "avoid",
39 Self::Always => "always",
40 Self::All => "all",
41 Self::Page => "page",
42 Self::AvoidPage => "avoid-page",
43 Self::Left => "left",
44 Self::Right => "right",
45 Self::Recto => "recto",
46 Self::Verso => "verso",
47 Self::Column => "column",
48 Self::AvoidColumn => "avoid-column",
49 })
50 }
51}
52
53#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
57#[repr(C)]
58#[derive(Default)]
59pub enum BreakInside {
60 #[default]
61 Auto,
62 Avoid,
63 AvoidPage,
64 AvoidColumn,
65}
66
67
68impl PrintAsCssValue for BreakInside {
69 fn print_as_css_value(&self) -> String {
70 String::from(match self {
71 Self::Auto => "auto",
72 Self::Avoid => "avoid",
73 Self::AvoidPage => "avoid-page",
74 Self::AvoidColumn => "avoid-column",
75 })
76 }
77}
78
79#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
84#[repr(C)]
85pub struct Widows {
86 pub inner: u32,
87}
88
89impl Default for Widows {
90 fn default() -> Self {
91 Self { inner: 2 }
92 }
93}
94
95impl PrintAsCssValue for Widows {
96 fn print_as_css_value(&self) -> String {
97 self.inner.to_string()
98 }
99}
100
101#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
104#[repr(C)]
105pub struct Orphans {
106 pub inner: u32,
107}
108
109impl Default for Orphans {
110 fn default() -> Self {
111 Self { inner: 2 }
112 }
113}
114
115impl PrintAsCssValue for Orphans {
116 fn print_as_css_value(&self) -> String {
117 self.inner.to_string()
118 }
119}
120
121#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
125#[repr(C)]
126#[derive(Default)]
127pub enum BoxDecorationBreak {
128 #[default]
129 Slice,
130 Clone,
131}
132
133
134impl PrintAsCssValue for BoxDecorationBreak {
135 fn print_as_css_value(&self) -> String {
136 String::from(match self {
137 Self::Slice => "slice",
138 Self::Clone => "clone",
139 })
140 }
141}
142
143impl crate::format_rust_code::FormatAsRustCode for PageBreak {
145 fn format_as_rust_code(&self, _tabs: usize) -> String {
146 match self {
147 PageBreak::Auto => String::from("PageBreak::Auto"),
148 PageBreak::Avoid => String::from("PageBreak::Avoid"),
149 PageBreak::Always => String::from("PageBreak::Always"),
150 PageBreak::All => String::from("PageBreak::All"),
151 PageBreak::Page => String::from("PageBreak::Page"),
152 PageBreak::AvoidPage => String::from("PageBreak::AvoidPage"),
153 PageBreak::Left => String::from("PageBreak::Left"),
154 PageBreak::Right => String::from("PageBreak::Right"),
155 PageBreak::Recto => String::from("PageBreak::Recto"),
156 PageBreak::Verso => String::from("PageBreak::Verso"),
157 PageBreak::Column => String::from("PageBreak::Column"),
158 PageBreak::AvoidColumn => String::from("PageBreak::AvoidColumn"),
159 }
160 }
161}
162
163impl crate::format_rust_code::FormatAsRustCode for BreakInside {
164 fn format_as_rust_code(&self, _tabs: usize) -> String {
165 match self {
166 BreakInside::Auto => String::from("BreakInside::Auto"),
167 BreakInside::Avoid => String::from("BreakInside::Avoid"),
168 BreakInside::AvoidPage => String::from("BreakInside::AvoidPage"),
169 BreakInside::AvoidColumn => String::from("BreakInside::AvoidColumn"),
170 }
171 }
172}
173
174impl crate::format_rust_code::FormatAsRustCode for Widows {
175 fn format_as_rust_code(&self, _tabs: usize) -> String {
176 format!("Widows {{ inner: {} }}", self.inner)
177 }
178}
179
180impl crate::format_rust_code::FormatAsRustCode for Orphans {
181 fn format_as_rust_code(&self, _tabs: usize) -> String {
182 format!("Orphans {{ inner: {} }}", self.inner)
183 }
184}
185
186impl crate::format_rust_code::FormatAsRustCode for BoxDecorationBreak {
187 fn format_as_rust_code(&self, _tabs: usize) -> String {
188 match self {
189 BoxDecorationBreak::Slice => String::from("BoxDecorationBreak::Slice"),
190 BoxDecorationBreak::Clone => String::from("BoxDecorationBreak::Clone"),
191 }
192 }
193}
194
195#[cfg(feature = "parser")]
198pub mod parser {
199 use super::*;
200 use core::num::ParseIntError;
201 use crate::corety::AzString;
202 use crate::props::layout::position::ParseIntErrorWithInput;
203
204 #[derive(Clone, PartialEq)]
208 pub enum PageBreakParseError<'a> {
209 InvalidValue(&'a str),
210 }
211
212 impl_debug_as_display!(PageBreakParseError<'a>);
213 impl_display! { PageBreakParseError<'a>, {
214 InvalidValue(v) => format!("Invalid break value: \"{}\"", v),
215 }}
216
217 #[derive(Debug, Clone, PartialEq)]
219 #[repr(C, u8)]
220 pub enum PageBreakParseErrorOwned {
221 InvalidValue(AzString),
222 }
223
224 impl<'a> PageBreakParseError<'a> {
225 pub fn to_contained(&self) -> PageBreakParseErrorOwned {
226 match self {
227 Self::InvalidValue(s) => PageBreakParseErrorOwned::InvalidValue(s.to_string().into()),
228 }
229 }
230 }
231
232 impl PageBreakParseErrorOwned {
233 pub fn to_shared<'a>(&'a self) -> PageBreakParseError<'a> {
234 match self {
235 Self::InvalidValue(s) => PageBreakParseError::InvalidValue(s.as_str()),
236 }
237 }
238 }
239
240 pub fn parse_page_break<'a>(input: &'a str) -> Result<PageBreak, PageBreakParseError<'a>> {
241 match input.trim() {
242 "auto" => Ok(PageBreak::Auto),
243 "avoid" => Ok(PageBreak::Avoid),
244 "always" => Ok(PageBreak::Always),
245 "all" => Ok(PageBreak::All),
246 "page" => Ok(PageBreak::Page),
247 "avoid-page" => Ok(PageBreak::AvoidPage),
248 "left" => Ok(PageBreak::Left),
249 "right" => Ok(PageBreak::Right),
250 "recto" => Ok(PageBreak::Recto),
251 "verso" => Ok(PageBreak::Verso),
252 "column" => Ok(PageBreak::Column),
253 "avoid-column" => Ok(PageBreak::AvoidColumn),
254 _ => Err(PageBreakParseError::InvalidValue(input)),
255 }
256 }
257
258 #[derive(Clone, PartialEq)]
262 pub enum BreakInsideParseError<'a> {
263 InvalidValue(&'a str),
264 }
265
266 impl_debug_as_display!(BreakInsideParseError<'a>);
267 impl_display! { BreakInsideParseError<'a>, {
268 InvalidValue(v) => format!("Invalid break-inside value: \"{}\"", v),
269 }}
270
271 #[derive(Debug, Clone, PartialEq)]
273 #[repr(C, u8)]
274 pub enum BreakInsideParseErrorOwned {
275 InvalidValue(AzString),
276 }
277
278 impl<'a> BreakInsideParseError<'a> {
279 pub fn to_contained(&self) -> BreakInsideParseErrorOwned {
280 match self {
281 Self::InvalidValue(s) => BreakInsideParseErrorOwned::InvalidValue(s.to_string().into()),
282 }
283 }
284 }
285
286 impl BreakInsideParseErrorOwned {
287 pub fn to_shared<'a>(&'a self) -> BreakInsideParseError<'a> {
288 match self {
289 Self::InvalidValue(s) => BreakInsideParseError::InvalidValue(s.as_str()),
290 }
291 }
292 }
293
294 pub fn parse_break_inside<'a>(
295 input: &'a str,
296 ) -> Result<BreakInside, BreakInsideParseError<'a>> {
297 match input.trim() {
298 "auto" => Ok(BreakInside::Auto),
299 "avoid" => Ok(BreakInside::Avoid),
300 "avoid-page" => Ok(BreakInside::AvoidPage),
301 "avoid-column" => Ok(BreakInside::AvoidColumn),
302 _ => Err(BreakInsideParseError::InvalidValue(input)),
303 }
304 }
305
306 macro_rules! define_widow_orphan_parser {
309 ($fn_name:ident, $struct_name:ident, $error_name:ident, $error_owned_name:ident, $prop_name:expr) => {
310 #[derive(Clone, PartialEq)]
311 pub enum $error_name<'a> {
312 ParseInt(ParseIntError, &'a str),
313 ParseIntOwned(&'a str, &'a str),
314 NegativeValue(&'a str),
315 }
316
317 impl_debug_as_display!($error_name<'a>);
318 impl_display! { $error_name<'a>, {
319 ParseInt(e, s) => format!("Invalid integer for {}: \"{}\". Reason: {}", $prop_name, s, e),
320 ParseIntOwned(e, s) => format!("Invalid integer for {}: \"{}\". Reason: {}", $prop_name, s, e),
321 NegativeValue(s) => format!("Invalid value for {}: \"{}\". Value cannot be negative.", $prop_name, s),
322 }}
323
324 #[derive(Debug, Clone, PartialEq)]
325 #[repr(C, u8)]
326 pub enum $error_owned_name {
327 ParseInt(ParseIntErrorWithInput),
328 NegativeValue(AzString),
329 }
330
331 impl<'a> $error_name<'a> {
332 pub fn to_contained(&self) -> $error_owned_name {
333 match self {
334 Self::ParseInt(e, s) => $error_owned_name::ParseInt(ParseIntErrorWithInput { error: e.to_string().into(), input: s.to_string().into() }),
335 Self::ParseIntOwned(e, s) => $error_owned_name::ParseInt(ParseIntErrorWithInput { error: e.to_string().into(), input: s.to_string().into() }),
336 Self::NegativeValue(s) => $error_owned_name::NegativeValue(s.to_string().into()),
337 }
338 }
339 }
340
341 impl $error_owned_name {
342 pub fn to_shared<'a>(&'a self) -> $error_name<'a> {
343 match self {
344 Self::ParseInt(e) => $error_name::ParseIntOwned(e.error.as_str(), e.input.as_str()),
345 Self::NegativeValue(s) => $error_name::NegativeValue(s),
346 }
347 }
348 }
349
350 pub fn $fn_name<'a>(input: &'a str) -> Result<$struct_name, $error_name<'a>> {
351 let trimmed = input.trim();
352 let val: i32 = trimmed.parse().map_err(|e| $error_name::ParseInt(e, trimmed))?;
353 if val < 0 {
354 return Err($error_name::NegativeValue(trimmed));
355 }
356 Ok($struct_name { inner: val as u32 })
357 }
358 };
359 }
360
361 define_widow_orphan_parser!(
362 parse_widows,
363 Widows,
364 WidowsParseError,
365 WidowsParseErrorOwned,
366 "widows"
367 );
368 define_widow_orphan_parser!(
369 parse_orphans,
370 Orphans,
371 OrphansParseError,
372 OrphansParseErrorOwned,
373 "orphans"
374 );
375
376 #[derive(Clone, PartialEq)]
380 pub enum BoxDecorationBreakParseError<'a> {
381 InvalidValue(&'a str),
382 }
383
384 impl_debug_as_display!(BoxDecorationBreakParseError<'a>);
385 impl_display! { BoxDecorationBreakParseError<'a>, {
386 InvalidValue(v) => format!("Invalid box-decoration-break value: \"{}\"", v),
387 }}
388
389 #[derive(Debug, Clone, PartialEq)]
391 #[repr(C, u8)]
392 pub enum BoxDecorationBreakParseErrorOwned {
393 InvalidValue(AzString),
394 }
395
396 impl<'a> BoxDecorationBreakParseError<'a> {
397 pub fn to_contained(&self) -> BoxDecorationBreakParseErrorOwned {
398 match self {
399 Self::InvalidValue(s) => {
400 BoxDecorationBreakParseErrorOwned::InvalidValue(s.to_string().into())
401 }
402 }
403 }
404 }
405
406 impl BoxDecorationBreakParseErrorOwned {
407 pub fn to_shared<'a>(&'a self) -> BoxDecorationBreakParseError<'a> {
408 match self {
409 Self::InvalidValue(s) => BoxDecorationBreakParseError::InvalidValue(s.as_str()),
410 }
411 }
412 }
413
414 pub fn parse_box_decoration_break<'a>(
415 input: &'a str,
416 ) -> Result<BoxDecorationBreak, BoxDecorationBreakParseError<'a>> {
417 match input.trim() {
418 "slice" => Ok(BoxDecorationBreak::Slice),
419 "clone" => Ok(BoxDecorationBreak::Clone),
420 _ => Err(BoxDecorationBreakParseError::InvalidValue(input)),
421 }
422 }
423}
424
425#[cfg(feature = "parser")]
426pub use parser::*;
427
428#[cfg(all(test, feature = "parser"))]
429mod tests {
430 use super::*;
431
432 #[test]
433 fn test_parse_page_break() {
434 assert_eq!(parse_page_break("auto").unwrap(), PageBreak::Auto);
435 assert_eq!(parse_page_break("page").unwrap(), PageBreak::Page);
436 assert_eq!(
437 parse_page_break("avoid-column").unwrap(),
438 PageBreak::AvoidColumn
439 );
440 assert!(parse_page_break("invalid").is_err());
441 }
442
443 #[test]
444 fn test_parse_break_inside() {
445 assert_eq!(parse_break_inside("auto").unwrap(), BreakInside::Auto);
446 assert_eq!(parse_break_inside("avoid").unwrap(), BreakInside::Avoid);
447 assert!(parse_break_inside("always").is_err());
448 }
449
450 #[test]
451 fn test_parse_widows_orphans() {
452 assert_eq!(parse_widows("3").unwrap().inner, 3);
453 assert_eq!(parse_orphans(" 1 ").unwrap().inner, 1);
454 assert!(parse_widows("-2").is_err());
455 assert!(parse_orphans("auto").is_err());
456 }
457
458 #[test]
459 fn test_parse_box_decoration_break() {
460 assert_eq!(
461 parse_box_decoration_break("slice").unwrap(),
462 BoxDecorationBreak::Slice
463 );
464 assert_eq!(
465 parse_box_decoration_break("clone").unwrap(),
466 BoxDecorationBreak::Clone
467 );
468 assert!(parse_box_decoration_break("copy").is_err());
469 }
470}