1use alloc::string::{String, ToString};
4use core::num::ParseIntError;
5
6use crate::props::formatter::PrintAsCssValue;
7
8#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
12#[repr(C)]
13pub enum PageBreak {
14 Auto,
15 Avoid,
16 Always,
17 All,
18 Page,
19 AvoidPage,
20 Left,
21 Right,
22 Recto,
23 Verso,
24 Column,
25 AvoidColumn,
26}
27
28impl Default for PageBreak {
29 fn default() -> Self {
30 Self::Auto
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)]
58pub enum BreakInside {
59 Auto,
60 Avoid,
61 AvoidPage,
62 AvoidColumn,
63}
64
65impl Default for BreakInside {
66 fn default() -> Self {
67 Self::Auto
68 }
69}
70
71impl PrintAsCssValue for BreakInside {
72 fn print_as_css_value(&self) -> String {
73 String::from(match self {
74 Self::Auto => "auto",
75 Self::Avoid => "avoid",
76 Self::AvoidPage => "avoid-page",
77 Self::AvoidColumn => "avoid-column",
78 })
79 }
80}
81
82#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
87#[repr(C)]
88pub struct Widows {
89 pub inner: u32,
90}
91
92impl Default for Widows {
93 fn default() -> Self {
94 Self { inner: 2 }
95 }
96}
97
98impl PrintAsCssValue for Widows {
99 fn print_as_css_value(&self) -> String {
100 self.inner.to_string()
101 }
102}
103
104#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
107#[repr(C)]
108pub struct Orphans {
109 pub inner: u32,
110}
111
112impl Default for Orphans {
113 fn default() -> Self {
114 Self { inner: 2 }
115 }
116}
117
118impl PrintAsCssValue for Orphans {
119 fn print_as_css_value(&self) -> String {
120 self.inner.to_string()
121 }
122}
123
124#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
128#[repr(C)]
129pub enum BoxDecorationBreak {
130 Slice,
131 Clone,
132}
133
134impl Default for BoxDecorationBreak {
135 fn default() -> Self {
136 Self::Slice
137 }
138}
139
140impl PrintAsCssValue for BoxDecorationBreak {
141 fn print_as_css_value(&self) -> String {
142 String::from(match self {
143 Self::Slice => "slice",
144 Self::Clone => "clone",
145 })
146 }
147}
148
149impl crate::format_rust_code::FormatAsRustCode for PageBreak {
151 fn format_as_rust_code(&self, _tabs: usize) -> String {
152 match self {
153 PageBreak::Auto => String::from("PageBreak::Auto"),
154 PageBreak::Avoid => String::from("PageBreak::Avoid"),
155 PageBreak::Always => String::from("PageBreak::Always"),
156 PageBreak::All => String::from("PageBreak::All"),
157 PageBreak::Page => String::from("PageBreak::Page"),
158 PageBreak::AvoidPage => String::from("PageBreak::AvoidPage"),
159 PageBreak::Left => String::from("PageBreak::Left"),
160 PageBreak::Right => String::from("PageBreak::Right"),
161 PageBreak::Recto => String::from("PageBreak::Recto"),
162 PageBreak::Verso => String::from("PageBreak::Verso"),
163 PageBreak::Column => String::from("PageBreak::Column"),
164 PageBreak::AvoidColumn => String::from("PageBreak::AvoidColumn"),
165 }
166 }
167}
168
169impl crate::format_rust_code::FormatAsRustCode for BreakInside {
170 fn format_as_rust_code(&self, _tabs: usize) -> String {
171 match self {
172 BreakInside::Auto => String::from("BreakInside::Auto"),
173 BreakInside::Avoid => String::from("BreakInside::Avoid"),
174 BreakInside::AvoidPage => String::from("BreakInside::AvoidPage"),
175 BreakInside::AvoidColumn => String::from("BreakInside::AvoidColumn"),
176 }
177 }
178}
179
180impl crate::format_rust_code::FormatAsRustCode for Widows {
181 fn format_as_rust_code(&self, _tabs: usize) -> String {
182 format!("Widows {{ inner: {} }}", self.inner)
183 }
184}
185
186impl crate::format_rust_code::FormatAsRustCode for Orphans {
187 fn format_as_rust_code(&self, _tabs: usize) -> String {
188 format!("Orphans {{ inner: {} }}", self.inner)
189 }
190}
191
192impl crate::format_rust_code::FormatAsRustCode for BoxDecorationBreak {
193 fn format_as_rust_code(&self, _tabs: usize) -> String {
194 match self {
195 BoxDecorationBreak::Slice => String::from("BoxDecorationBreak::Slice"),
196 BoxDecorationBreak::Clone => String::from("BoxDecorationBreak::Clone"),
197 }
198 }
199}
200
201#[cfg(feature = "parser")]
204mod parser {
205 use super::*;
206
207 #[derive(Clone, PartialEq)]
210 pub enum PageBreakParseError<'a> {
211 InvalidValue(&'a str),
212 }
213
214 impl_debug_as_display!(PageBreakParseError<'a>);
215 impl_display! { PageBreakParseError<'a>, {
216 InvalidValue(v) => format!("Invalid break value: \"{}\"", v),
217 }}
218
219 #[derive(Debug, Clone, PartialEq)]
220 pub enum PageBreakParseErrorOwned {
221 InvalidValue(String),
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()),
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)]
261 pub enum BreakInsideParseError<'a> {
262 InvalidValue(&'a str),
263 }
264
265 impl_debug_as_display!(BreakInsideParseError<'a>);
266 impl_display! { BreakInsideParseError<'a>, {
267 InvalidValue(v) => format!("Invalid break-inside value: \"{}\"", v),
268 }}
269
270 #[derive(Debug, Clone, PartialEq)]
271 pub enum BreakInsideParseErrorOwned {
272 InvalidValue(String),
273 }
274
275 impl<'a> BreakInsideParseError<'a> {
276 pub fn to_contained(&self) -> BreakInsideParseErrorOwned {
277 match self {
278 Self::InvalidValue(s) => BreakInsideParseErrorOwned::InvalidValue(s.to_string()),
279 }
280 }
281 }
282
283 impl BreakInsideParseErrorOwned {
284 pub fn to_shared<'a>(&'a self) -> BreakInsideParseError<'a> {
285 match self {
286 Self::InvalidValue(s) => BreakInsideParseError::InvalidValue(s.as_str()),
287 }
288 }
289 }
290
291 pub fn parse_break_inside<'a>(
292 input: &'a str,
293 ) -> Result<BreakInside, BreakInsideParseError<'a>> {
294 match input.trim() {
295 "auto" => Ok(BreakInside::Auto),
296 "avoid" => Ok(BreakInside::Avoid),
297 "avoid-page" => Ok(BreakInside::AvoidPage),
298 "avoid-column" => Ok(BreakInside::AvoidColumn),
299 _ => Err(BreakInsideParseError::InvalidValue(input)),
300 }
301 }
302
303 macro_rules! define_widow_orphan_parser {
306 ($fn_name:ident, $struct_name:ident, $error_name:ident, $error_owned_name:ident, $prop_name:expr) => {
307 #[derive(Clone, PartialEq)]
308 pub enum $error_name<'a> {
309 ParseInt(ParseIntError, &'a str),
310 NegativeValue(&'a str),
311 }
312
313 impl_debug_as_display!($error_name<'a>);
314 impl_display! { $error_name<'a>, {
315 ParseInt(e, s) => format!("Invalid integer for {}: \"{}\". Reason: {}", $prop_name, s, e),
316 NegativeValue(s) => format!("Invalid value for {}: \"{}\". Value cannot be negative.", $prop_name, s),
317 }}
318
319 #[derive(Debug, Clone, PartialEq)]
320 pub enum $error_owned_name {
321 ParseInt(String, String),
322 NegativeValue(String),
323 }
324
325 impl<'a> $error_name<'a> {
326 pub fn to_contained(&self) -> $error_owned_name {
327 match self {
328 Self::ParseInt(e, s) => $error_owned_name::ParseInt(e.to_string(), s.to_string()),
329 Self::NegativeValue(s) => $error_owned_name::NegativeValue(s.to_string()),
330 }
331 }
332 }
333
334 impl $error_owned_name {
335 pub fn to_shared<'a>(&'a self) -> $error_name<'a> {
336 match self {
337 Self::ParseInt(_e, s) => $error_name::NegativeValue(s),
339 Self::NegativeValue(s) => $error_name::NegativeValue(s),
340 }
341 }
342 }
343
344 pub fn $fn_name<'a>(input: &'a str) -> Result<$struct_name, $error_name<'a>> {
345 let trimmed = input.trim();
346 let val: i32 = trimmed.parse().map_err(|e| $error_name::ParseInt(e, trimmed))?;
347 if val < 0 {
348 return Err($error_name::NegativeValue(trimmed));
349 }
350 Ok($struct_name { inner: val as u32 })
351 }
352 };
353 }
354
355 define_widow_orphan_parser!(
356 parse_widows,
357 Widows,
358 WidowsParseError,
359 WidowsParseErrorOwned,
360 "widows"
361 );
362 define_widow_orphan_parser!(
363 parse_orphans,
364 Orphans,
365 OrphansParseError,
366 OrphansParseErrorOwned,
367 "orphans"
368 );
369
370 #[derive(Clone, PartialEq)]
373 pub enum BoxDecorationBreakParseError<'a> {
374 InvalidValue(&'a str),
375 }
376
377 impl_debug_as_display!(BoxDecorationBreakParseError<'a>);
378 impl_display! { BoxDecorationBreakParseError<'a>, {
379 InvalidValue(v) => format!("Invalid box-decoration-break value: \"{}\"", v),
380 }}
381
382 #[derive(Debug, Clone, PartialEq)]
383 pub enum BoxDecorationBreakParseErrorOwned {
384 InvalidValue(String),
385 }
386
387 impl<'a> BoxDecorationBreakParseError<'a> {
388 pub fn to_contained(&self) -> BoxDecorationBreakParseErrorOwned {
389 match self {
390 Self::InvalidValue(s) => {
391 BoxDecorationBreakParseErrorOwned::InvalidValue(s.to_string())
392 }
393 }
394 }
395 }
396
397 impl BoxDecorationBreakParseErrorOwned {
398 pub fn to_shared<'a>(&'a self) -> BoxDecorationBreakParseError<'a> {
399 match self {
400 Self::InvalidValue(s) => BoxDecorationBreakParseError::InvalidValue(s.as_str()),
401 }
402 }
403 }
404
405 pub fn parse_box_decoration_break<'a>(
406 input: &'a str,
407 ) -> Result<BoxDecorationBreak, BoxDecorationBreakParseError<'a>> {
408 match input.trim() {
409 "slice" => Ok(BoxDecorationBreak::Slice),
410 "clone" => Ok(BoxDecorationBreak::Clone),
411 _ => Err(BoxDecorationBreakParseError::InvalidValue(input)),
412 }
413 }
414}
415
416#[cfg(feature = "parser")]
417pub use parser::*;
418
419#[cfg(all(test, feature = "parser"))]
420mod tests {
421 use super::*;
422
423 #[test]
424 fn test_parse_page_break() {
425 assert_eq!(parse_page_break("auto").unwrap(), PageBreak::Auto);
426 assert_eq!(parse_page_break("page").unwrap(), PageBreak::Page);
427 assert_eq!(
428 parse_page_break("avoid-column").unwrap(),
429 PageBreak::AvoidColumn
430 );
431 assert!(parse_page_break("invalid").is_err());
432 }
433
434 #[test]
435 fn test_parse_break_inside() {
436 assert_eq!(parse_break_inside("auto").unwrap(), BreakInside::Auto);
437 assert_eq!(parse_break_inside("avoid").unwrap(), BreakInside::Avoid);
438 assert!(parse_break_inside("always").is_err());
439 }
440
441 #[test]
442 fn test_parse_widows_orphans() {
443 assert_eq!(parse_widows("3").unwrap().inner, 3);
444 assert_eq!(parse_orphans(" 1 ").unwrap().inner, 1);
445 assert!(parse_widows("-2").is_err());
446 assert!(parse_orphans("auto").is_err());
447 }
448
449 #[test]
450 fn test_parse_box_decoration_break() {
451 assert_eq!(
452 parse_box_decoration_break("slice").unwrap(),
453 BoxDecorationBreak::Slice
454 );
455 assert_eq!(
456 parse_box_decoration_break("clone").unwrap(),
457 BoxDecorationBreak::Clone
458 );
459 assert!(parse_box_decoration_break("copy").is_err());
460 }
461}