Parse more css

This commit is contained in:
Adrian Woźniak 2020-04-24 18:17:47 +02:00 committed by Adrian Wozniak
parent 16b1a3c669
commit 089bdfd951
2 changed files with 199 additions and 86 deletions

View File

@ -157,7 +157,7 @@ impl CssParser {
self.skip_white(); self.skip_white();
self.consume_expected(":")?; self.consume_expected(":")?;
self.skip_white(); self.skip_white();
let p = self.parse_expected::<AlignContentProperty>()?; let p = self.parse_expected_prop_value::<AlignContentProperty>()?;
self.consume_expected(";")?; self.consume_expected(";")?;
Property::AlignContent(p) Property::AlignContent(p)
} }
@ -165,7 +165,7 @@ impl CssParser {
self.skip_white(); self.skip_white();
self.consume_expected(":")?; self.consume_expected(":")?;
self.skip_white(); self.skip_white();
let p = self.parse_expected::<AlignItemsProperty>()?; let p = self.parse_expected_prop_value::<AlignItemsProperty>()?;
self.consume_expected(";")?; self.consume_expected(";")?;
Property::AlignItems(p) Property::AlignItems(p)
} }
@ -173,7 +173,7 @@ impl CssParser {
self.skip_white(); self.skip_white();
self.consume_expected(":")?; self.consume_expected(":")?;
self.skip_white(); self.skip_white();
let p = self.parse_expected::<AlignSelfProperty>()?; let p = self.parse_expected_prop_value::<AlignSelfProperty>()?;
self.consume_expected(";")?; self.consume_expected(";")?;
Property::AlignSelf(p) Property::AlignSelf(p)
} }
@ -181,7 +181,7 @@ impl CssParser {
self.skip_white(); self.skip_white();
self.consume_expected(":")?; self.consume_expected(":")?;
self.skip_white(); self.skip_white();
let p = self.parse_expected::<AllProperty>()?; let p = self.parse_expected_prop_value::<AllProperty>()?;
self.consume_expected(";")?; self.consume_expected(";")?;
Property::All(p) Property::All(p)
} }
@ -191,35 +191,38 @@ impl CssParser {
self.skip_white(); self.skip_white();
let def = self.expect_consume()?; let def = self.expect_consume()?;
let p = match def.as_str() { let p = match def.as_str() {
"initial" => AnimationProperty::Initial, "initial" => PropertyValue::Other(AnimationProperty::Initial),
"inherit" => AnimationProperty::Inherit, "inherit" => PropertyValue::Other(AnimationProperty::Inherit),
_ if def.starts_with("--") => PropertyValue::Variable(def[2..].to_string()),
_ => { _ => {
let name = def; let name = def;
self.skip_white(); self.skip_white();
let duration = if self.current_is_semicolon() { let duration = if self.current_is_semicolon() {
TimeProperty::Seconds(0) PropertyValue::Other(TimeProperty::Seconds(0))
} else { } else {
let v = self.expect_consume()?.parse::<TimeProperty>()?; let v = self.parse_expected_prop_value::<TimeProperty>()?;
self.skip_white(); self.skip_white();
v v
}; };
let timing = if self.current_is_semicolon() { let timing = if self.current_is_semicolon() {
AnimationTimingFunction::Ease PropertyValue::Other(AnimationTimingFunction::Ease)
} else { } else {
let v = self.expect_consume()?.parse::<AnimationTimingFunction>()?; let v = self.parse_expected_prop_value::<AnimationTimingFunction>()?;
self.skip_white(); self.skip_white();
v v
}; };
let delay = if self.current_is_semicolon() { let delay = if self.current_is_semicolon() {
AnimationDelayProperty::Time(TimeProperty::Seconds(0)) PropertyValue::Other(AnimationDelayProperty::Time(
TimeProperty::Seconds(0),
))
} else { } else {
let v = self.expect_consume()?.parse::<AnimationDelayProperty>()?; let v = self.parse_expected_prop_value::<AnimationDelayProperty>()?;
self.skip_white(); self.skip_white();
v v
}; };
let iteration_count = if self.current_is_semicolon() { let iteration_count = if self.current_is_semicolon() {
1 PropertyValue::Other(1)
} else { } else {
let count = self.expect_consume()?; let count = self.expect_consume()?;
let v = count.parse::<usize>().map_err(|_| { let v = count.parse::<usize>().map_err(|_| {
@ -229,37 +232,34 @@ impl CssParser {
) )
})?; })?;
self.skip_white(); self.skip_white();
v PropertyValue::Other(v)
}; };
let direction = if self.current_is_semicolon() { let direction = if self.current_is_semicolon() {
AnimationDirectionProperty::Normal PropertyValue::Other(AnimationDirectionProperty::Normal)
} else { } else {
let v = self let v =
.expect_consume()? self.parse_expected_prop_value::<AnimationDirectionProperty>()?;
.parse::<AnimationDirectionProperty>()?;
self.skip_white(); self.skip_white();
v v
}; };
let fill_mode = if self.current_is_semicolon() { let fill_mode = if self.current_is_semicolon() {
AnimationFillModeProperty::None PropertyValue::Other(AnimationFillModeProperty::None)
} else { } else {
let v = self let v =
.expect_consume()? self.parse_expected_prop_value::<AnimationFillModeProperty>()?;
.parse::<AnimationFillModeProperty>()?;
self.skip_white(); self.skip_white();
v v
}; };
let play_state = if self.current_is_semicolon() { let play_state = if self.current_is_semicolon() {
AnimationPlayStateProperty::Running PropertyValue::Other(AnimationPlayStateProperty::Running)
} else { } else {
let v = self let v =
.expect_consume()? self.parse_expected_prop_value::<AnimationPlayStateProperty>()?;
.parse::<AnimationPlayStateProperty>()?;
self.skip_white(); self.skip_white();
v v
}; };
AnimationProperty::Custom( PropertyValue::Other(AnimationProperty::Custom(
name, name,
duration, duration,
timing, timing,
@ -268,63 +268,87 @@ impl CssParser {
direction, direction,
fill_mode, fill_mode,
play_state, play_state,
) ))
} }
}; };
self.consume_expected(";")?; self.consume_expected(";")?;
Property::Animation(p) Property::Animation(p)
} }
"animation-delay" => { "animation-delay" => {
let d = s.parse::<TimeProperty>()?; let d = self.parse_expected_prop_value::<TimeProperty>()?;
self.consume_expected(";")?; self.consume_expected(";")?;
Property::AnimationDelay(d) Property::AnimationDelay(d)
} }
"animation-direction" => { "animation-direction" => {
let p = s.parse::<AnimationDirectionProperty>()?; let p = self.parse_expected_prop_value::<AnimationDirectionProperty>()?;
self.consume_expected(";")?; self.consume_expected(";")?;
Property::AnimationDirection(p) Property::AnimationDirection(p)
} }
"animation-duration" => { "animation-duration" => {
let p = s.parse::<AnimationDirectionProperty>()?; let p = self.parse_expected_prop_value::<AnimationDirectionProperty>()?;
self.consume_expected(";")?; self.consume_expected(";")?;
Property::AnimationDuration(p) Property::AnimationDuration(p)
} }
"animation-fill-mode" => { "animation-fill-mode" => {
let p = s.parse()?; let p = self.parse_expected_prop_value()?;
self.consume_expected(";")?; self.consume_expected(";")?;
Property::AnimationFillMode(p) Property::AnimationFillMode(p)
} }
"animation-iteration-count" => { "animation-iteration-count" => {
let p = s let count = self.expect_consume()?;
.parse() let p = if count.starts_with("--") {
.map_err(|_| format!("invalid iteration count, expect number got {:?}", s))?; PropertyValue::Variable(count[2..].to_string())
} else {
PropertyValue::Other(count.parse::<usize>().map_err(|_| {
format!("invalid iteration count, expect number got {:?}", count)
})?)
};
self.consume_expected(";")?; self.consume_expected(";")?;
Property::AnimationIterationCount(p) Property::AnimationIterationCount(p)
} }
"animation-name" => { "animation-name" => {
self.consume_expected(";")?; self.consume_expected(";")?;
Property::AnimationName(s.to_string()) let name = match self.expect_consume()?.as_str() {
name @ _ if name.starts_with("--") => {
PropertyValue::Variable(name[2..].to_string())
}
name @ _ => PropertyValue::Other(name.to_string()),
};
Property::AnimationName(name)
} }
"animation-play-state" => { "animation-play-state" => {
let p = s.parse()?; let p = self.parse_expected_prop_value()?;
self.consume_expected(";")?; self.consume_expected(";")?;
Property::AnimationPlayState(p) Property::AnimationPlayState(p)
} }
"animation-timing-function" => { "animation-timing-function" => {
let p = s.parse()?; let p = self.parse_expected_prop_value()?;
self.consume_expected(";")?; self.consume_expected(";")?;
Property::AnimationTimingFunction(p) Property::AnimationTimingFunction(p)
} }
"backface-visibility" => { "backface-visibility" => {
let p = s.parse()?; let p = self.parse_expected_prop_value()?;
self.consume_expected(";")?; self.consume_expected(";")?;
Property::BackfaceVisibility(p) Property::BackfaceVisibility(p)
} }
// "background" => Property::Background, // "background" => Property::Background,
// "background-attachment" => Property::BackgroundAttachment, // "background-attachment" => Property::BackgroundAttachment,
// "background-blend-mode" => Property::BackgroundBlendMode, "background-blend-mode" => {
// "background-clip" => Property::BackgroundClip, let p = self.parse_expected_prop_value()?;
// "background-color" => Property::BackgroundColor, self.consume_expected(";")?;
Property::BackgroundBlendMode(p)
}
"background-clip" => {
let p = self.parse_expected_prop_value()?;
self.consume_expected(";")?;
Property::BackgroundClip(p)
}
"background-color" => {
let p = self.parse_expected_prop_value()?;
self.consume_expected(";")?;
Property::BackgroundColor(p)
}
// "background-image" => Property::BackgroundImage, // "background-image" => Property::BackgroundImage,
// "background-origin" => Property::BackgroundOrigin, // "background-origin" => Property::BackgroundOrigin,
// "background-position" => Property::BackgroundPosition, // "background-position" => Property::BackgroundPosition,
@ -449,7 +473,7 @@ impl CssParser {
self.skip_white(); self.skip_white();
self.consume_expected(":")?; self.consume_expected(":")?;
self.skip_white(); self.skip_white();
let p = self.parse_expected::<JustifyContentProperty>()?; let p = self.parse_expected_prop_value::<JustifyContentProperty>()?;
self.consume_expected(";")?; self.consume_expected(";")?;
Property::JustifyContent(p) Property::JustifyContent(p)
} }
@ -533,8 +557,8 @@ impl CssParser {
// "word-wrap" => Property::WordWrap, // "word-wrap" => Property::WordWrap,
// "writing-mode" => Property::WritingMode, // "writing-mode" => Property::WritingMode,
"z-index" => { "z-index" => {
let p = s let p = self
.parse() .parse_expected_prop_value()
.map_err(|_| format!("invalid z-index, expect number got {:?}", s))?; .map_err(|_| format!("invalid z-index, expect number got {:?}", s))?;
self.consume_expected(";")?; self.consume_expected(";")?;
Property::ZIndex(p) Property::ZIndex(p)
@ -606,6 +630,20 @@ impl CssParser {
s.parse::<ExpectedType>() s.parse::<ExpectedType>()
} }
fn parse_expected_prop_value<ExpectedType>(
&mut self,
) -> Result<PropertyValue<ExpectedType>, String>
where
ExpectedType: FromStr<Err = String>,
{
let s = self.expect_consume()?;
if s.starts_with("--") {
Ok(PropertyValue::Variable(s[2..].to_string()))
} else {
s.parse::<ExpectedType>().map(|v| PropertyValue::Other(v))
}
}
fn current_is_semicolon(&mut self) -> bool { fn current_is_semicolon(&mut self) -> bool {
self.peek().map(|s| s.as_str() == ";").unwrap_or_default() self.peek().map(|s| s.as_str() == ";").unwrap_or_default()
} }
@ -997,7 +1035,7 @@ impl FromStr for AnimationTimingFunction {
let n_start = "steps(".len(); let n_start = "steps(".len();
let (n_end, _) = s let (n_end, _) = s
.char_indices() .char_indices()
.find(|(idx, c)| *c == ',') .find(|(_idx, c)| *c == ',')
.ok_or_else(|| format!("invalid animation timing function {:?}", s))?; .ok_or_else(|| format!("invalid animation timing function {:?}", s))?;
let b = s[n_start..n_end] let b = s[n_start..n_end]
.trim() .trim()
@ -1057,13 +1095,13 @@ pub enum AnimationProperty {
Inherit, Inherit,
Custom( Custom(
String, String,
TimeProperty, PropertyValue<TimeProperty>,
AnimationTimingFunction, PropertyValue<AnimationTimingFunction>,
AnimationDelayProperty, PropertyValue<AnimationDelayProperty>,
usize, PropertyValue<usize>,
AnimationDirectionProperty, PropertyValue<AnimationDirectionProperty>,
AnimationFillModeProperty, PropertyValue<AnimationFillModeProperty>,
AnimationPlayStateProperty, PropertyValue<AnimationPlayStateProperty>,
), ),
} }
@ -1216,27 +1254,93 @@ impl FromStr for ColorProperty {
} }
} }
#[derive(Debug, PartialEq)]
pub enum BackgroundClipProperty {
BorderBox,
PaddingBox,
ContentBox,
Initial,
Inherit,
}
impl FromStr for BackgroundClipProperty {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let p = match s {
"border-box" => BackgroundClipProperty::BorderBox,
"padding-box" => BackgroundClipProperty::PaddingBox,
"content-box" => BackgroundClipProperty::ContentBox,
"initial" => BackgroundClipProperty::Initial,
"inherit" => BackgroundClipProperty::Inherit,
_ => return Err(format!("invalid background clip {:?}", s)),
};
Ok(p)
}
}
#[derive(Debug, PartialEq)]
pub enum BackgroundBlendModeProperty {
Normal,
Multiply,
Screen,
Overlay,
Darken,
Lighten,
ColorDodge,
Saturation,
Color,
Luminosity,
}
impl FromStr for BackgroundBlendModeProperty {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let p = match s {
"normal" => BackgroundBlendModeProperty::Normal,
"multiply" => BackgroundBlendModeProperty::Multiply,
"screen" => BackgroundBlendModeProperty::Screen,
"overlay" => BackgroundBlendModeProperty::Overlay,
"darken" => BackgroundBlendModeProperty::Darken,
"lighten" => BackgroundBlendModeProperty::Lighten,
"color-dodge" => BackgroundBlendModeProperty::ColorDodge,
"saturation" => BackgroundBlendModeProperty::Saturation,
"color" => BackgroundBlendModeProperty::Color,
"luminosity" => BackgroundBlendModeProperty::Luminosity,
_ => return Err(format!("invalid background blend mode {:?}", s)),
};
Ok(p)
}
}
#[derive(Debug, PartialEq)]
pub enum PropertyValue<T> {
Variable(String),
Other(T),
}
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum Property { pub enum Property {
AlignContent(AlignContentProperty), AlignContent(PropertyValue<AlignContentProperty>),
AlignItems(AlignItemsProperty), AlignItems(PropertyValue<AlignItemsProperty>),
AlignSelf(AlignSelfProperty), AlignSelf(PropertyValue<AlignSelfProperty>),
All(AllProperty), All(PropertyValue<AllProperty>),
Animation(AnimationProperty), Animation(PropertyValue<AnimationProperty>),
AnimationDelay(TimeProperty), AnimationDelay(PropertyValue<TimeProperty>),
AnimationDirection(AnimationDirectionProperty), AnimationDirection(PropertyValue<AnimationDirectionProperty>),
AnimationDuration(AnimationDirectionProperty), AnimationDuration(PropertyValue<AnimationDirectionProperty>),
AnimationFillMode(AnimationFillModeProperty), AnimationFillMode(PropertyValue<AnimationFillModeProperty>),
AnimationIterationCount(usize), AnimationIterationCount(PropertyValue<usize>),
AnimationName(String), AnimationName(PropertyValue<String>),
AnimationPlayState(AnimationPlayStateProperty), AnimationPlayState(PropertyValue<AnimationPlayStateProperty>),
AnimationTimingFunction(AnimationTimingFunction), AnimationTimingFunction(PropertyValue<AnimationTimingFunction>),
BackfaceVisibility(BackfaceVisibilityProperty), BackfaceVisibility(PropertyValue<BackfaceVisibilityProperty>),
Background(String), Background(String),
BackgroundAttachment(String), BackgroundAttachment(String),
BackgroundBlendMode(String), BackgroundBlendMode(PropertyValue<BackgroundBlendModeProperty>),
BackgroundClip(String), BackgroundClip(PropertyValue<BackgroundClipProperty>),
BackgroundColor(String), BackgroundColor(PropertyValue<ColorProperty>),
BackgroundImage(String), BackgroundImage(String),
BackgroundOrigin(String), BackgroundOrigin(String),
BackgroundPosition(String), BackgroundPosition(String),
@ -1350,7 +1454,7 @@ pub enum Property {
Hyphens(String), Hyphens(String),
AtImport(String), AtImport(String),
Isolation(String), Isolation(String),
JustifyContent(JustifyContentProperty), JustifyContent(PropertyValue<JustifyContentProperty>),
AtKeyframes(String), AtKeyframes(String),
Left(String), Left(String),
LetterSpacing(String), LetterSpacing(String),
@ -1430,7 +1534,7 @@ pub enum Property {
WordSpacing(String), WordSpacing(String),
WordWrap(String), WordWrap(String),
WritingMode(String), WritingMode(String),
ZIndex(ZIndexProperty), ZIndex(PropertyValue<ZIndexProperty>),
Variable(String, String), Variable(String, String),
} }
@ -1679,7 +1783,9 @@ mod tests {
Ok(vec![Selector { Ok(vec![Selector {
path: vec![SelectorPart::TagName("p".to_string())], path: vec![SelectorPart::TagName("p".to_string())],
block: Block { block: Block {
properties: vec![Property::AlignContent(AlignContentProperty::FlexStart),] properties: vec![Property::AlignContent(PropertyValue::Other(
AlignContentProperty::FlexStart
)),]
}, },
}]), }]),
) )
@ -1784,7 +1890,9 @@ mod tests {
block: Block { block: Block {
properties: vec![ properties: vec![
Property::Display(DisplayProperty::Block), Property::Display(DisplayProperty::Block),
Property::JustifyContent(JustifyContentProperty::Initial), Property::JustifyContent(PropertyValue::Other(
JustifyContentProperty::Initial
)),
] ]
}, },
}]), }]),
@ -1801,7 +1909,9 @@ mod tests {
Ok(vec![Selector { Ok(vec![Selector {
path: vec![SelectorPart::TagName("p".to_string())], path: vec![SelectorPart::TagName("p".to_string())],
block: Block { block: Block {
properties: vec![Property::Animation(AnimationProperty::Initial),] properties: vec![Property::Animation(PropertyValue::Other(
AnimationProperty::Initial
))]
}, },
}]), }]),
); );
@ -1813,28 +1923,31 @@ mod tests {
Ok(vec![Selector { Ok(vec![Selector {
path: vec![SelectorPart::TagName("p".to_string())], path: vec![SelectorPart::TagName("p".to_string())],
block: Block { block: Block {
properties: vec![Property::Animation(AnimationProperty::Inherit),] properties: vec![Property::Animation(PropertyValue::Other(
AnimationProperty::Inherit
)),]
}, },
}]), }]),
); );
let source = r#"p { animation: some; }"#; let source = r#"p { animation: some; }"#;
let tokens = CssTokenizer::new(source).tokenize(); let tokens = CssTokenizer::new(source).tokenize();
let result = CssParser::new(tokens).parse(); let result = CssParser::new(tokens).parse();
let animation = Property::Animation(PropertyValue::Other(AnimationProperty::Custom(
"some".to_string(),
PropertyValue::Other(TimeProperty::Seconds(0)),
PropertyValue::Other(AnimationTimingFunction::Ease),
PropertyValue::Other(AnimationDelayProperty::Time(TimeProperty::Seconds(0))),
PropertyValue::Other(1),
PropertyValue::Other(AnimationDirectionProperty::Normal),
PropertyValue::Other(AnimationFillModeProperty::None),
PropertyValue::Other(AnimationPlayStateProperty::Running),
)));
assert_eq!( assert_eq!(
result, result,
Ok(vec![Selector { Ok(vec![Selector {
path: vec![SelectorPart::TagName("p".to_string())], path: vec![SelectorPart::TagName("p".to_string())],
block: Block { block: Block {
properties: vec![Property::Animation(AnimationProperty::Custom( properties: vec![animation]
"some".to_string(),
TimeProperty::Seconds(0),
AnimationTimingFunction::Ease,
AnimationDelayProperty::Time(TimeProperty::Seconds(0)),
1,
AnimationDirectionProperty::Normal,
AnimationFillModeProperty::None,
AnimationPlayStateProperty::Running,
)),]
}, },
}]), }]),
); );