Skip to main content

takumi_css/style/properties/
border.rs

1use crate::style::unexpected_token;
2use cssparser::Parser;
3
4use crate::style::{
5  BorderStyle, ColorInput, CssSyntaxKind, CssToken, FromCss, MakeComputed, ParseResult,
6  SizingContext, properties::Length,
7};
8
9/// Parsed `border` value.
10#[derive(Debug, Default, Clone, Copy, PartialEq)]
11#[non_exhaustive]
12pub struct Border {
13  /// Border width.
14  pub width: Length,
15  /// Border style.
16  pub style: BorderStyle,
17  /// Border color.
18  pub color: ColorInput,
19}
20
21impl<'i> FromCss<'i> for Border {
22  fn from_css(input: &mut Parser<'i, '_>) -> ParseResult<'i, Self> {
23    let mut width = None;
24    let mut style = None;
25    let mut color = None;
26
27    loop {
28      if input.is_exhausted() {
29        break;
30      }
31
32      if let Ok(value) = input.try_parse(Length::from_css) {
33        width = Some(value);
34        continue;
35      }
36
37      if let Ok(value) = input.try_parse(BorderStyle::from_css) {
38        style = Some(value);
39        continue;
40      }
41
42      if let Ok(value) = input.try_parse(ColorInput::from_css) {
43        color = Some(value);
44        continue;
45      }
46
47      return Err(unexpected_token!(
48        input.current_source_location(),
49        input.next()?,
50      ));
51    }
52
53    Ok(Border {
54      width: width.unwrap_or_default(),
55      style: style.unwrap_or_default(),
56      color: color.unwrap_or_default(),
57    })
58  }
59
60  const VALID_TOKENS: &'static [CssToken] = &[
61    CssToken::Syntax(CssSyntaxKind::Length),
62    CssToken::Syntax(CssSyntaxKind::BorderStyle),
63    CssToken::Syntax(CssSyntaxKind::Color),
64  ];
65}
66
67impl MakeComputed for Border {
68  fn make_computed(&mut self, sizing: &SizingContext) {
69    self.width.make_computed(sizing);
70  }
71}
72
73#[cfg(test)]
74mod tests {
75  use crate::style::Color;
76
77  use super::*;
78
79  #[test]
80  fn test_parse_border_style_solid() {
81    assert_eq!(BorderStyle::from_str("solid"), Ok(BorderStyle::Solid));
82  }
83
84  #[test]
85  fn test_parse_border_style_dashed() {
86    assert_eq!(BorderStyle::from_str("dashed"), Ok(BorderStyle::Dashed));
87  }
88
89  #[test]
90  fn test_parse_border_width_only() {
91    assert_eq!(
92      Border::from_str("10px"),
93      Ok(Border {
94        width: Length::Px(10.0),
95        style: BorderStyle::None,
96        color: ColorInput::CurrentColor,
97      })
98    );
99  }
100
101  #[test]
102  fn test_parse_border_style_only() {
103    assert_eq!(
104      Border::from_str("solid"),
105      Ok(Border {
106        width: Length::default(),
107        style: BorderStyle::Solid,
108        color: ColorInput::CurrentColor,
109      })
110    );
111  }
112
113  #[test]
114  fn test_parse_border_color_only() {
115    assert_eq!(
116      Border::from_str("red"),
117      Ok(Border {
118        width: Length::default(),
119        style: BorderStyle::None,
120        color: ColorInput::Value(Color([255, 0, 0, 255])),
121      })
122    );
123  }
124
125  #[test]
126  fn test_parse_border_width_and_style() {
127    assert_eq!(
128      Border::from_str("2px solid"),
129      Ok(Border {
130        width: Length::Px(2.0),
131        style: BorderStyle::Solid,
132        color: ColorInput::CurrentColor,
133      })
134    );
135  }
136
137  #[test]
138  fn test_parse_border_width_style_color() {
139    assert_eq!(
140      Border::from_str("2px solid red"),
141      Ok(Border {
142        width: Length::Px(2.0),
143        style: BorderStyle::Solid,
144        color: ColorInput::Value(Color([255, 0, 0, 255])),
145      })
146    );
147  }
148
149  #[test]
150  fn test_parse_border_style_width_color() {
151    assert_eq!(
152      Border::from_str("solid 2px red"),
153      Ok(Border {
154        width: Length::Px(2.0),
155        style: BorderStyle::Solid,
156        color: ColorInput::Value(Color([255, 0, 0, 255])),
157      })
158    );
159  }
160
161  #[test]
162  fn test_parse_border_color_style_width() {
163    assert_eq!(
164      Border::from_str("red solid 2px"),
165      Ok(Border {
166        width: Length::Px(2.0),
167        style: BorderStyle::Solid,
168        color: ColorInput::Value(Color([255, 0, 0, 255])),
169      })
170    );
171  }
172
173  #[test]
174  fn test_parse_border_rem_units() {
175    assert_eq!(
176      Border::from_str("1.5rem solid blue"),
177      Ok(Border {
178        width: Length::Rem(1.5),
179        style: BorderStyle::Solid,
180        color: ColorInput::Value(Color([0, 0, 255, 255])),
181      })
182    );
183  }
184
185  #[test]
186  fn test_parse_border_hex_color() {
187    assert_eq!(
188      Border::from_str("3px solid #ff0000"),
189      Ok(Border {
190        width: Length::Px(3.0),
191        style: BorderStyle::Solid,
192        color: ColorInput::Value(Color([255, 0, 0, 255])),
193      })
194    );
195  }
196
197  #[test]
198  fn test_parse_border_rgb_color() {
199    assert_eq!(
200      Border::from_str("4px solid rgb(0, 255, 0)"),
201      Ok(Border {
202        width: Length::Px(4.0),
203        style: BorderStyle::Solid,
204        color: ColorInput::Value(Color([0, 255, 0, 255])),
205      })
206    );
207  }
208
209  #[test]
210  fn test_parse_border_dashed() {
211    assert_eq!(
212      Border::from_str("2px dashed red"),
213      Ok(Border {
214        width: Length::Px(2.0),
215        style: BorderStyle::Dashed,
216        color: ColorInput::Value(Color([255, 0, 0, 255])),
217      })
218    );
219  }
220
221  #[test]
222  fn test_parse_border_invalid_color() {
223    assert!(Border::from_str("2px solid invalid-color").is_err());
224  }
225
226  #[test]
227  fn test_parse_border_empty() {
228    assert_eq!(Border::from_str(""), Ok(Border::default()));
229  }
230
231  #[test]
232  fn test_border_value_from_css() {
233    assert_eq!(
234      Border::from_str("3px solid blue"),
235      Ok(Border {
236        width: Length::Px(3.0),
237        style: BorderStyle::Solid,
238        color: ColorInput::Value(Color([0, 0, 255, 255])),
239      })
240    );
241  }
242
243  #[test]
244  fn test_border_value_from_invalid_css() {
245    assert!(Border::from_str("invalid border").is_err());
246  }
247}