Skip to content

Commit f2c16f5

Browse files
committed
borders
1 parent ff88d98 commit f2c16f5

File tree

9 files changed

+492
-34
lines changed

9 files changed

+492
-34
lines changed

src/properties/mod.rs

Lines changed: 120 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@ mod custom;
22

33
use cssparser::*;
44
use custom::*;
5-
use crate::values::{image::*, length::*};
5+
use crate::values::{image::*, length::*, border::*, rect::*, color::*};
6+
use super::values::traits::Parse;
67

78
#[derive(Debug)]
89
pub enum Property {
9-
BackgroundColor(Color),
10+
BackgroundColor(CssColor),
1011
BackgroundImage(Vec<Image>),
11-
Color(Color),
12+
Color(CssColor),
1213
Custom(CustomProperty),
1314

1415
Width(Size),
@@ -31,7 +32,48 @@ pub enum Property {
3132
InsetBlockStart(LengthPercentageOrAuto),
3233
InsetBlockEnd(LengthPercentageOrAuto),
3334
InsetInlineStart(LengthPercentageOrAuto),
34-
InsetInlineEnd(LengthPercentageOrAuto)
35+
InsetInlineEnd(LengthPercentageOrAuto),
36+
37+
BorderTopColor(CssColor),
38+
BorderBottomColor(CssColor),
39+
BorderLeftColor(CssColor),
40+
BorderRightColor(CssColor),
41+
BorderBlockStartColor(CssColor),
42+
BorderBlockEndColor(CssColor),
43+
BorderInlineStartColor(CssColor),
44+
BorderInlineEndColor(CssColor),
45+
46+
BorderTopStyle(BorderStyle),
47+
BorderBottomStyle(BorderStyle),
48+
BorderLeftStyle(BorderStyle),
49+
BorderRightStyle(BorderStyle),
50+
BorderBlockStartStyle(BorderStyle),
51+
BorderBlockEndStyle(BorderStyle),
52+
BorderInlineStartStyle(BorderStyle),
53+
BorderInlineEndStyle(BorderStyle),
54+
55+
BorderTopWidth(BorderSideWidth),
56+
BorderBottomWidth(BorderSideWidth),
57+
BorderLeftWidth(BorderSideWidth),
58+
BorderRightWidth(BorderSideWidth),
59+
BorderBlockStartWidth(BorderSideWidth),
60+
BorderBlockEndWidth(BorderSideWidth),
61+
BorderInlineStartWidth(BorderSideWidth),
62+
BorderInlineEndWidth(BorderSideWidth),
63+
64+
BorderColor(Rect<CssColor>),
65+
BorderStyle(Rect<BorderStyle>),
66+
BorderWidth(Rect<BorderSideWidth>),
67+
68+
Border(Border),
69+
BorderTop(Border),
70+
BorderBottom(Border),
71+
BorderLeft(Border),
72+
BorderRight(Border),
73+
BorderBlockStart(Border),
74+
BorderBlockEnd(Border),
75+
BorderInlineStart(Border),
76+
BorderInlineEnd(Border)
3577
}
3678

3779
impl Property {
@@ -51,9 +93,9 @@ impl Property {
5193

5294
let state = input.state();
5395
match name.as_ref() {
54-
"background-color" => property!(BackgroundColor, Color),
96+
"background-color" => property!(BackgroundColor, CssColor),
5597
"background-image" => property!(BackgroundImage, Image, true),
56-
"color" => property!(Color, Color),
98+
"color" => property!(Color, CssColor),
5799
"width" => property!(Width, Size),
58100
"height" => property!(Height, Size),
59101
"min-width" => property!(MinWidth, MinMaxSize),
@@ -74,6 +116,42 @@ impl Property {
74116
"inset-block-end" => property!(InsetBlockEnd, LengthPercentageOrAuto),
75117
"inset-inline-start" => property!(InsetInlineStart, LengthPercentageOrAuto),
76118
"inset-inline-end" => property!(InsetInlineEnd, LengthPercentageOrAuto),
119+
"border-top-color" => property!(BorderTopColor, CssColor),
120+
"border-bottom-color" => property!(BorderBottomColor, CssColor),
121+
"border-left-color" => property!(BorderLeftColor, CssColor),
122+
"border-right-color" => property!(BorderRightColor, CssColor),
123+
"border-block-start-color" => property!(BorderBlockStartColor, CssColor),
124+
"border-block-end-color" => property!(BorderBlockEndColor, CssColor),
125+
"border-inline-start-color" => property!(BorderInlineStartColor, CssColor),
126+
"border-inline-end-color" => property!(BorderInlineEndColor, CssColor),
127+
"border-top-style" => property!(BorderTopStyle, BorderStyle),
128+
"border-bottom-style" => property!(BorderBottomStyle, BorderStyle),
129+
"border-left-style" => property!(BorderLeftStyle, BorderStyle),
130+
"border-right-style" => property!(BorderRightStyle, BorderStyle),
131+
"border-block-start-style" => property!(BorderBlockStartStyle, BorderStyle),
132+
"border-block-end-style" => property!(BorderBlockEndStyle, BorderStyle),
133+
"border-inline-start-style" => property!(BorderInlineStartStyle, BorderStyle),
134+
"border-inline-end-style" => property!(BorderInlineEndStyle, BorderStyle),
135+
"border-top-width" => property!(BorderTopWidth, BorderSideWidth),
136+
"border-bottom-width" => property!(BorderBottomWidth, BorderSideWidth),
137+
"border-left-width" => property!(BorderLeftWidth, BorderSideWidth),
138+
"border-right-width" => property!(BorderRightWidth, BorderSideWidth),
139+
"border-block-start-width" => property!(BorderBlockStartWidth, BorderSideWidth),
140+
"border-block-end-width" => property!(BorderBlockEndWidth, BorderSideWidth),
141+
"border-inline-start-width" => property!(BorderInlineStartWidth, BorderSideWidth),
142+
"border-inline-end-width" => property!(BorderInlineEndWidth, BorderSideWidth),
143+
"border-color" => property!(BorderColor, Rect),
144+
"border-style" => property!(BorderStyle, Rect),
145+
"border-width" => property!(BorderWidth, Rect),
146+
"border" => property!(Border, Border),
147+
"border-top" => property!(BorderTop, Border),
148+
"border-bottom" => property!(BorderBottom, Border),
149+
"border-left" => property!(BorderLeft, Border),
150+
"border-right" => property!(BorderRight, Border),
151+
"border-block-start" => property!(BorderBlockStart, Border),
152+
"border-block-end" => property!(BorderBlockEnd, Border),
153+
"border-inline-start" => property!(BorderInlineStart, Border),
154+
"border-inline-end" => property!(BorderInlineEnd, Border),
77155
_ => {}
78156
}
79157

@@ -130,6 +208,42 @@ impl Property {
130208
InsetBlockEnd(val) => property!("inset-block-end", val),
131209
InsetInlineStart(val) => property!("inset-inline-start", val),
132210
InsetInlineEnd(val) => property!("inset-inline-end", val),
211+
BorderTopColor(val) => property!("border-top-color", val),
212+
BorderBottomColor(val) => property!("border-bottom-color", val),
213+
BorderLeftColor(val) => property!("border-left-color", val),
214+
BorderRightColor(val) => property!("border-right-color", val),
215+
BorderBlockStartColor(val) => property!("border-block-start-color", val),
216+
BorderBlockEndColor(val) => property!("border-block-end-color", val),
217+
BorderInlineStartColor(val) => property!("border-inline-start-color", val),
218+
BorderInlineEndColor(val) => property!("border-inline-end-color", val),
219+
BorderTopStyle(val) => property!("border-top-style", val),
220+
BorderBottomStyle(val) => property!("border-bottom-style", val),
221+
BorderLeftStyle(val) => property!("border-left-style", val),
222+
BorderRightStyle(val) => property!("border-right-style", val),
223+
BorderBlockStartStyle(val) => property!("border-block-start-style", val),
224+
BorderBlockEndStyle(val) => property!("border-block-end-style", val),
225+
BorderInlineStartStyle(val) => property!("border-inline-start-style", val),
226+
BorderInlineEndStyle(val) => property!("border-inline-end-style", val),
227+
BorderTopWidth(val) => property!("border-top-width", val),
228+
BorderBottomWidth(val) => property!("border-bottom-width", val),
229+
BorderLeftWidth(val) => property!("border-left-width", val),
230+
BorderRightWidth(val) => property!("border-right-width", val),
231+
BorderBlockStartWidth(val) => property!("border-block-start-width", val),
232+
BorderBlockEndWidth(val) => property!("border-block-end-width", val),
233+
BorderInlineStartWidth(val) => property!("border-inline-start-width", val),
234+
BorderInlineEndWidth(val) => property!("border-inline-end-width", val),
235+
BorderColor(val) => property!("border-color", val),
236+
BorderStyle(val) => property!("border-style", val),
237+
BorderWidth(val) => property!("border-width", val),
238+
Border(val) => property!("border", val),
239+
BorderTop(val) => property!("border-top", val),
240+
BorderBottom(val) => property!("border-bottom", val),
241+
BorderLeft(val) => property!("border-left", val),
242+
BorderRight(val) => property!("border-right", val),
243+
BorderBlockStart(val) => property!("border-block-start", val),
244+
BorderBlockEnd(val) => property!("border-block-end", val),
245+
BorderInlineStart(val) => property!("border-inline-start", val),
246+
BorderInlineEnd(val) => property!("border-inline-end", val),
133247
Custom(custom) => {
134248
dest.write_str(custom.name.as_ref())?;
135249
dest.write_str(": ")?;

src/values/border.rs

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
use super::length::*;
2+
use cssparser::*;
3+
use super::traits::Parse;
4+
use super::color::CssColor;
5+
6+
#[derive(Debug, Clone, PartialEq)]
7+
pub enum BorderSideWidth {
8+
/// `thin`
9+
Thin,
10+
/// `medium`
11+
Medium,
12+
/// `thick`
13+
Thick,
14+
/// `<length>`
15+
Length(Length),
16+
}
17+
18+
impl Parse for BorderSideWidth {
19+
fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ()>> {
20+
if let Ok(length) = input.try_parse(|i| Length::parse(i)) {
21+
return Ok(BorderSideWidth::Length(length));
22+
}
23+
let ident = input.expect_ident_cloned()?;
24+
match_ignore_ascii_case! { &ident,
25+
"thin" => Ok(BorderSideWidth::Thin),
26+
"medium" => Ok(BorderSideWidth::Medium),
27+
"thick" => Ok(BorderSideWidth::Thick),
28+
_ => return Err(input.new_error(BasicParseErrorKind::QualifiedRuleInvalid))
29+
}
30+
}
31+
}
32+
33+
impl ToCss for BorderSideWidth {
34+
fn to_css<W>(&self, dest: &mut W) -> std::fmt::Result where W: std::fmt::Write {
35+
use BorderSideWidth::*;
36+
match self {
37+
Thin => dest.write_str("thin"),
38+
Medium => dest.write_str("medium"),
39+
Thick => dest.write_str("thick"),
40+
Length(length) => length.to_css(dest)
41+
}
42+
}
43+
}
44+
45+
macro_rules! enum_property {
46+
($name: ident, $( $x: ident ),+) => {
47+
#[derive(Debug, Clone, PartialEq)]
48+
pub enum $name {
49+
$(
50+
$x,
51+
)+
52+
}
53+
54+
impl Parse for $name {
55+
fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ()>> {
56+
let ident = input.expect_ident()?;
57+
match &ident[..] {
58+
$(
59+
s if s.eq_ignore_ascii_case(stringify!($x)) => Ok($name::$x),
60+
)+
61+
_ => return Err(input.new_error(BasicParseErrorKind::QualifiedRuleInvalid))
62+
}
63+
}
64+
}
65+
66+
impl ToCss for $name {
67+
fn to_css<W>(&self, dest: &mut W) -> std::fmt::Result where W: std::fmt::Write {
68+
use $name::*;
69+
match self {
70+
$(
71+
$x => dest.write_str(&stringify!($x).to_lowercase()),
72+
)+
73+
}
74+
}
75+
}
76+
};
77+
}
78+
79+
enum_property!(BorderStyle,
80+
Hidden,
81+
None,
82+
Inset,
83+
Groove,
84+
Outset,
85+
Ridge,
86+
Dotted,
87+
Dashed,
88+
Solid,
89+
Double
90+
);
91+
92+
#[derive(Debug, Clone, PartialEq)]
93+
pub struct Border {
94+
width: BorderSideWidth,
95+
style: BorderStyle,
96+
color: CssColor
97+
}
98+
99+
impl Parse for Border {
100+
fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ()>> {
101+
// Order doesn't matter...
102+
let mut color = None;
103+
let mut style = None;
104+
let mut width = None;
105+
let mut any = false;
106+
loop {
107+
if width.is_none() {
108+
if let Ok(value) = input.try_parse(|i| BorderSideWidth::parse(i)) {
109+
width = Some(value);
110+
any = true;
111+
}
112+
}
113+
if style.is_none() {
114+
if let Ok(value) = input.try_parse(BorderStyle::parse) {
115+
style = Some(value);
116+
any = true;
117+
continue
118+
}
119+
}
120+
if color.is_none() {
121+
if let Ok(value) = input.try_parse(|i| CssColor::parse(i)) {
122+
color = Some(value);
123+
any = true;
124+
continue
125+
}
126+
}
127+
break
128+
}
129+
if any {
130+
Ok(Border {
131+
width: width.unwrap_or(BorderSideWidth::Medium),
132+
style: style.unwrap_or(BorderStyle::None),
133+
color: color.unwrap_or_else(|| CssColor::current_color())
134+
})
135+
} else {
136+
Err(input.new_error(BasicParseErrorKind::QualifiedRuleInvalid))
137+
}
138+
}
139+
}
140+
141+
impl ToCss for Border {
142+
fn to_css<W>(&self, dest: &mut W) -> std::fmt::Result where W: std::fmt::Write {
143+
if self.width != BorderSideWidth::Medium {
144+
self.width.to_css(dest)?;
145+
dest.write_str(" ")?;
146+
}
147+
if self.style != BorderStyle::None {
148+
self.style.to_css(dest)?;
149+
dest.write_str(" ")?;
150+
}
151+
if self.color != CssColor::current_color() {
152+
self.color.to_css(dest)?;
153+
}
154+
Ok(())
155+
}
156+
}

src/values/color.rs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
use cssparser::*;
2+
use super::traits::Parse;
3+
4+
#[derive(Debug, Clone, PartialEq)]
5+
pub struct CssColor(Color);
6+
7+
impl CssColor {
8+
pub fn current_color() -> CssColor {
9+
CssColor(Color::CurrentColor)
10+
}
11+
}
12+
13+
impl Parse for CssColor {
14+
fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i, ()>> {
15+
Color::parse(input)
16+
.map(CssColor)
17+
.map_err(|_| input.new_error(BasicParseErrorKind::QualifiedRuleInvalid))
18+
}
19+
}
20+
21+
impl ToCss for CssColor {
22+
fn to_css<W>(&self, dest: &mut W) -> std::fmt::Result where W: std::fmt::Write {
23+
match self.0 {
24+
Color::CurrentColor => dest.write_str("currentColor"),
25+
Color::RGBA(color) => {
26+
if color.alpha == 255 {
27+
let hex: u32 = ((color.red as u32) << 16) | ((color.green as u32) << 8) | (color.blue as u32);
28+
let compact = compact_hex(hex);
29+
if hex == expand_hex(compact) {
30+
write!(dest, "#{:03x}", compact);
31+
} else {
32+
write!(dest, "#{:06x}", hex);
33+
}
34+
} else {
35+
let hex: u32 = ((color.red as u32) << 24) | ((color.green as u32) << 16) | ((color.blue as u32) << 8) | (color.alpha as u32);
36+
let compact = compact_hex(hex);
37+
if hex == expand_hex(compact) {
38+
write!(dest, "#{:04x}", compact);
39+
} else {
40+
write!(dest, "#{:08x}", hex);
41+
}
42+
}
43+
Ok(())
44+
}
45+
}
46+
}
47+
}
48+
49+
// From esbuild: https://github.com/evanw/esbuild/blob/18e13bdfdca5cd3c7a2fae1a8bd739f8f891572c/internal/css_parser/css_decls_color.go#L218
50+
// 0xAABBCCDD => 0xABCD
51+
fn compact_hex(v: u32) -> u32 {
52+
return ((v & 0x0FF00000) >> 12) | ((v & 0x00000FF0) >> 4)
53+
}
54+
55+
// 0xABCD => 0xAABBCCDD
56+
fn expand_hex(v: u32) -> u32 {
57+
return ((v & 0xF000) << 16) | ((v & 0xFF00) << 12) | ((v & 0x0FF0) << 8) | ((v & 0x00FF) << 4) | (v & 0x000F)
58+
}

0 commit comments

Comments
 (0)