1use alloc::string::{String, ToString};
4use core::fmt;
5use crate::corety::AzString;
6
7#[cfg(feature = "parser")]
8use crate::props::basic::{color::parse_css_color, pixel::parse_pixel_value};
9use crate::{
10 css::PrintAsCssValue,
11 props::{
12 basic::{
13 color::{ColorU, CssColorParseError, CssColorParseErrorOwned},
14 pixel::{
15 CssPixelValueParseError, CssPixelValueParseErrorOwned, PixelValue,
16 MEDIUM_BORDER_THICKNESS, THICK_BORDER_THICKNESS, THIN_BORDER_THICKNESS,
17 },
18 },
19 macros::PixelValueTaker,
20 },
21};
22
23#[derive(Debug, Copy, Clone, PartialEq, Ord, PartialOrd, Eq, Hash)]
25#[repr(C)]
26#[derive(Default)]
28pub enum BorderStyle {
29 #[default]
30 None,
31 Solid,
32 Double,
33 Dotted,
34 Dashed,
35 Hidden,
36 Groove,
37 Ridge,
38 Inset,
39 Outset,
40}
41
42
43impl fmt::Display for BorderStyle {
44 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45 write!(
46 f,
47 "{}",
48 match self {
49 Self::None => "none",
50 Self::Solid => "solid",
51 Self::Double => "double",
52 Self::Dotted => "dotted",
53 Self::Dashed => "dashed",
54 Self::Hidden => "hidden",
55 Self::Groove => "groove",
56 Self::Ridge => "ridge",
57 Self::Inset => "inset",
58 Self::Outset => "outset",
59 }
60 )
61 }
62}
63
64impl PrintAsCssValue for BorderStyle {
65 fn print_as_css_value(&self) -> String {
66 self.to_string()
67 }
68}
69
70macro_rules! define_border_side_property {
72 ($struct_name:ident, $inner_type:ty, $default:expr) => {
74 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
75 #[repr(C)]
76 pub struct $struct_name {
77 pub inner: $inner_type,
78 }
79 impl ::core::fmt::Debug for $struct_name {
80 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
81 write!(f, "{}", self.inner)
82 }
83 }
84 impl Default for $struct_name {
85 fn default() -> Self {
86 Self { inner: $default }
87 }
88 }
89 };
90 ($struct_name:ident,ColorU) => {
92 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
93 #[repr(C)]
94 pub struct $struct_name {
95 pub inner: ColorU,
96 }
97 impl ::core::fmt::Debug for $struct_name {
98 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
99 write!(f, "{}", self.inner.to_hash())
100 }
101 }
102 impl Default for $struct_name {
105 fn default() -> Self {
106 Self {
107 inner: ColorU::BLACK,
108 }
109 }
110 }
111 impl $struct_name {
112 #[must_use] pub fn interpolate(&self, other: &Self, t: f32) -> Self {
113 Self {
114 inner: self.inner.interpolate(&other.inner, t),
115 }
116 }
117 }
118 };
119 }
124
125define_border_side_property!(StyleBorderTopStyle, BorderStyle, BorderStyle::None);
131define_border_side_property!(StyleBorderRightStyle, BorderStyle, BorderStyle::None);
133define_border_side_property!(StyleBorderBottomStyle, BorderStyle, BorderStyle::None);
135define_border_side_property!(StyleBorderLeftStyle, BorderStyle, BorderStyle::None);
137
138impl crate::codegen::format::FormatAsRustCode for StyleBorderTopStyle {
140 fn format_as_rust_code(&self, tabs: usize) -> String {
141 format!(
142 "StyleBorderTopStyle {{ inner: {} }}",
143 &self.inner.format_as_rust_code(tabs)
144 )
145 }
146}
147
148impl crate::codegen::format::FormatAsRustCode for StyleBorderRightStyle {
149 fn format_as_rust_code(&self, tabs: usize) -> String {
150 format!(
151 "StyleBorderRightStyle {{ inner: {} }}",
152 &self.inner.format_as_rust_code(tabs)
153 )
154 }
155}
156
157impl crate::codegen::format::FormatAsRustCode for StyleBorderLeftStyle {
158 fn format_as_rust_code(&self, tabs: usize) -> String {
159 format!(
160 "StyleBorderLeftStyle {{ inner: {} }}",
161 &self.inner.format_as_rust_code(tabs)
162 )
163 }
164}
165
166impl crate::codegen::format::FormatAsRustCode for StyleBorderBottomStyle {
167 fn format_as_rust_code(&self, tabs: usize) -> String {
168 format!(
169 "StyleBorderBottomStyle {{ inner: {} }}",
170 &self.inner.format_as_rust_code(tabs)
171 )
172 }
173}
174
175define_border_side_property!(StyleBorderTopColor, ColorU);
178define_border_side_property!(StyleBorderRightColor, ColorU);
180define_border_side_property!(StyleBorderBottomColor, ColorU);
182define_border_side_property!(StyleBorderLeftColor, ColorU);
184
185define_border_side_property!(LayoutBorderTopWidth, PixelValue, MEDIUM_BORDER_THICKNESS);
190define_border_side_property!(LayoutBorderRightWidth, PixelValue, MEDIUM_BORDER_THICKNESS);
192define_border_side_property!(LayoutBorderBottomWidth, PixelValue, MEDIUM_BORDER_THICKNESS);
194define_border_side_property!(LayoutBorderLeftWidth, PixelValue, MEDIUM_BORDER_THICKNESS);
196
197macro_rules! impl_border_width_helpers {
198 ($($t:ty),+) => { $(
199 impl $t {
200 #[must_use] pub fn interpolate(&self, other: &Self, t: f32) -> Self {
201 Self { inner: self.inner.interpolate(&other.inner, t) }
202 }
203 #[must_use] pub const fn const_px(value: isize) -> Self {
204 Self { inner: PixelValue::const_px(value) }
205 }
206 }
207 )+ };
208}
209
210impl_border_width_helpers!(
211 LayoutBorderTopWidth,
212 LayoutBorderRightWidth,
213 LayoutBorderBottomWidth,
214 LayoutBorderLeftWidth
215);
216
217#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
220pub struct StyleBorderSide {
221 pub border_width: PixelValue,
222 pub border_style: BorderStyle,
223 pub border_color: ColorU,
224}
225
226#[cfg(feature = "parser")]
231#[derive(Clone, PartialEq, Eq)]
232pub enum CssBorderStyleParseError<'a> {
233 InvalidStyle(&'a str),
234}
235
236#[cfg(feature = "parser")]
237impl_debug_as_display!(CssBorderStyleParseError<'a>);
238#[cfg(feature = "parser")]
239impl_display! { CssBorderStyleParseError<'a>, {
240 InvalidStyle(val) => format!("Invalid border style: \"{}\"", val),
241}}
242
243#[cfg(feature = "parser")]
244#[derive(Debug, Clone, PartialEq, Eq)]
245#[repr(C, u8)]
246pub enum CssBorderStyleParseErrorOwned {
247 InvalidStyle(AzString),
248}
249
250#[cfg(feature = "parser")]
251impl CssBorderStyleParseError<'_> {
252 #[must_use] pub fn to_contained(&self) -> CssBorderStyleParseErrorOwned {
253 match self {
254 CssBorderStyleParseError::InvalidStyle(s) => {
255 CssBorderStyleParseErrorOwned::InvalidStyle((*s).to_string().into())
256 }
257 }
258 }
259}
260
261#[cfg(feature = "parser")]
262impl CssBorderStyleParseErrorOwned {
263 #[must_use] pub fn to_shared(&self) -> CssBorderStyleParseError<'_> {
264 match self {
265 Self::InvalidStyle(s) => {
266 CssBorderStyleParseError::InvalidStyle(s.as_str())
267 }
268 }
269 }
270}
271
272#[cfg(feature = "parser")]
273pub fn parse_border_style(input: &str) -> Result<BorderStyle, CssBorderStyleParseError<'_>> {
277 match input.trim() {
278 "none" => Ok(BorderStyle::None),
279 "solid" => Ok(BorderStyle::Solid),
280 "double" => Ok(BorderStyle::Double),
281 "dotted" => Ok(BorderStyle::Dotted),
282 "dashed" => Ok(BorderStyle::Dashed),
283 "hidden" => Ok(BorderStyle::Hidden),
284 "groove" => Ok(BorderStyle::Groove),
285 "ridge" => Ok(BorderStyle::Ridge),
286 "inset" => Ok(BorderStyle::Inset),
287 "outset" => Ok(BorderStyle::Outset),
288 _ => Err(CssBorderStyleParseError::InvalidStyle(input)),
289 }
290}
291
292#[cfg(feature = "parser")]
295#[derive(Clone, PartialEq)]
296pub enum CssBorderSideParseError<'a> {
297 InvalidDeclaration(&'a str),
298 Width(CssPixelValueParseError<'a>),
299 Style(CssBorderStyleParseError<'a>),
300 Color(CssColorParseError<'a>),
301}
302
303#[cfg(feature = "parser")]
304impl_debug_as_display!(CssBorderSideParseError<'a>);
305#[cfg(feature = "parser")]
306impl_display! { CssBorderSideParseError<'a>, {
307 InvalidDeclaration(e) => format!("Invalid border declaration: \"{}\"", e),
308 Width(e) => format!("Invalid border-width component: {}", e),
309 Style(e) => format!("Invalid border-style component: {}", e),
310 Color(e) => format!("Invalid border-color component: {}", e),
311}}
312
313#[cfg(feature = "parser")]
314impl_from!(CssPixelValueParseError<'a>, CssBorderSideParseError::Width);
315#[cfg(feature = "parser")]
316impl_from!(CssBorderStyleParseError<'a>, CssBorderSideParseError::Style);
317#[cfg(feature = "parser")]
318impl_from!(CssColorParseError<'a>, CssBorderSideParseError::Color);
319
320#[cfg(feature = "parser")]
321#[derive(Debug, Clone, PartialEq)]
322#[repr(C, u8)]
323pub enum CssBorderSideParseErrorOwned {
324 InvalidDeclaration(AzString),
325 Width(CssPixelValueParseErrorOwned),
326 Style(CssBorderStyleParseErrorOwned),
327 Color(CssColorParseErrorOwned),
328}
329
330#[cfg(feature = "parser")]
331impl CssBorderSideParseError<'_> {
332 #[must_use] pub fn to_contained(&self) -> CssBorderSideParseErrorOwned {
333 match self {
334 CssBorderSideParseError::InvalidDeclaration(s) => {
335 CssBorderSideParseErrorOwned::InvalidDeclaration((*s).to_string().into())
336 }
337 CssBorderSideParseError::Width(e) => {
338 CssBorderSideParseErrorOwned::Width(e.to_contained())
339 }
340 CssBorderSideParseError::Style(e) => {
341 CssBorderSideParseErrorOwned::Style(e.to_contained())
342 }
343 CssBorderSideParseError::Color(e) => {
344 CssBorderSideParseErrorOwned::Color(e.to_contained())
345 }
346 }
347 }
348}
349
350#[cfg(feature = "parser")]
351impl CssBorderSideParseErrorOwned {
352 #[must_use] pub fn to_shared(&self) -> CssBorderSideParseError<'_> {
353 match self {
354 Self::InvalidDeclaration(s) => {
355 CssBorderSideParseError::InvalidDeclaration(s.as_str())
356 }
357 Self::Width(e) => CssBorderSideParseError::Width(e.to_shared()),
358 Self::Style(e) => CssBorderSideParseError::Style(e.to_shared()),
359 Self::Color(e) => CssBorderSideParseError::Color(e.to_shared()),
360 }
361 }
362}
363
364#[cfg(feature = "parser")]
366pub type CssBorderParseError<'a> = CssBorderSideParseError<'a>;
367
368#[cfg(feature = "parser")]
370#[derive(Debug, Clone, PartialEq)]
371#[repr(C)]
372pub struct CssBorderParseErrorOwned {
373 pub inner: CssBorderSideParseErrorOwned,
374}
375
376#[cfg(feature = "parser")]
377impl From<CssBorderSideParseErrorOwned> for CssBorderParseErrorOwned {
378 fn from(v: CssBorderSideParseErrorOwned) -> Self {
379 Self { inner: v }
380 }
381}
382
383#[cfg(feature = "parser")]
386fn parse_border_side(
387 input: &str,
388) -> Result<StyleBorderSide, CssBorderSideParseError<'_>> {
389 let mut width = None;
390 let mut style = None;
391 let mut color = None;
392
393 if input.trim().is_empty() {
394 return Err(CssBorderSideParseError::InvalidDeclaration(input));
395 }
396
397 for part in input.split_whitespace() {
398 if width.is_none() {
400 if let Ok(w) = parse_border_width_value(part) {
401 width = Some(w);
402 continue;
403 }
404 }
405
406 if style.is_none() {
408 if let Ok(s) = parse_border_style(part) {
409 style = Some(s);
410 continue;
411 }
412 }
413
414 if color.is_none() {
416 if let Ok(c) = parse_css_color(part) {
417 color = Some(c);
418 continue;
419 }
420 }
421
422 return Err(CssBorderSideParseError::InvalidDeclaration(input));
424 }
425
426 Ok(StyleBorderSide {
427 border_width: width.unwrap_or(MEDIUM_BORDER_THICKNESS),
428 border_style: style.unwrap_or(BorderStyle::None),
429 border_color: color.unwrap_or(ColorU::BLACK),
430 })
431}
432
433#[cfg(feature = "parser")]
436fn parse_border_width_value(
437 input: &str,
438) -> Result<PixelValue, CssPixelValueParseError<'_>> {
439 match input.trim() {
440 "thin" => Ok(THIN_BORDER_THICKNESS),
441 "medium" => Ok(MEDIUM_BORDER_THICKNESS),
442 "thick" => Ok(THICK_BORDER_THICKNESS),
443 _ => parse_pixel_value(input),
444 }
445}
446
447#[cfg(feature = "parser")]
448pub fn parse_border_top_width(
452 input: &str,
453) -> Result<LayoutBorderTopWidth, CssPixelValueParseError<'_>> {
454 parse_border_width_value(input).map(|inner| LayoutBorderTopWidth { inner })
455}
456
457#[cfg(feature = "parser")]
458pub fn parse_border_right_width(
462 input: &str,
463) -> Result<LayoutBorderRightWidth, CssPixelValueParseError<'_>> {
464 parse_border_width_value(input).map(|inner| LayoutBorderRightWidth { inner })
465}
466
467#[cfg(feature = "parser")]
468pub fn parse_border_bottom_width(
472 input: &str,
473) -> Result<LayoutBorderBottomWidth, CssPixelValueParseError<'_>> {
474 parse_border_width_value(input).map(|inner| LayoutBorderBottomWidth { inner })
475}
476
477#[cfg(feature = "parser")]
478pub fn parse_border_left_width(
482 input: &str,
483) -> Result<LayoutBorderLeftWidth, CssPixelValueParseError<'_>> {
484 parse_border_width_value(input).map(|inner| LayoutBorderLeftWidth { inner })
485}
486
487#[cfg(feature = "parser")]
488pub fn parse_border_top_style(
492 input: &str,
493) -> Result<StyleBorderTopStyle, CssBorderStyleParseError<'_>> {
494 parse_border_style(input).map(|inner| StyleBorderTopStyle { inner })
495}
496#[cfg(feature = "parser")]
497pub fn parse_border_right_style(
501 input: &str,
502) -> Result<StyleBorderRightStyle, CssBorderStyleParseError<'_>> {
503 parse_border_style(input).map(|inner| StyleBorderRightStyle { inner })
504}
505#[cfg(feature = "parser")]
506pub fn parse_border_bottom_style(
510 input: &str,
511) -> Result<StyleBorderBottomStyle, CssBorderStyleParseError<'_>> {
512 parse_border_style(input).map(|inner| StyleBorderBottomStyle { inner })
513}
514#[cfg(feature = "parser")]
515pub fn parse_border_left_style(
519 input: &str,
520) -> Result<StyleBorderLeftStyle, CssBorderStyleParseError<'_>> {
521 parse_border_style(input).map(|inner| StyleBorderLeftStyle { inner })
522}
523
524#[cfg(feature = "parser")]
525pub fn parse_border_top_color(
529 input: &str,
530) -> Result<StyleBorderTopColor, CssColorParseError<'_>> {
531 parse_css_color(input).map(|inner| StyleBorderTopColor { inner })
532}
533#[cfg(feature = "parser")]
534pub fn parse_border_right_color(
538 input: &str,
539) -> Result<StyleBorderRightColor, CssColorParseError<'_>> {
540 parse_css_color(input).map(|inner| StyleBorderRightColor { inner })
541}
542#[cfg(feature = "parser")]
543pub fn parse_border_bottom_color(
547 input: &str,
548) -> Result<StyleBorderBottomColor, CssColorParseError<'_>> {
549 parse_css_color(input).map(|inner| StyleBorderBottomColor { inner })
550}
551#[cfg(feature = "parser")]
552pub fn parse_border_left_color(
556 input: &str,
557) -> Result<StyleBorderLeftColor, CssColorParseError<'_>> {
558 parse_css_color(input).map(|inner| StyleBorderLeftColor { inner })
559}
560
561#[cfg(feature = "parser")]
565#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
566pub struct StyleBorderColors {
567 pub top: ColorU,
568 pub right: ColorU,
569 pub bottom: ColorU,
570 pub left: ColorU,
571}
572
573#[cfg(feature = "parser")]
579pub fn parse_style_border_color(
583 input: &str,
584) -> Result<StyleBorderColors, CssColorParseError<'_>> {
585 let input = input.trim();
586 let parts: Vec<&str> = input.split_whitespace().collect();
587
588 match parts.len() {
589 1 => {
590 let color = parse_css_color(parts[0])?;
591 Ok(StyleBorderColors {
592 top: color,
593 right: color,
594 bottom: color,
595 left: color,
596 })
597 }
598 2 => {
599 let top_bottom = parse_css_color(parts[0])?;
600 let left_right = parse_css_color(parts[1])?;
601 Ok(StyleBorderColors {
602 top: top_bottom,
603 right: left_right,
604 bottom: top_bottom,
605 left: left_right,
606 })
607 }
608 3 => {
609 let top = parse_css_color(parts[0])?;
610 let left_right = parse_css_color(parts[1])?;
611 let bottom = parse_css_color(parts[2])?;
612 Ok(StyleBorderColors {
613 top,
614 right: left_right,
615 bottom,
616 left: left_right,
617 })
618 }
619 4 => {
620 let top = parse_css_color(parts[0])?;
621 let right = parse_css_color(parts[1])?;
622 let bottom = parse_css_color(parts[2])?;
623 let left = parse_css_color(parts[3])?;
624 Ok(StyleBorderColors {
625 top,
626 right,
627 bottom,
628 left,
629 })
630 }
631 _ => Err(CssColorParseError::InvalidColor(input)),
632 }
633}
634
635#[cfg(feature = "parser")]
639#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
640pub struct StyleBorderStyles {
641 pub top: BorderStyle,
642 pub right: BorderStyle,
643 pub bottom: BorderStyle,
644 pub left: BorderStyle,
645}
646
647#[cfg(feature = "parser")]
649pub fn parse_style_border_style(
653 input: &str,
654) -> Result<StyleBorderStyles, CssBorderStyleParseError<'_>> {
655 let input = input.trim();
656 let parts: Vec<&str> = input.split_whitespace().collect();
657
658 match parts.len() {
659 1 => {
660 let style = parse_border_style(parts[0])?;
661 Ok(StyleBorderStyles {
662 top: style,
663 right: style,
664 bottom: style,
665 left: style,
666 })
667 }
668 2 => {
669 let top_bottom = parse_border_style(parts[0])?;
670 let left_right = parse_border_style(parts[1])?;
671 Ok(StyleBorderStyles {
672 top: top_bottom,
673 right: left_right,
674 bottom: top_bottom,
675 left: left_right,
676 })
677 }
678 3 => {
679 let top = parse_border_style(parts[0])?;
680 let left_right = parse_border_style(parts[1])?;
681 let bottom = parse_border_style(parts[2])?;
682 Ok(StyleBorderStyles {
683 top,
684 right: left_right,
685 bottom,
686 left: left_right,
687 })
688 }
689 4 => {
690 let top = parse_border_style(parts[0])?;
691 let right = parse_border_style(parts[1])?;
692 let bottom = parse_border_style(parts[2])?;
693 let left = parse_border_style(parts[3])?;
694 Ok(StyleBorderStyles {
695 top,
696 right,
697 bottom,
698 left,
699 })
700 }
701 _ => Err(CssBorderStyleParseError::InvalidStyle(input)),
702 }
703}
704
705#[cfg(feature = "parser")]
709#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
710pub struct StyleBorderWidths {
711 pub top: PixelValue,
712 pub right: PixelValue,
713 pub bottom: PixelValue,
714 pub left: PixelValue,
715}
716
717#[cfg(feature = "parser")]
719pub fn parse_style_border_width(
723 input: &str,
724) -> Result<StyleBorderWidths, CssPixelValueParseError<'_>> {
725 let input = input.trim();
726 let parts: Vec<&str> = input.split_whitespace().collect();
727
728 match parts.len() {
729 1 => {
730 let width = parse_pixel_value(parts[0])?;
731 Ok(StyleBorderWidths {
732 top: width,
733 right: width,
734 bottom: width,
735 left: width,
736 })
737 }
738 2 => {
739 let top_bottom = parse_pixel_value(parts[0])?;
740 let left_right = parse_pixel_value(parts[1])?;
741 Ok(StyleBorderWidths {
742 top: top_bottom,
743 right: left_right,
744 bottom: top_bottom,
745 left: left_right,
746 })
747 }
748 3 => {
749 let top = parse_pixel_value(parts[0])?;
750 let left_right = parse_pixel_value(parts[1])?;
751 let bottom = parse_pixel_value(parts[2])?;
752 Ok(StyleBorderWidths {
753 top,
754 right: left_right,
755 bottom,
756 left: left_right,
757 })
758 }
759 4 => {
760 let top = parse_pixel_value(parts[0])?;
761 let right = parse_pixel_value(parts[1])?;
762 let bottom = parse_pixel_value(parts[2])?;
763 let left = parse_pixel_value(parts[3])?;
764 Ok(StyleBorderWidths {
765 top,
766 right,
767 bottom,
768 left,
769 })
770 }
771 _ => Err(CssPixelValueParseError::InvalidPixelValue(input)),
772 }
773}
774
775#[cfg(feature = "parser")]
777pub fn parse_style_border(input: &str) -> Result<StyleBorderSide, CssBorderParseError<'_>> {
781 parse_border_side(input)
782}
783
784#[cfg(all(test, feature = "parser"))]
785mod tests {
786 use super::*;
787
788 #[test]
789 fn test_parse_border_style() {
790 assert_eq!(parse_border_style("solid").unwrap(), BorderStyle::Solid);
791 assert_eq!(parse_border_style("dotted").unwrap(), BorderStyle::Dotted);
792 assert_eq!(parse_border_style("none").unwrap(), BorderStyle::None);
793 assert_eq!(
794 parse_border_style(" dashed ").unwrap(),
795 BorderStyle::Dashed
796 );
797 assert!(parse_border_style("solidd").is_err());
798 }
799
800 #[test]
801 fn test_parse_border_side_shorthand() {
802 let result = parse_border_side("2px dotted #ff0000").unwrap();
804 assert_eq!(result.border_width, PixelValue::px(2.0));
805 assert_eq!(result.border_style, BorderStyle::Dotted);
806 assert_eq!(result.border_color, ColorU::new_rgb(255, 0, 0));
807
808 let result = parse_border_side("solid green 1em").unwrap();
810 assert_eq!(result.border_width, PixelValue::em(1.0));
811 assert_eq!(result.border_style, BorderStyle::Solid);
812 assert_eq!(result.border_color, ColorU::new_rgb(0, 128, 0));
813
814 let result = parse_border_side("ridge #f0f").unwrap();
816 assert_eq!(result.border_width, MEDIUM_BORDER_THICKNESS); assert_eq!(result.border_style, BorderStyle::Ridge);
818 assert_eq!(result.border_color, ColorU::new_rgb(255, 0, 255));
819
820 let result = parse_border_side("5pt blue").unwrap();
822 assert_eq!(result.border_width, PixelValue::pt(5.0));
823 assert_eq!(result.border_style, BorderStyle::None); assert_eq!(result.border_color, ColorU::BLUE);
825
826 let result = parse_border_side("thick double").unwrap();
828 assert_eq!(result.border_width, PixelValue::px(5.0));
829 assert_eq!(result.border_style, BorderStyle::Double);
830 assert_eq!(result.border_color, ColorU::BLACK); let result = parse_border_side("inset").unwrap();
834 assert_eq!(result.border_width, MEDIUM_BORDER_THICKNESS);
835 assert_eq!(result.border_style, BorderStyle::Inset);
836 assert_eq!(result.border_color, ColorU::BLACK);
837 }
838
839 #[test]
840 fn test_parse_border_side_invalid() {
841 assert!(parse_border_side("1px 2px solid red").is_err());
843 assert!(parse_border_side("solid dashed red").is_err());
845 assert!(parse_border_side("red blue solid").is_err());
847 assert!(parse_border_side("").is_err());
849 assert!(parse_border_side("1px unknown red").is_err());
851 }
852
853 #[test]
854 fn test_parse_longhand_border() {
855 assert_eq!(
856 parse_border_top_width("1.5em").unwrap().inner,
857 PixelValue::em(1.5)
858 );
859 assert_eq!(
860 parse_border_left_style("groove").unwrap().inner,
861 BorderStyle::Groove
862 );
863 assert_eq!(
864 parse_border_right_color("rgba(10, 20, 30, 0.5)")
865 .unwrap()
866 .inner,
867 ColorU::new(10, 20, 30, 128)
868 );
869 }
870}