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