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 pub fn interpolate(&self, other: &Self, t: f32) -> Self {
113 Self {
114 inner: self.inner.interpolate(&other.inner, t),
115 }
116 }
117 }
118 };
119 ($struct_name:ident,PixelValue, $default:expr) => {
121 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
122 #[repr(C)]
123 pub struct $struct_name {
124 pub inner: PixelValue,
125 }
126 impl ::core::fmt::Debug for $struct_name {
127 fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
128 write!(f, "{}", self.inner)
129 }
130 }
131 impl Default for $struct_name {
132 fn default() -> Self {
133 Self { inner: $default }
134 }
135 }
136 impl_pixel_value!($struct_name);
137 impl PixelValueTaker for $struct_name {
138 fn from_pixel_value(inner: PixelValue) -> Self {
139 Self { inner }
140 }
141 }
142 };
143}
144
145define_border_side_property!(StyleBorderTopStyle, BorderStyle, BorderStyle::None);
151define_border_side_property!(StyleBorderRightStyle, BorderStyle, BorderStyle::None);
153define_border_side_property!(StyleBorderBottomStyle, BorderStyle, BorderStyle::None);
155define_border_side_property!(StyleBorderLeftStyle, BorderStyle, BorderStyle::None);
157
158impl crate::format_rust_code::FormatAsRustCode for StyleBorderTopStyle {
160 fn format_as_rust_code(&self, tabs: usize) -> String {
161 format!(
162 "StyleBorderTopStyle {{ inner: {} }}",
163 &self.inner.format_as_rust_code(tabs)
164 )
165 }
166}
167
168impl crate::format_rust_code::FormatAsRustCode for StyleBorderRightStyle {
169 fn format_as_rust_code(&self, tabs: usize) -> String {
170 format!(
171 "StyleBorderRightStyle {{ inner: {} }}",
172 &self.inner.format_as_rust_code(tabs)
173 )
174 }
175}
176
177impl crate::format_rust_code::FormatAsRustCode for StyleBorderLeftStyle {
178 fn format_as_rust_code(&self, tabs: usize) -> String {
179 format!(
180 "StyleBorderLeftStyle {{ inner: {} }}",
181 &self.inner.format_as_rust_code(tabs)
182 )
183 }
184}
185
186impl crate::format_rust_code::FormatAsRustCode for StyleBorderBottomStyle {
187 fn format_as_rust_code(&self, tabs: usize) -> String {
188 format!(
189 "StyleBorderBottomStyle {{ inner: {} }}",
190 &self.inner.format_as_rust_code(tabs)
191 )
192 }
193}
194
195define_border_side_property!(StyleBorderTopColor, ColorU);
198define_border_side_property!(StyleBorderRightColor, ColorU);
200define_border_side_property!(StyleBorderBottomColor, ColorU);
202define_border_side_property!(StyleBorderLeftColor, ColorU);
204
205define_border_side_property!(LayoutBorderTopWidth, PixelValue, MEDIUM_BORDER_THICKNESS);
210define_border_side_property!(LayoutBorderRightWidth, PixelValue, MEDIUM_BORDER_THICKNESS);
212define_border_side_property!(LayoutBorderBottomWidth, PixelValue, MEDIUM_BORDER_THICKNESS);
214define_border_side_property!(LayoutBorderLeftWidth, PixelValue, MEDIUM_BORDER_THICKNESS);
216
217macro_rules! impl_border_width_helpers {
218 ($($t:ty),+) => { $(
219 impl $t {
220 pub fn interpolate(&self, other: &Self, t: f32) -> Self {
221 Self { inner: self.inner.interpolate(&other.inner, t) }
222 }
223 pub const fn const_px(value: isize) -> Self {
224 Self { inner: PixelValue::const_px(value) }
225 }
226 }
227 )+ };
228}
229
230impl_border_width_helpers!(
231 LayoutBorderTopWidth,
232 LayoutBorderRightWidth,
233 LayoutBorderBottomWidth,
234 LayoutBorderLeftWidth
235);
236
237#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
240pub struct StyleBorderSide {
241 pub border_width: PixelValue,
242 pub border_style: BorderStyle,
243 pub border_color: ColorU,
244}
245
246#[cfg(feature = "parser")]
251#[derive(Clone, PartialEq)]
252pub enum CssBorderStyleParseError<'a> {
253 InvalidStyle(&'a str),
254}
255
256#[cfg(feature = "parser")]
257impl_debug_as_display!(CssBorderStyleParseError<'a>);
258#[cfg(feature = "parser")]
259impl_display! { CssBorderStyleParseError<'a>, {
260 InvalidStyle(val) => format!("Invalid border style: \"{}\"", val),
261}}
262
263#[cfg(feature = "parser")]
264#[derive(Debug, Clone, PartialEq)]
265#[repr(C, u8)]
266pub enum CssBorderStyleParseErrorOwned {
267 InvalidStyle(AzString),
268}
269
270#[cfg(feature = "parser")]
271impl<'a> CssBorderStyleParseError<'a> {
272 pub fn to_contained(&self) -> CssBorderStyleParseErrorOwned {
273 match self {
274 CssBorderStyleParseError::InvalidStyle(s) => {
275 CssBorderStyleParseErrorOwned::InvalidStyle(s.to_string().into())
276 }
277 }
278 }
279}
280
281#[cfg(feature = "parser")]
282impl CssBorderStyleParseErrorOwned {
283 pub fn to_shared<'a>(&'a self) -> CssBorderStyleParseError<'a> {
284 match self {
285 CssBorderStyleParseErrorOwned::InvalidStyle(s) => {
286 CssBorderStyleParseError::InvalidStyle(s.as_str())
287 }
288 }
289 }
290}
291
292#[cfg(feature = "parser")]
293pub fn parse_border_style<'a>(input: &'a str) -> Result<BorderStyle, CssBorderStyleParseError<'a>> {
294 match input.trim() {
295 "none" => Ok(BorderStyle::None),
296 "solid" => Ok(BorderStyle::Solid),
297 "double" => Ok(BorderStyle::Double),
298 "dotted" => Ok(BorderStyle::Dotted),
299 "dashed" => Ok(BorderStyle::Dashed),
300 "hidden" => Ok(BorderStyle::Hidden),
301 "groove" => Ok(BorderStyle::Groove),
302 "ridge" => Ok(BorderStyle::Ridge),
303 "inset" => Ok(BorderStyle::Inset),
304 "outset" => Ok(BorderStyle::Outset),
305 _ => Err(CssBorderStyleParseError::InvalidStyle(input)),
306 }
307}
308
309#[cfg(feature = "parser")]
312#[derive(Clone, PartialEq)]
313pub enum CssBorderSideParseError<'a> {
314 InvalidDeclaration(&'a str),
315 Width(CssPixelValueParseError<'a>),
316 Style(CssBorderStyleParseError<'a>),
317 Color(CssColorParseError<'a>),
318}
319
320#[cfg(feature = "parser")]
321impl_debug_as_display!(CssBorderSideParseError<'a>);
322#[cfg(feature = "parser")]
323impl_display! { CssBorderSideParseError<'a>, {
324 InvalidDeclaration(e) => format!("Invalid border declaration: \"{}\"", e),
325 Width(e) => format!("Invalid border-width component: {}", e),
326 Style(e) => format!("Invalid border-style component: {}", e),
327 Color(e) => format!("Invalid border-color component: {}", e),
328}}
329
330#[cfg(feature = "parser")]
331impl_from!(CssPixelValueParseError<'a>, CssBorderSideParseError::Width);
332#[cfg(feature = "parser")]
333impl_from!(CssBorderStyleParseError<'a>, CssBorderSideParseError::Style);
334#[cfg(feature = "parser")]
335impl_from!(CssColorParseError<'a>, CssBorderSideParseError::Color);
336
337#[cfg(feature = "parser")]
338#[derive(Debug, Clone, PartialEq)]
339#[repr(C, u8)]
340pub enum CssBorderSideParseErrorOwned {
341 InvalidDeclaration(AzString),
342 Width(CssPixelValueParseErrorOwned),
343 Style(CssBorderStyleParseErrorOwned),
344 Color(CssColorParseErrorOwned),
345}
346
347#[cfg(feature = "parser")]
348impl<'a> CssBorderSideParseError<'a> {
349 pub fn to_contained(&self) -> CssBorderSideParseErrorOwned {
350 match self {
351 CssBorderSideParseError::InvalidDeclaration(s) => {
352 CssBorderSideParseErrorOwned::InvalidDeclaration(s.to_string().into())
353 }
354 CssBorderSideParseError::Width(e) => {
355 CssBorderSideParseErrorOwned::Width(e.to_contained())
356 }
357 CssBorderSideParseError::Style(e) => {
358 CssBorderSideParseErrorOwned::Style(e.to_contained())
359 }
360 CssBorderSideParseError::Color(e) => {
361 CssBorderSideParseErrorOwned::Color(e.to_contained())
362 }
363 }
364 }
365}
366
367#[cfg(feature = "parser")]
368impl CssBorderSideParseErrorOwned {
369 pub fn to_shared<'a>(&'a self) -> CssBorderSideParseError<'a> {
370 match self {
371 CssBorderSideParseErrorOwned::InvalidDeclaration(s) => {
372 CssBorderSideParseError::InvalidDeclaration(s.as_str())
373 }
374 CssBorderSideParseErrorOwned::Width(e) => CssBorderSideParseError::Width(e.to_shared()),
375 CssBorderSideParseErrorOwned::Style(e) => CssBorderSideParseError::Style(e.to_shared()),
376 CssBorderSideParseErrorOwned::Color(e) => CssBorderSideParseError::Color(e.to_shared()),
377 }
378 }
379}
380
381#[cfg(feature = "parser")]
383pub type CssBorderParseError<'a> = CssBorderSideParseError<'a>;
384
385#[cfg(feature = "parser")]
387#[derive(Debug, Clone, PartialEq)]
388#[repr(C)]
389pub struct CssBorderParseErrorOwned {
390 pub inner: CssBorderSideParseErrorOwned,
391}
392
393#[cfg(feature = "parser")]
394impl From<CssBorderSideParseErrorOwned> for CssBorderParseErrorOwned {
395 fn from(v: CssBorderSideParseErrorOwned) -> Self {
396 Self { inner: v }
397 }
398}
399
400#[cfg(feature = "parser")]
403fn parse_border_side<'a>(
404 input: &'a str,
405) -> Result<StyleBorderSide, CssBorderSideParseError<'a>> {
406 let mut width = None;
407 let mut style = None;
408 let mut color = None;
409
410 if input.trim().is_empty() {
411 return Err(CssBorderSideParseError::InvalidDeclaration(input));
412 }
413
414 for part in input.split_whitespace() {
415 if width.is_none() {
417 if let Ok(w) = parse_border_width_value(part) {
418 width = Some(w);
419 continue;
420 }
421 }
422
423 if style.is_none() {
425 if let Ok(s) = parse_border_style(part) {
426 style = Some(s);
427 continue;
428 }
429 }
430
431 if color.is_none() {
433 if let Ok(c) = parse_css_color(part) {
434 color = Some(c);
435 continue;
436 }
437 }
438
439 return Err(CssBorderSideParseError::InvalidDeclaration(input));
441 }
442
443 Ok(StyleBorderSide {
444 border_width: width.unwrap_or(MEDIUM_BORDER_THICKNESS),
445 border_style: style.unwrap_or(BorderStyle::None),
446 border_color: color.unwrap_or(ColorU::BLACK),
447 })
448}
449
450#[cfg(feature = "parser")]
453fn parse_border_width_value<'a>(
454 input: &'a str,
455) -> Result<PixelValue, CssPixelValueParseError<'a>> {
456 match input.trim() {
457 "thin" => Ok(THIN_BORDER_THICKNESS),
458 "medium" => Ok(MEDIUM_BORDER_THICKNESS),
459 "thick" => Ok(THICK_BORDER_THICKNESS),
460 _ => parse_pixel_value(input),
461 }
462}
463
464#[cfg(feature = "parser")]
465pub fn parse_border_top_width<'a>(
466 input: &'a str,
467) -> Result<LayoutBorderTopWidth, CssPixelValueParseError<'a>> {
468 parse_border_width_value(input).map(|inner| LayoutBorderTopWidth { inner })
469}
470
471#[cfg(feature = "parser")]
472pub fn parse_border_right_width<'a>(
473 input: &'a str,
474) -> Result<LayoutBorderRightWidth, CssPixelValueParseError<'a>> {
475 parse_border_width_value(input).map(|inner| LayoutBorderRightWidth { inner })
476}
477
478#[cfg(feature = "parser")]
479pub fn parse_border_bottom_width<'a>(
480 input: &'a str,
481) -> Result<LayoutBorderBottomWidth, CssPixelValueParseError<'a>> {
482 parse_border_width_value(input).map(|inner| LayoutBorderBottomWidth { inner })
483}
484
485#[cfg(feature = "parser")]
486pub fn parse_border_left_width<'a>(
487 input: &'a str,
488) -> Result<LayoutBorderLeftWidth, CssPixelValueParseError<'a>> {
489 parse_border_width_value(input).map(|inner| LayoutBorderLeftWidth { inner })
490}
491
492#[cfg(feature = "parser")]
493pub fn parse_border_top_style<'a>(
494 input: &'a str,
495) -> Result<StyleBorderTopStyle, CssBorderStyleParseError<'a>> {
496 parse_border_style(input).map(|inner| StyleBorderTopStyle { inner })
497}
498#[cfg(feature = "parser")]
499pub fn parse_border_right_style<'a>(
500 input: &'a str,
501) -> Result<StyleBorderRightStyle, CssBorderStyleParseError<'a>> {
502 parse_border_style(input).map(|inner| StyleBorderRightStyle { inner })
503}
504#[cfg(feature = "parser")]
505pub fn parse_border_bottom_style<'a>(
506 input: &'a str,
507) -> Result<StyleBorderBottomStyle, CssBorderStyleParseError<'a>> {
508 parse_border_style(input).map(|inner| StyleBorderBottomStyle { inner })
509}
510#[cfg(feature = "parser")]
511pub fn parse_border_left_style<'a>(
512 input: &'a str,
513) -> Result<StyleBorderLeftStyle, CssBorderStyleParseError<'a>> {
514 parse_border_style(input).map(|inner| StyleBorderLeftStyle { inner })
515}
516
517#[cfg(feature = "parser")]
518pub fn parse_border_top_color<'a>(
519 input: &'a str,
520) -> Result<StyleBorderTopColor, CssColorParseError<'a>> {
521 parse_css_color(input).map(|inner| StyleBorderTopColor { inner })
522}
523#[cfg(feature = "parser")]
524pub fn parse_border_right_color<'a>(
525 input: &'a str,
526) -> Result<StyleBorderRightColor, CssColorParseError<'a>> {
527 parse_css_color(input).map(|inner| StyleBorderRightColor { inner })
528}
529#[cfg(feature = "parser")]
530pub fn parse_border_bottom_color<'a>(
531 input: &'a str,
532) -> Result<StyleBorderBottomColor, CssColorParseError<'a>> {
533 parse_css_color(input).map(|inner| StyleBorderBottomColor { inner })
534}
535#[cfg(feature = "parser")]
536pub fn parse_border_left_color<'a>(
537 input: &'a str,
538) -> Result<StyleBorderLeftColor, CssColorParseError<'a>> {
539 parse_css_color(input).map(|inner| StyleBorderLeftColor { inner })
540}
541
542#[cfg(feature = "parser")]
546#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
547pub struct StyleBorderColors {
548 pub top: ColorU,
549 pub right: ColorU,
550 pub bottom: ColorU,
551 pub left: ColorU,
552}
553
554#[cfg(feature = "parser")]
560pub fn parse_style_border_color<'a>(
561 input: &'a str,
562) -> Result<StyleBorderColors, CssColorParseError<'a>> {
563 let input = input.trim();
564 let parts: Vec<&str> = input.split_whitespace().collect();
565
566 match parts.len() {
567 1 => {
568 let color = parse_css_color(parts[0])?;
569 Ok(StyleBorderColors {
570 top: color,
571 right: color,
572 bottom: color,
573 left: color,
574 })
575 }
576 2 => {
577 let top_bottom = parse_css_color(parts[0])?;
578 let left_right = parse_css_color(parts[1])?;
579 Ok(StyleBorderColors {
580 top: top_bottom,
581 right: left_right,
582 bottom: top_bottom,
583 left: left_right,
584 })
585 }
586 3 => {
587 let top = parse_css_color(parts[0])?;
588 let left_right = parse_css_color(parts[1])?;
589 let bottom = parse_css_color(parts[2])?;
590 Ok(StyleBorderColors {
591 top,
592 right: left_right,
593 bottom,
594 left: left_right,
595 })
596 }
597 4 => {
598 let top = parse_css_color(parts[0])?;
599 let right = parse_css_color(parts[1])?;
600 let bottom = parse_css_color(parts[2])?;
601 let left = parse_css_color(parts[3])?;
602 Ok(StyleBorderColors {
603 top,
604 right,
605 bottom,
606 left,
607 })
608 }
609 _ => Err(CssColorParseError::InvalidColor(input)),
610 }
611}
612
613#[cfg(feature = "parser")]
617#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
618pub struct StyleBorderStyles {
619 pub top: BorderStyle,
620 pub right: BorderStyle,
621 pub bottom: BorderStyle,
622 pub left: BorderStyle,
623}
624
625#[cfg(feature = "parser")]
627pub fn parse_style_border_style<'a>(
628 input: &'a str,
629) -> Result<StyleBorderStyles, CssBorderStyleParseError<'a>> {
630 let input = input.trim();
631 let parts: Vec<&str> = input.split_whitespace().collect();
632
633 match parts.len() {
634 1 => {
635 let style = parse_border_style(parts[0])?;
636 Ok(StyleBorderStyles {
637 top: style,
638 right: style,
639 bottom: style,
640 left: style,
641 })
642 }
643 2 => {
644 let top_bottom = parse_border_style(parts[0])?;
645 let left_right = parse_border_style(parts[1])?;
646 Ok(StyleBorderStyles {
647 top: top_bottom,
648 right: left_right,
649 bottom: top_bottom,
650 left: left_right,
651 })
652 }
653 3 => {
654 let top = parse_border_style(parts[0])?;
655 let left_right = parse_border_style(parts[1])?;
656 let bottom = parse_border_style(parts[2])?;
657 Ok(StyleBorderStyles {
658 top,
659 right: left_right,
660 bottom,
661 left: left_right,
662 })
663 }
664 4 => {
665 let top = parse_border_style(parts[0])?;
666 let right = parse_border_style(parts[1])?;
667 let bottom = parse_border_style(parts[2])?;
668 let left = parse_border_style(parts[3])?;
669 Ok(StyleBorderStyles {
670 top,
671 right,
672 bottom,
673 left,
674 })
675 }
676 _ => Err(CssBorderStyleParseError::InvalidStyle(input)),
677 }
678}
679
680#[cfg(feature = "parser")]
684#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
685pub struct StyleBorderWidths {
686 pub top: PixelValue,
687 pub right: PixelValue,
688 pub bottom: PixelValue,
689 pub left: PixelValue,
690}
691
692#[cfg(feature = "parser")]
694pub fn parse_style_border_width<'a>(
695 input: &'a str,
696) -> Result<StyleBorderWidths, CssPixelValueParseError<'a>> {
697 let input = input.trim();
698 let parts: Vec<&str> = input.split_whitespace().collect();
699
700 match parts.len() {
701 1 => {
702 let width = parse_pixel_value(parts[0])?;
703 Ok(StyleBorderWidths {
704 top: width,
705 right: width,
706 bottom: width,
707 left: width,
708 })
709 }
710 2 => {
711 let top_bottom = parse_pixel_value(parts[0])?;
712 let left_right = parse_pixel_value(parts[1])?;
713 Ok(StyleBorderWidths {
714 top: top_bottom,
715 right: left_right,
716 bottom: top_bottom,
717 left: left_right,
718 })
719 }
720 3 => {
721 let top = parse_pixel_value(parts[0])?;
722 let left_right = parse_pixel_value(parts[1])?;
723 let bottom = parse_pixel_value(parts[2])?;
724 Ok(StyleBorderWidths {
725 top,
726 right: left_right,
727 bottom,
728 left: left_right,
729 })
730 }
731 4 => {
732 let top = parse_pixel_value(parts[0])?;
733 let right = parse_pixel_value(parts[1])?;
734 let bottom = parse_pixel_value(parts[2])?;
735 let left = parse_pixel_value(parts[3])?;
736 Ok(StyleBorderWidths {
737 top,
738 right,
739 bottom,
740 left,
741 })
742 }
743 _ => Err(CssPixelValueParseError::InvalidPixelValue(input)),
744 }
745}
746
747#[cfg(feature = "parser")]
749pub fn parse_style_border<'a>(input: &'a str) -> Result<StyleBorderSide, CssBorderParseError<'a>> {
750 parse_border_side(input)
751}
752
753#[cfg(all(test, feature = "parser"))]
754mod tests {
755 use super::*;
756
757 #[test]
758 fn test_parse_border_style() {
759 assert_eq!(parse_border_style("solid").unwrap(), BorderStyle::Solid);
760 assert_eq!(parse_border_style("dotted").unwrap(), BorderStyle::Dotted);
761 assert_eq!(parse_border_style("none").unwrap(), BorderStyle::None);
762 assert_eq!(
763 parse_border_style(" dashed ").unwrap(),
764 BorderStyle::Dashed
765 );
766 assert!(parse_border_style("solidd").is_err());
767 }
768
769 #[test]
770 fn test_parse_border_side_shorthand() {
771 let result = parse_border_side("2px dotted #ff0000").unwrap();
773 assert_eq!(result.border_width, PixelValue::px(2.0));
774 assert_eq!(result.border_style, BorderStyle::Dotted);
775 assert_eq!(result.border_color, ColorU::new_rgb(255, 0, 0));
776
777 let result = parse_border_side("solid green 1em").unwrap();
779 assert_eq!(result.border_width, PixelValue::em(1.0));
780 assert_eq!(result.border_style, BorderStyle::Solid);
781 assert_eq!(result.border_color, ColorU::new_rgb(0, 128, 0));
782
783 let result = parse_border_side("ridge #f0f").unwrap();
785 assert_eq!(result.border_width, MEDIUM_BORDER_THICKNESS); assert_eq!(result.border_style, BorderStyle::Ridge);
787 assert_eq!(result.border_color, ColorU::new_rgb(255, 0, 255));
788
789 let result = parse_border_side("5pt blue").unwrap();
791 assert_eq!(result.border_width, PixelValue::pt(5.0));
792 assert_eq!(result.border_style, BorderStyle::None); assert_eq!(result.border_color, ColorU::BLUE);
794
795 let result = parse_border_side("thick double").unwrap();
797 assert_eq!(result.border_width, PixelValue::px(5.0));
798 assert_eq!(result.border_style, BorderStyle::Double);
799 assert_eq!(result.border_color, ColorU::BLACK); let result = parse_border_side("inset").unwrap();
803 assert_eq!(result.border_width, MEDIUM_BORDER_THICKNESS);
804 assert_eq!(result.border_style, BorderStyle::Inset);
805 assert_eq!(result.border_color, ColorU::BLACK);
806 }
807
808 #[test]
809 fn test_parse_border_side_invalid() {
810 assert!(parse_border_side("1px 2px solid red").is_err());
812 assert!(parse_border_side("solid dashed red").is_err());
814 assert!(parse_border_side("red blue solid").is_err());
816 assert!(parse_border_side("").is_err());
818 assert!(parse_border_side("1px unknown red").is_err());
820 }
821
822 #[test]
823 fn test_parse_longhand_border() {
824 assert_eq!(
825 parse_border_top_width("1.5em").unwrap().inner,
826 PixelValue::em(1.5)
827 );
828 assert_eq!(
829 parse_border_left_style("groove").unwrap().inner,
830 BorderStyle::Groove
831 );
832 assert_eq!(
833 parse_border_right_color("rgba(10, 20, 30, 0.5)")
834 .unwrap()
835 .inner,
836 ColorU::new(10, 20, 30, 128)
837 );
838 }
839}