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::codegen::format::FormatAsRustCode for PageBreak {
145 fn format_as_rust_code(&self, _tabs: usize) -> String {
146 match self {
147 Self::Auto => String::from("PageBreak::Auto"),
148 Self::Avoid => String::from("PageBreak::Avoid"),
149 Self::Always => String::from("PageBreak::Always"),
150 Self::All => String::from("PageBreak::All"),
151 Self::Page => String::from("PageBreak::Page"),
152 Self::AvoidPage => String::from("PageBreak::AvoidPage"),
153 Self::Left => String::from("PageBreak::Left"),
154 Self::Right => String::from("PageBreak::Right"),
155 Self::Recto => String::from("PageBreak::Recto"),
156 Self::Verso => String::from("PageBreak::Verso"),
157 Self::Column => String::from("PageBreak::Column"),
158 Self::AvoidColumn => String::from("PageBreak::AvoidColumn"),
159 }
160 }
161}
162
163impl crate::codegen::format::FormatAsRustCode for BreakInside {
164 fn format_as_rust_code(&self, _tabs: usize) -> String {
165 match self {
166 Self::Auto => String::from("BreakInside::Auto"),
167 Self::Avoid => String::from("BreakInside::Avoid"),
168 Self::AvoidPage => String::from("BreakInside::AvoidPage"),
169 Self::AvoidColumn => String::from("BreakInside::AvoidColumn"),
170 }
171 }
172}
173
174impl crate::codegen::format::FormatAsRustCode for Widows {
175 fn format_as_rust_code(&self, _tabs: usize) -> String {
176 format!("Widows {{ inner: {} }}", self.inner)
177 }
178}
179
180impl crate::codegen::format::FormatAsRustCode for Orphans {
181 fn format_as_rust_code(&self, _tabs: usize) -> String {
182 format!("Orphans {{ inner: {} }}", self.inner)
183 }
184}
185
186impl crate::codegen::format::FormatAsRustCode for BoxDecorationBreak {
187 fn format_as_rust_code(&self, _tabs: usize) -> String {
188 match self {
189 Self::Slice => String::from("BoxDecorationBreak::Slice"),
190 Self::Clone => String::from("BoxDecorationBreak::Clone"),
191 }
192 }
193}
194
195#[cfg(feature = "parser")]
198pub mod parser {
199 #[allow(clippy::wildcard_imports)] use super::*;
201 use core::num::ParseIntError;
202 use crate::corety::AzString;
203 use crate::props::layout::position::ParseIntErrorWithInput;
204
205 #[derive(Clone, PartialEq, Eq)]
209 pub enum PageBreakParseError<'a> {
210 InvalidValue(&'a str),
211 }
212
213 impl_debug_as_display!(PageBreakParseError<'a>);
214 impl_display! { PageBreakParseError<'a>, {
215 InvalidValue(v) => format!("Invalid break value: \"{}\"", v),
216 }}
217
218 #[derive(Debug, Clone, PartialEq, Eq)]
220 #[repr(C, u8)]
221 pub enum PageBreakParseErrorOwned {
222 InvalidValue(AzString),
223 }
224
225 impl PageBreakParseError<'_> {
226 #[must_use] pub fn to_contained(&self) -> PageBreakParseErrorOwned {
227 match self {
228 Self::InvalidValue(s) => PageBreakParseErrorOwned::InvalidValue((*s).to_string().into()),
229 }
230 }
231 }
232
233 impl PageBreakParseErrorOwned {
234 #[must_use] pub fn to_shared(&self) -> PageBreakParseError<'_> {
235 match self {
236 Self::InvalidValue(s) => PageBreakParseError::InvalidValue(s.as_str()),
237 }
238 }
239 }
240
241 pub fn parse_page_break(input: &str) -> Result<PageBreak, PageBreakParseError<'_>> {
245 match input.trim() {
246 "auto" => Ok(PageBreak::Auto),
247 "avoid" => Ok(PageBreak::Avoid),
248 "always" => Ok(PageBreak::Always),
249 "all" => Ok(PageBreak::All),
250 "page" => Ok(PageBreak::Page),
251 "avoid-page" => Ok(PageBreak::AvoidPage),
252 "left" => Ok(PageBreak::Left),
253 "right" => Ok(PageBreak::Right),
254 "recto" => Ok(PageBreak::Recto),
255 "verso" => Ok(PageBreak::Verso),
256 "column" => Ok(PageBreak::Column),
257 "avoid-column" => Ok(PageBreak::AvoidColumn),
258 _ => Err(PageBreakParseError::InvalidValue(input)),
259 }
260 }
261
262 #[derive(Clone, PartialEq, Eq)]
266 pub enum BreakInsideParseError<'a> {
267 InvalidValue(&'a str),
268 }
269
270 impl_debug_as_display!(BreakInsideParseError<'a>);
271 impl_display! { BreakInsideParseError<'a>, {
272 InvalidValue(v) => format!("Invalid break-inside value: \"{}\"", v),
273 }}
274
275 #[derive(Debug, Clone, PartialEq, Eq)]
277 #[repr(C, u8)]
278 pub enum BreakInsideParseErrorOwned {
279 InvalidValue(AzString),
280 }
281
282 impl BreakInsideParseError<'_> {
283 #[must_use] pub fn to_contained(&self) -> BreakInsideParseErrorOwned {
284 match self {
285 Self::InvalidValue(s) => BreakInsideParseErrorOwned::InvalidValue((*s).to_string().into()),
286 }
287 }
288 }
289
290 impl BreakInsideParseErrorOwned {
291 #[must_use] pub fn to_shared(&self) -> BreakInsideParseError<'_> {
292 match self {
293 Self::InvalidValue(s) => BreakInsideParseError::InvalidValue(s.as_str()),
294 }
295 }
296 }
297
298 pub fn parse_break_inside(
302 input: &str,
303 ) -> Result<BreakInside, BreakInsideParseError<'_>> {
304 match input.trim() {
305 "auto" => Ok(BreakInside::Auto),
306 "avoid" => Ok(BreakInside::Avoid),
307 "avoid-page" => Ok(BreakInside::AvoidPage),
308 "avoid-column" => Ok(BreakInside::AvoidColumn),
309 _ => Err(BreakInsideParseError::InvalidValue(input)),
310 }
311 }
312
313 macro_rules! define_widow_orphan_parser {
316 ($fn_name:ident, $struct_name:ident, $error_name:ident, $error_owned_name:ident, $prop_name:expr) => {
317 #[derive(Clone, PartialEq, Eq)]
318 pub enum $error_name<'a> {
319 ParseInt(ParseIntError, &'a str),
320 ParseIntOwned(&'a str, &'a str),
321 NegativeValue(&'a str),
322 }
323
324 impl_debug_as_display!($error_name<'a>);
325 impl_display! { $error_name<'a>, {
326 ParseInt(e, s) => format!("Invalid integer for {}: \"{}\". Reason: {}", $prop_name, s, e),
327 ParseIntOwned(e, s) => format!("Invalid integer for {}: \"{}\". Reason: {}", $prop_name, s, e),
328 NegativeValue(s) => format!("Invalid value for {}: \"{}\". Value cannot be negative.", $prop_name, s),
329 }}
330
331 #[derive(Debug, Clone, PartialEq, Eq)]
332 #[repr(C, u8)]
333 pub enum $error_owned_name {
334 ParseInt(ParseIntErrorWithInput),
335 NegativeValue(AzString),
336 }
337
338 impl $error_name<'_> {
339 #[must_use] pub fn to_contained(&self) -> $error_owned_name {
340 match self {
341 Self::ParseInt(e, s) => $error_owned_name::ParseInt(ParseIntErrorWithInput { error: e.to_string().into(), input: s.to_string().into() }),
342 Self::ParseIntOwned(e, s) => $error_owned_name::ParseInt(ParseIntErrorWithInput { error: e.to_string().into(), input: s.to_string().into() }),
343 Self::NegativeValue(s) => $error_owned_name::NegativeValue(s.to_string().into()),
344 }
345 }
346 }
347
348 impl $error_owned_name {
349 #[must_use] pub fn to_shared(&self) -> $error_name<'_> {
350 match self {
351 Self::ParseInt(e) => $error_name::ParseIntOwned(e.error.as_str(), e.input.as_str()),
352 Self::NegativeValue(s) => $error_name::NegativeValue(s),
353 }
354 }
355 }
356
357 pub fn $fn_name(input: &str) -> Result<$struct_name, $error_name<'_>> {
361 let trimmed = input.trim();
362 let val: i32 = trimmed.parse().map_err(|e| $error_name::ParseInt(e, trimmed))?;
363 if val < 0 {
364 return Err($error_name::NegativeValue(trimmed));
365 }
366 Ok($struct_name { inner: u32::try_from(val).unwrap_or(0) })
367 }
368 };
369 }
370
371 define_widow_orphan_parser!(
372 parse_widows,
373 Widows,
374 WidowsParseError,
375 WidowsParseErrorOwned,
376 "widows"
377 );
378 define_widow_orphan_parser!(
379 parse_orphans,
380 Orphans,
381 OrphansParseError,
382 OrphansParseErrorOwned,
383 "orphans"
384 );
385
386 #[derive(Clone, PartialEq, Eq)]
390 pub enum BoxDecorationBreakParseError<'a> {
391 InvalidValue(&'a str),
392 }
393
394 impl_debug_as_display!(BoxDecorationBreakParseError<'a>);
395 impl_display! { BoxDecorationBreakParseError<'a>, {
396 InvalidValue(v) => format!("Invalid box-decoration-break value: \"{}\"", v),
397 }}
398
399 #[derive(Debug, Clone, PartialEq, Eq)]
401 #[repr(C, u8)]
402 pub enum BoxDecorationBreakParseErrorOwned {
403 InvalidValue(AzString),
404 }
405
406 impl BoxDecorationBreakParseError<'_> {
407 #[must_use] pub fn to_contained(&self) -> BoxDecorationBreakParseErrorOwned {
408 match self {
409 Self::InvalidValue(s) => {
410 BoxDecorationBreakParseErrorOwned::InvalidValue((*s).to_string().into())
411 }
412 }
413 }
414 }
415
416 impl BoxDecorationBreakParseErrorOwned {
417 #[must_use] pub fn to_shared(&self) -> BoxDecorationBreakParseError<'_> {
418 match self {
419 Self::InvalidValue(s) => BoxDecorationBreakParseError::InvalidValue(s.as_str()),
420 }
421 }
422 }
423
424 pub fn parse_box_decoration_break(
428 input: &str,
429 ) -> Result<BoxDecorationBreak, BoxDecorationBreakParseError<'_>> {
430 match input.trim() {
431 "slice" => Ok(BoxDecorationBreak::Slice),
432 "clone" => Ok(BoxDecorationBreak::Clone),
433 _ => Err(BoxDecorationBreakParseError::InvalidValue(input)),
434 }
435 }
436}
437
438#[cfg(feature = "parser")]
439pub use parser::*;
440
441#[cfg(all(test, feature = "parser"))]
442mod tests {
443 use super::*;
444
445 #[test]
446 fn test_parse_page_break() {
447 assert_eq!(parse_page_break("auto").unwrap(), PageBreak::Auto);
448 assert_eq!(parse_page_break("page").unwrap(), PageBreak::Page);
449 assert_eq!(
450 parse_page_break("avoid-column").unwrap(),
451 PageBreak::AvoidColumn
452 );
453 assert!(parse_page_break("invalid").is_err());
454 }
455
456 #[test]
457 fn test_parse_break_inside() {
458 assert_eq!(parse_break_inside("auto").unwrap(), BreakInside::Auto);
459 assert_eq!(parse_break_inside("avoid").unwrap(), BreakInside::Avoid);
460 assert!(parse_break_inside("always").is_err());
461 }
462
463 #[test]
464 fn test_parse_widows_orphans() {
465 assert_eq!(parse_widows("3").unwrap().inner, 3);
466 assert_eq!(parse_orphans(" 1 ").unwrap().inner, 1);
467 assert!(parse_widows("-2").is_err());
468 assert!(parse_orphans("auto").is_err());
469 }
470
471 #[test]
472 fn test_parse_box_decoration_break() {
473 assert_eq!(
474 parse_box_decoration_break("slice").unwrap(),
475 BoxDecorationBreak::Slice
476 );
477 assert_eq!(
478 parse_box_decoration_break("clone").unwrap(),
479 BoxDecorationBreak::Clone
480 );
481 assert!(parse_box_decoration_break("copy").is_err());
482 }
483}