1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
use super::length::*;
use crate::style::errors::PropertyParseError;
use cssparser::{Color, Parser};

/// <https://drafts.csswg.org/css-backgrounds/#typedef-line-style>
#[derive(Copy, Clone, Parse, SpecifiedAsComputed)]
pub enum LineStyle {
    None,
    Solid,
}

#[derive(Parse)]
enum ParsedLineWidth {
    Thin,
    Medium,
    Thick,
    Other(SpecifiedLengthOrPercentage),
}

#[derive(Clone)]
pub struct SpecifiedLineWidth(pub SpecifiedLengthOrPercentage);

#[derive(Copy, Clone, FromSpecified)]
pub struct LineWidth(pub LengthOrPercentage);

impl LineWidth {
    pub const MEDIUM: Self = LineWidth(LengthOrPercentage::Length(Length { px: 3. }));

    pub fn fixup(&mut self, style: LineStyle) {
        if let LineStyle::None = style {
            self.0 = LengthOrPercentage::Length(Length::zero())
        }
    }
}

impl super::Parse for SpecifiedLineWidth {
    fn parse<'i, 't>(parser: &mut Parser<'i, 't>) -> Result<Self, PropertyParseError<'i>> {
        let px = match ParsedLineWidth::parse(parser)? {
            ParsedLineWidth::Thin => 1.0,
            ParsedLineWidth::Medium => 3.0,
            ParsedLineWidth::Thick => 5.0,
            ParsedLineWidth::Other(value) => return Ok(SpecifiedLineWidth(value)),
        };
        Ok(SpecifiedLineWidth(
            SpecifiedLength::Absolute(Length { px }).into(),
        ))
    }
}

macro_rules! parse_one_or_more {
    ($type: ty { $( $field: ident, )+ }) => {
        impl crate::style::values::Parse for BorderSide {
            fn parse<'i, 't>(parser: &mut Parser<'i, 't>)
                -> Result<Self, PropertyParseError<'i>>
            {
                let mut values = Self::default();
                let mut any = false;
                loop {
                    $(
                        if values.$field.is_none() {
                            if let Ok(value) = parser.r#try(crate::style::values::Parse::parse) {
                                values.$field = Some(value);
                                any = true;
                                continue
                            }
                        }
                    )+
                    break
                }
                if any {
                    Ok(values)
                } else {
                    Err(parser.new_error_for_next_token())
                }
            }
        }
    };
}

parse_one_or_more!(BorderSide {
    style,
    color,
    width,
});

#[derive(Default)]
pub struct BorderSide {
    pub style: Option<LineStyle>,
    pub color: Option<Color>,
    pub width: Option<SpecifiedLineWidth>,
}