1use alloc::{
4 string::{String, ToString},
5 vec::Vec,
6};
7
8#[cfg(feature = "parser")]
9use crate::props::basic::pixel::{parse_pixel_value_with_auto, PixelValueWithAuto};
10use crate::{
11 css::PrintAsCssValue,
12 props::{
13 basic::pixel::{CssPixelValueParseError, CssPixelValueParseErrorOwned, PixelValue},
14 macros::PixelValueTaker,
15 },
16};
17
18#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
24#[repr(C)]
25pub struct LayoutPaddingTop {
26 pub inner: PixelValue,
27}
28
29impl ::core::fmt::Debug for LayoutPaddingTop {
30 fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
31 write!(f, "{}", self.inner)
32 }
33}
34
35impl PixelValueTaker for LayoutPaddingTop {
36 fn from_pixel_value(inner: PixelValue) -> Self {
37 Self { inner }
38 }
39}
40
41impl_pixel_value!(LayoutPaddingTop);
42
43#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
45#[repr(C)]
46pub struct LayoutPaddingRight {
47 pub inner: PixelValue,
48}
49
50impl ::core::fmt::Debug for LayoutPaddingRight {
51 fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
52 write!(f, "{}", self.inner)
53 }
54}
55
56impl PixelValueTaker for LayoutPaddingRight {
57 fn from_pixel_value(inner: PixelValue) -> Self {
58 Self { inner }
59 }
60}
61
62impl_pixel_value!(LayoutPaddingRight);
63
64#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
66#[repr(C)]
67pub struct LayoutPaddingBottom {
68 pub inner: PixelValue,
69}
70
71impl ::core::fmt::Debug for LayoutPaddingBottom {
72 fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
73 write!(f, "{}", self.inner)
74 }
75}
76
77impl PixelValueTaker for LayoutPaddingBottom {
78 fn from_pixel_value(inner: PixelValue) -> Self {
79 Self { inner }
80 }
81}
82
83impl_pixel_value!(LayoutPaddingBottom);
84
85#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
87#[repr(C)]
88pub struct LayoutPaddingLeft {
89 pub inner: PixelValue,
90}
91
92impl ::core::fmt::Debug for LayoutPaddingLeft {
93 fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
94 write!(f, "{}", self.inner)
95 }
96}
97
98impl PixelValueTaker for LayoutPaddingLeft {
99 fn from_pixel_value(inner: PixelValue) -> Self {
100 Self { inner }
101 }
102}
103
104impl_pixel_value!(LayoutPaddingLeft);
105
106#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
108#[repr(C)]
109pub struct LayoutPaddingInlineStart {
110 pub inner: PixelValue,
111}
112
113impl ::core::fmt::Debug for LayoutPaddingInlineStart {
114 fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
115 write!(f, "{}", self.inner)
116 }
117}
118
119impl PixelValueTaker for LayoutPaddingInlineStart {
120 fn from_pixel_value(inner: PixelValue) -> Self {
121 Self { inner }
122 }
123}
124
125impl_pixel_value!(LayoutPaddingInlineStart);
126
127#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
129#[repr(C)]
130pub struct LayoutPaddingInlineEnd {
131 pub inner: PixelValue,
132}
133
134impl ::core::fmt::Debug for LayoutPaddingInlineEnd {
135 fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
136 write!(f, "{}", self.inner)
137 }
138}
139
140impl PixelValueTaker for LayoutPaddingInlineEnd {
141 fn from_pixel_value(inner: PixelValue) -> Self {
142 Self { inner }
143 }
144}
145
146impl_pixel_value!(LayoutPaddingInlineEnd);
147
148#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
150#[repr(C)]
151pub struct LayoutMarginTop {
152 pub inner: PixelValue,
153}
154
155impl ::core::fmt::Debug for LayoutMarginTop {
156 fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
157 write!(f, "{}", self.inner)
158 }
159}
160
161impl PixelValueTaker for LayoutMarginTop {
162 fn from_pixel_value(inner: PixelValue) -> Self {
163 Self { inner }
164 }
165}
166
167impl_pixel_value!(LayoutMarginTop);
168
169#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
171#[repr(C)]
172pub struct LayoutMarginRight {
173 pub inner: PixelValue,
174}
175
176impl ::core::fmt::Debug for LayoutMarginRight {
177 fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
178 write!(f, "{}", self.inner)
179 }
180}
181
182impl PixelValueTaker for LayoutMarginRight {
183 fn from_pixel_value(inner: PixelValue) -> Self {
184 Self { inner }
185 }
186}
187
188impl_pixel_value!(LayoutMarginRight);
189
190#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
192#[repr(C)]
193pub struct LayoutMarginBottom {
194 pub inner: PixelValue,
195}
196
197impl ::core::fmt::Debug for LayoutMarginBottom {
198 fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
199 write!(f, "{}", self.inner)
200 }
201}
202
203impl PixelValueTaker for LayoutMarginBottom {
204 fn from_pixel_value(inner: PixelValue) -> Self {
205 Self { inner }
206 }
207}
208
209impl_pixel_value!(LayoutMarginBottom);
210
211#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
213#[repr(C)]
214pub struct LayoutMarginLeft {
215 pub inner: PixelValue,
216}
217
218impl ::core::fmt::Debug for LayoutMarginLeft {
219 fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
220 write!(f, "{}", self.inner)
221 }
222}
223
224impl PixelValueTaker for LayoutMarginLeft {
225 fn from_pixel_value(inner: PixelValue) -> Self {
226 Self { inner }
227 }
228}
229
230impl_pixel_value!(LayoutMarginLeft);
231
232#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
234#[repr(C)]
235pub struct LayoutColumnGap {
236 pub inner: PixelValue,
237}
238
239impl ::core::fmt::Debug for LayoutColumnGap {
240 fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
241 write!(f, "{}", self.inner)
242 }
243}
244
245impl PixelValueTaker for LayoutColumnGap {
246 fn from_pixel_value(inner: PixelValue) -> Self {
247 Self { inner }
248 }
249}
250
251impl_pixel_value!(LayoutColumnGap);
252
253#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
255#[repr(C)]
256pub struct LayoutRowGap {
257 pub inner: PixelValue,
258}
259
260impl ::core::fmt::Debug for LayoutRowGap {
261 fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
262 write!(f, "{}", self.inner)
263 }
264}
265
266impl PixelValueTaker for LayoutRowGap {
267 fn from_pixel_value(inner: PixelValue) -> Self {
268 Self { inner }
269 }
270}
271
272impl_pixel_value!(LayoutRowGap);
273
274#[cfg(feature = "parser")]
279#[derive(Clone, PartialEq)]
280pub enum LayoutPaddingParseError<'a> {
281 PixelValueParseError(CssPixelValueParseError<'a>),
282 TooManyValues,
283 TooFewValues,
284}
285
286#[cfg(feature = "parser")]
287impl_debug_as_display!(LayoutPaddingParseError<'a>);
288
289#[cfg(feature = "parser")]
290impl_display! { LayoutPaddingParseError<'a>, {
291 PixelValueParseError(e) => format!("Could not parse pixel value: {}", e),
292 TooManyValues => "Too many values: padding property accepts at most 4 values.",
293 TooFewValues => "Too few values: padding property requires at least 1 value.",
294}}
295
296#[cfg(feature = "parser")]
297impl_from!(
298 CssPixelValueParseError<'a>,
299 LayoutPaddingParseError::PixelValueParseError
300);
301
302#[cfg(feature = "parser")]
303#[derive(Debug, Clone, PartialEq)]
304pub enum LayoutPaddingParseErrorOwned {
305 PixelValueParseError(CssPixelValueParseErrorOwned),
306 TooManyValues,
307 TooFewValues,
308}
309
310#[cfg(feature = "parser")]
311impl<'a> LayoutPaddingParseError<'a> {
312 pub fn to_contained(&self) -> LayoutPaddingParseErrorOwned {
313 match self {
314 LayoutPaddingParseError::PixelValueParseError(e) => {
315 LayoutPaddingParseErrorOwned::PixelValueParseError(e.to_contained())
316 }
317 LayoutPaddingParseError::TooManyValues => LayoutPaddingParseErrorOwned::TooManyValues,
318 LayoutPaddingParseError::TooFewValues => LayoutPaddingParseErrorOwned::TooFewValues,
319 }
320 }
321}
322
323#[cfg(feature = "parser")]
324impl LayoutPaddingParseErrorOwned {
325 pub fn to_shared<'a>(&'a self) -> LayoutPaddingParseError<'a> {
326 match self {
327 LayoutPaddingParseErrorOwned::PixelValueParseError(e) => {
328 LayoutPaddingParseError::PixelValueParseError(e.to_shared())
329 }
330 LayoutPaddingParseErrorOwned::TooManyValues => LayoutPaddingParseError::TooManyValues,
331 LayoutPaddingParseErrorOwned::TooFewValues => LayoutPaddingParseError::TooFewValues,
332 }
333 }
334}
335
336#[cfg(feature = "parser")]
337#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
338pub struct LayoutPadding {
339 pub top: PixelValueWithAuto,
340 pub bottom: PixelValueWithAuto,
341 pub left: PixelValueWithAuto,
342 pub right: PixelValueWithAuto,
343}
344
345#[cfg(feature = "parser")]
346pub fn parse_layout_padding<'a>(
347 input: &'a str,
348) -> Result<LayoutPadding, LayoutPaddingParseError<'a>> {
349 let values: Vec<_> = input.split_whitespace().collect();
350
351 let parsed_values: Vec<PixelValueWithAuto> = values
352 .iter()
353 .map(|s| parse_pixel_value_with_auto(s))
354 .collect::<Result<_, _>>()?;
355
356 match parsed_values.len() {
357 1 => {
358 let all = parsed_values[0];
360 Ok(LayoutPadding {
361 top: all,
362 right: all,
363 bottom: all,
364 left: all,
365 })
366 }
367 2 => {
368 let vertical = parsed_values[0];
370 let horizontal = parsed_values[1];
371 Ok(LayoutPadding {
372 top: vertical,
373 right: horizontal,
374 bottom: vertical,
375 left: horizontal,
376 })
377 }
378 3 => {
379 let top = parsed_values[0];
381 let horizontal = parsed_values[1];
382 let bottom = parsed_values[2];
383 Ok(LayoutPadding {
384 top,
385 right: horizontal,
386 bottom,
387 left: horizontal,
388 })
389 }
390 4 => {
391 Ok(LayoutPadding {
393 top: parsed_values[0],
394 right: parsed_values[1],
395 bottom: parsed_values[2],
396 left: parsed_values[3],
397 })
398 }
399 0 => Err(LayoutPaddingParseError::TooFewValues),
400 _ => Err(LayoutPaddingParseError::TooManyValues),
401 }
402}
403
404#[cfg(feature = "parser")]
407#[derive(Clone, PartialEq)]
408pub enum LayoutMarginParseError<'a> {
409 PixelValueParseError(CssPixelValueParseError<'a>),
410 TooManyValues,
411 TooFewValues,
412}
413
414#[cfg(feature = "parser")]
415impl_debug_as_display!(LayoutMarginParseError<'a>);
416
417#[cfg(feature = "parser")]
418impl_display! { LayoutMarginParseError<'a>, {
419 PixelValueParseError(e) => format!("Could not parse pixel value: {}", e),
420 TooManyValues => "Too many values: margin property accepts at most 4 values.",
421 TooFewValues => "Too few values: margin property requires at least 1 value.",
422}}
423
424#[cfg(feature = "parser")]
425impl_from!(
426 CssPixelValueParseError<'a>,
427 LayoutMarginParseError::PixelValueParseError
428);
429
430#[cfg(feature = "parser")]
431#[derive(Debug, Clone, PartialEq)]
432pub enum LayoutMarginParseErrorOwned {
433 PixelValueParseError(CssPixelValueParseErrorOwned),
434 TooManyValues,
435 TooFewValues,
436}
437
438#[cfg(feature = "parser")]
439impl<'a> LayoutMarginParseError<'a> {
440 pub fn to_contained(&self) -> LayoutMarginParseErrorOwned {
441 match self {
442 LayoutMarginParseError::PixelValueParseError(e) => {
443 LayoutMarginParseErrorOwned::PixelValueParseError(e.to_contained())
444 }
445 LayoutMarginParseError::TooManyValues => LayoutMarginParseErrorOwned::TooManyValues,
446 LayoutMarginParseError::TooFewValues => LayoutMarginParseErrorOwned::TooFewValues,
447 }
448 }
449}
450
451#[cfg(feature = "parser")]
452impl LayoutMarginParseErrorOwned {
453 pub fn to_shared<'a>(&'a self) -> LayoutMarginParseError<'a> {
454 match self {
455 LayoutMarginParseErrorOwned::PixelValueParseError(e) => {
456 LayoutMarginParseError::PixelValueParseError(e.to_shared())
457 }
458 LayoutMarginParseErrorOwned::TooManyValues => LayoutMarginParseError::TooManyValues,
459 LayoutMarginParseErrorOwned::TooFewValues => LayoutMarginParseError::TooFewValues,
460 }
461 }
462}
463
464#[cfg(feature = "parser")]
465#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
466pub struct LayoutMargin {
467 pub top: PixelValueWithAuto,
468 pub bottom: PixelValueWithAuto,
469 pub left: PixelValueWithAuto,
470 pub right: PixelValueWithAuto,
471}
472
473#[cfg(feature = "parser")]
474pub fn parse_layout_margin<'a>(input: &'a str) -> Result<LayoutMargin, LayoutMarginParseError<'a>> {
475 match parse_layout_padding(input) {
478 Ok(padding) => Ok(LayoutMargin {
479 top: padding.top,
480 left: padding.left,
481 right: padding.right,
482 bottom: padding.bottom,
483 }),
484 Err(e) => match e {
485 LayoutPaddingParseError::PixelValueParseError(err) => {
486 Err(LayoutMarginParseError::PixelValueParseError(err))
487 }
488 LayoutPaddingParseError::TooManyValues => Err(LayoutMarginParseError::TooManyValues),
489 LayoutPaddingParseError::TooFewValues => Err(LayoutMarginParseError::TooFewValues),
490 },
491 }
492}
493
494macro_rules! typed_pixel_value_parser {
497 (
498 $fn:ident, $fn_str:expr, $return:ident, $return_str:expr, $import_str:expr, $test_str:expr
499 ) => {
500 #[doc = $return_str]
502 #[doc = $import_str]
508 #[doc = $test_str]
509 pub fn $fn<'a>(input: &'a str) -> Result<$return, CssPixelValueParseError<'a>> {
511 crate::props::basic::parse_pixel_value(input).and_then(|e| Ok($return { inner: e }))
512 }
513
514 impl crate::props::formatter::FormatAsCssValue for $return {
515 fn format_as_css_value(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
516 self.inner.format_as_css_value(f)
517 }
518 }
519 };
520 ($fn:ident, $return:ident) => {
521 typed_pixel_value_parser!(
522 $fn,
523 stringify!($fn),
524 $return,
525 stringify!($return),
526 concat!(
527 "# extern crate azul_css;",
528 "\r\n",
529 "# use azul_css::props::layout::spacing::",
530 stringify!($fn),
531 ";",
532 "\r\n",
533 "# use azul_css::props::basic::pixel::PixelValue;\r\n",
534 "# use azul_css::props::layout::spacing::",
535 stringify!($return),
536 ";\r\n"
537 ),
538 concat!(
539 "assert_eq!(",
540 stringify!($fn),
541 "(\"5px\"), Ok(",
542 stringify!($return),
543 " { inner: PixelValue::px(5.0) }));"
544 )
545 );
546 };
547}
548
549#[cfg(feature = "parser")]
550typed_pixel_value_parser!(parse_layout_padding_top, LayoutPaddingTop);
551#[cfg(feature = "parser")]
552typed_pixel_value_parser!(parse_layout_padding_right, LayoutPaddingRight);
553#[cfg(feature = "parser")]
554typed_pixel_value_parser!(parse_layout_padding_bottom, LayoutPaddingBottom);
555#[cfg(feature = "parser")]
556typed_pixel_value_parser!(parse_layout_padding_left, LayoutPaddingLeft);
557#[cfg(feature = "parser")]
558typed_pixel_value_parser!(parse_layout_padding_inline_start, LayoutPaddingInlineStart);
559#[cfg(feature = "parser")]
560typed_pixel_value_parser!(parse_layout_padding_inline_end, LayoutPaddingInlineEnd);
561
562#[cfg(feature = "parser")]
563typed_pixel_value_parser!(parse_layout_margin_top, LayoutMarginTop);
564#[cfg(feature = "parser")]
565typed_pixel_value_parser!(parse_layout_margin_right, LayoutMarginRight);
566#[cfg(feature = "parser")]
567typed_pixel_value_parser!(parse_layout_margin_bottom, LayoutMarginBottom);
568#[cfg(feature = "parser")]
569typed_pixel_value_parser!(parse_layout_margin_left, LayoutMarginLeft);
570
571#[cfg(feature = "parser")]
572typed_pixel_value_parser!(parse_layout_column_gap, LayoutColumnGap);
573#[cfg(feature = "parser")]
574typed_pixel_value_parser!(parse_layout_row_gap, LayoutRowGap);
575
576#[cfg(all(test, feature = "parser"))]
577mod tests {
578 use super::*;
579 use crate::props::basic::pixel::{PixelValue, PixelValueWithAuto};
580
581 #[test]
582 fn test_parse_layout_padding_shorthand() {
583 let result = parse_layout_padding("10px").unwrap();
585 assert_eq!(result.top, PixelValueWithAuto::Exact(PixelValue::px(10.0)));
586 assert_eq!(
587 result.right,
588 PixelValueWithAuto::Exact(PixelValue::px(10.0))
589 );
590 assert_eq!(
591 result.bottom,
592 PixelValueWithAuto::Exact(PixelValue::px(10.0))
593 );
594 assert_eq!(result.left, PixelValueWithAuto::Exact(PixelValue::px(10.0)));
595
596 let result = parse_layout_padding("5% 2em").unwrap();
598 assert_eq!(
599 result.top,
600 PixelValueWithAuto::Exact(PixelValue::percent(5.0))
601 );
602 assert_eq!(result.right, PixelValueWithAuto::Exact(PixelValue::em(2.0)));
603 assert_eq!(
604 result.bottom,
605 PixelValueWithAuto::Exact(PixelValue::percent(5.0))
606 );
607 assert_eq!(result.left, PixelValueWithAuto::Exact(PixelValue::em(2.0)));
608
609 let result = parse_layout_padding("1px 2px 3px").unwrap();
611 assert_eq!(result.top, PixelValueWithAuto::Exact(PixelValue::px(1.0)));
612 assert_eq!(result.right, PixelValueWithAuto::Exact(PixelValue::px(2.0)));
613 assert_eq!(
614 result.bottom,
615 PixelValueWithAuto::Exact(PixelValue::px(3.0))
616 );
617 assert_eq!(result.left, PixelValueWithAuto::Exact(PixelValue::px(2.0)));
618
619 let result = parse_layout_padding("1px 2px 3px 4px").unwrap();
621 assert_eq!(result.top, PixelValueWithAuto::Exact(PixelValue::px(1.0)));
622 assert_eq!(result.right, PixelValueWithAuto::Exact(PixelValue::px(2.0)));
623 assert_eq!(
624 result.bottom,
625 PixelValueWithAuto::Exact(PixelValue::px(3.0))
626 );
627 assert_eq!(result.left, PixelValueWithAuto::Exact(PixelValue::px(4.0)));
628
629 let result = parse_layout_padding(" 1px 2px ").unwrap();
631 assert_eq!(result.top, PixelValueWithAuto::Exact(PixelValue::px(1.0)));
632 assert_eq!(result.right, PixelValueWithAuto::Exact(PixelValue::px(2.0)));
633 }
634
635 #[test]
636 fn test_parse_layout_padding_errors() {
637 assert!(matches!(
638 parse_layout_padding("").err().unwrap(),
639 LayoutPaddingParseError::TooFewValues
640 ));
641 assert!(matches!(
642 parse_layout_padding("1px 2px 3px 4px 5px").err().unwrap(),
643 LayoutPaddingParseError::TooManyValues
644 ));
645 assert!(matches!(
646 parse_layout_padding("1px oops 3px").err().unwrap(),
647 LayoutPaddingParseError::PixelValueParseError(_)
648 ));
649 }
650
651 #[test]
652 fn test_parse_layout_margin_shorthand() {
653 let result = parse_layout_margin("auto").unwrap();
655 assert_eq!(result.top, PixelValueWithAuto::Auto);
656 assert_eq!(result.right, PixelValueWithAuto::Auto);
657 assert_eq!(result.bottom, PixelValueWithAuto::Auto);
658 assert_eq!(result.left, PixelValueWithAuto::Auto);
659
660 let result = parse_layout_margin("10px auto").unwrap();
662 assert_eq!(result.top, PixelValueWithAuto::Exact(PixelValue::px(10.0)));
663 assert_eq!(result.right, PixelValueWithAuto::Auto);
664 assert_eq!(
665 result.bottom,
666 PixelValueWithAuto::Exact(PixelValue::px(10.0))
667 );
668 assert_eq!(result.left, PixelValueWithAuto::Auto);
669 }
670
671 #[test]
672 fn test_parse_layout_margin_errors() {
673 assert!(matches!(
674 parse_layout_margin("").err().unwrap(),
675 LayoutMarginParseError::TooFewValues
676 ));
677 assert!(matches!(
678 parse_layout_margin("1px 2px 3px 4px 5px").err().unwrap(),
679 LayoutMarginParseError::TooManyValues
680 ));
681 assert!(matches!(
682 parse_layout_margin("1px invalid").err().unwrap(),
683 LayoutMarginParseError::PixelValueParseError(_)
684 ));
685 }
686
687 #[test]
688 fn test_parse_longhand_spacing() {
689 assert_eq!(
690 parse_layout_padding_left("2em").unwrap(),
691 LayoutPaddingLeft {
692 inner: PixelValue::em(2.0)
693 }
694 );
695 assert!(parse_layout_margin_top("auto").is_err()); assert_eq!(
697 parse_layout_column_gap("20px").unwrap(),
698 LayoutColumnGap {
699 inner: PixelValue::px(20.0)
700 }
701 );
702 }
703}