Rewrite parsing to trait
This commit is contained in:
parent
899352dd9f
commit
c036891396
@ -12,7 +12,6 @@ const INPUT: &str = "./jirs-client/js/styles.css";
|
|||||||
|
|
||||||
type Css = Arc<RwLock<CssFile>>;
|
type Css = Arc<RwLock<CssFile>>;
|
||||||
|
|
||||||
mod colors;
|
|
||||||
mod prop;
|
mod prop;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
496
jirs-css/src/prop/animation.rs
Normal file
496
jirs-css/src/prop/animation.rs
Normal file
@ -0,0 +1,496 @@
|
|||||||
|
use crate::prop::{CssParser, ParseToken, Parser, PropertyValue, TimeProperty, Token, ValueResult};
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub enum AnimationDirectionProperty {
|
||||||
|
Normal,
|
||||||
|
Reverse,
|
||||||
|
Alternate,
|
||||||
|
AlternateReverse,
|
||||||
|
Initial,
|
||||||
|
Inherit,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Token for AnimationDirectionProperty {}
|
||||||
|
|
||||||
|
impl ParseToken<AnimationDirectionProperty> for CssParser {
|
||||||
|
fn parse_token(&mut self) -> Result<PropertyValue<AnimationDirectionProperty>, String> {
|
||||||
|
let p = match self.expect_consume()?.as_str() {
|
||||||
|
"normal" => AnimationDirectionProperty::Normal,
|
||||||
|
"reverse" => AnimationDirectionProperty::Reverse,
|
||||||
|
"alternate" => AnimationDirectionProperty::Alternate,
|
||||||
|
"alternate-reverse" => AnimationDirectionProperty::AlternateReverse,
|
||||||
|
"initial" => AnimationDirectionProperty::Initial,
|
||||||
|
"inherit" => AnimationDirectionProperty::Inherit,
|
||||||
|
_ => return Err(format!("invalid animation direction {:?}", self.current)),
|
||||||
|
};
|
||||||
|
Ok(PropertyValue::Other(p))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub enum AnimationFillModeProperty {
|
||||||
|
None,
|
||||||
|
Forwards,
|
||||||
|
Backwards,
|
||||||
|
Both,
|
||||||
|
Initial,
|
||||||
|
Inherit,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Token for AnimationFillModeProperty {}
|
||||||
|
|
||||||
|
impl ParseToken<AnimationFillModeProperty> for CssParser {
|
||||||
|
fn parse_token(&mut self) -> ValueResult<AnimationFillModeProperty> {
|
||||||
|
let p = match self.expect_consume()?.as_str() {
|
||||||
|
"none" => AnimationFillModeProperty::None,
|
||||||
|
"forwards" => AnimationFillModeProperty::Forwards,
|
||||||
|
"backwards" => AnimationFillModeProperty::Backwards,
|
||||||
|
"both" => AnimationFillModeProperty::Both,
|
||||||
|
"initial" => AnimationFillModeProperty::Initial,
|
||||||
|
"inherit" => AnimationFillModeProperty::Inherit,
|
||||||
|
_ => return Err(format!("invalid animation fill mode {:?}", self.current)),
|
||||||
|
};
|
||||||
|
Ok(PropertyValue::Other(p))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub enum AnimationPlayStateProperty {
|
||||||
|
Paused,
|
||||||
|
Running,
|
||||||
|
Initial,
|
||||||
|
Inherit,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Token for AnimationPlayStateProperty {}
|
||||||
|
|
||||||
|
impl ParseToken<AnimationPlayStateProperty> for CssParser {
|
||||||
|
fn parse_token(&mut self) -> ValueResult<AnimationPlayStateProperty> {
|
||||||
|
self.skip_white();
|
||||||
|
let name = self.expect_consume()?;
|
||||||
|
let p = match name.as_str() {
|
||||||
|
"paused" => AnimationPlayStateProperty::Paused,
|
||||||
|
"running" => AnimationPlayStateProperty::Running,
|
||||||
|
"initial" => AnimationPlayStateProperty::Initial,
|
||||||
|
"inherit" => AnimationPlayStateProperty::Inherit,
|
||||||
|
_ => return Err(format!("invalid animation play state {:?}", name)),
|
||||||
|
};
|
||||||
|
Ok(PropertyValue::Other(p))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub enum AnimationTimingFunctionStepsProperty {
|
||||||
|
Start,
|
||||||
|
End,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Token for AnimationTimingFunctionStepsProperty {}
|
||||||
|
|
||||||
|
impl ParseToken<AnimationTimingFunctionStepsProperty> for CssParser {
|
||||||
|
fn parse_token(&mut self) -> ValueResult<AnimationTimingFunctionStepsProperty> {
|
||||||
|
let s = self.expect_consume()?;
|
||||||
|
let p = match s.to_lowercase().as_str() {
|
||||||
|
"start" => AnimationTimingFunctionStepsProperty::Start,
|
||||||
|
"end" => AnimationTimingFunctionStepsProperty::End,
|
||||||
|
_ => return Err(format!("invalid animation timing function step {:?}", s)),
|
||||||
|
};
|
||||||
|
Ok(PropertyValue::Other(p))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub enum AnimationTimingFunctionProperty {
|
||||||
|
Linear,
|
||||||
|
Ease,
|
||||||
|
EaseIn,
|
||||||
|
EaseOut,
|
||||||
|
EaseInOut,
|
||||||
|
StepStart,
|
||||||
|
StepEnd,
|
||||||
|
Steps(
|
||||||
|
PropertyValue<u32>,
|
||||||
|
PropertyValue<AnimationTimingFunctionStepsProperty>,
|
||||||
|
),
|
||||||
|
CubicBezier(
|
||||||
|
PropertyValue<f64>,
|
||||||
|
PropertyValue<f64>,
|
||||||
|
PropertyValue<f64>,
|
||||||
|
PropertyValue<f64>,
|
||||||
|
),
|
||||||
|
Initial,
|
||||||
|
Inherit,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Token for AnimationTimingFunctionProperty {}
|
||||||
|
|
||||||
|
impl ParseToken<AnimationTimingFunctionProperty> for CssParser {
|
||||||
|
fn parse_token(&mut self) -> ValueResult<AnimationTimingFunctionProperty> {
|
||||||
|
let current = self.expect_consume()?;
|
||||||
|
let p = match current.as_str() {
|
||||||
|
"linear" => AnimationTimingFunctionProperty::Linear,
|
||||||
|
"ease" => AnimationTimingFunctionProperty::Ease,
|
||||||
|
"ease-in" => AnimationTimingFunctionProperty::EaseIn,
|
||||||
|
"ease-out" => AnimationTimingFunctionProperty::EaseOut,
|
||||||
|
"ease-in-out" => AnimationTimingFunctionProperty::EaseInOut,
|
||||||
|
"step-start" => AnimationTimingFunctionProperty::StepStart,
|
||||||
|
"step-end" => AnimationTimingFunctionProperty::StepEnd,
|
||||||
|
"initial" => AnimationTimingFunctionProperty::Initial,
|
||||||
|
"inherit" => AnimationTimingFunctionProperty::Inherit,
|
||||||
|
"steps" => {
|
||||||
|
self.consume_expected("(")?;
|
||||||
|
self.skip_white();
|
||||||
|
let b = self.parse_token()?;
|
||||||
|
match b {
|
||||||
|
PropertyValue::Other(n) if n <= 0 => {
|
||||||
|
return Err(format!("invalid animation timing function, number of iterations must be greater than 0"));
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
self.skip_white();
|
||||||
|
self.consume_expected(",")?;
|
||||||
|
self.skip_white();
|
||||||
|
let c = self.parse_token()?;
|
||||||
|
self.skip_white();
|
||||||
|
self.consume_expected(")")?;
|
||||||
|
self.skip_white();
|
||||||
|
self.consume_semicolon()?;
|
||||||
|
AnimationTimingFunctionProperty::Steps(b, c)
|
||||||
|
}
|
||||||
|
"cubic-bezier" => {
|
||||||
|
self.consume_expected("(")?;
|
||||||
|
self.skip_white();
|
||||||
|
let a = self.parse_token()?;
|
||||||
|
self.skip_white();
|
||||||
|
self.consume_expected(",")?;
|
||||||
|
self.skip_white();
|
||||||
|
let b = self.parse_token()?;
|
||||||
|
self.skip_white();
|
||||||
|
self.consume_expected(",")?;
|
||||||
|
self.skip_white();
|
||||||
|
let c = self.parse_token()?;
|
||||||
|
self.skip_white();
|
||||||
|
self.consume_expected(",")?;
|
||||||
|
self.skip_white();
|
||||||
|
let d = self.parse_token()?;
|
||||||
|
self.skip_white();
|
||||||
|
self.consume_expected(")")?;
|
||||||
|
self.skip_white();
|
||||||
|
self.consume_semicolon()?;
|
||||||
|
AnimationTimingFunctionProperty::CubicBezier(a, b, c, d)
|
||||||
|
}
|
||||||
|
_ => return Err(format!("invalid animation timing function {:?}", current)),
|
||||||
|
};
|
||||||
|
Ok(PropertyValue::Other(p))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub enum AnimationDelayProperty {
|
||||||
|
Time(PropertyValue<TimeProperty>),
|
||||||
|
Initial,
|
||||||
|
Inherit,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Token for AnimationDelayProperty {}
|
||||||
|
|
||||||
|
impl ParseToken<AnimationDelayProperty> for CssParser {
|
||||||
|
fn parse_token(&mut self) -> Result<PropertyValue<AnimationDelayProperty>, String> {
|
||||||
|
let p = match self.peek().cloned().unwrap_or_default().as_str() {
|
||||||
|
"initial" => {
|
||||||
|
self.expect_consume()?;
|
||||||
|
AnimationDelayProperty::Initial
|
||||||
|
}
|
||||||
|
"inherit" => {
|
||||||
|
self.expect_consume()?;
|
||||||
|
AnimationDelayProperty::Inherit
|
||||||
|
}
|
||||||
|
_ => AnimationDelayProperty::Time(self.parse_token()?),
|
||||||
|
};
|
||||||
|
Ok(PropertyValue::Other(p))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub enum AnimationProperty {
|
||||||
|
Initial,
|
||||||
|
Inherit,
|
||||||
|
Custom(
|
||||||
|
String,
|
||||||
|
PropertyValue<TimeProperty>,
|
||||||
|
PropertyValue<AnimationTimingFunctionProperty>,
|
||||||
|
PropertyValue<AnimationDelayProperty>,
|
||||||
|
PropertyValue<usize>,
|
||||||
|
PropertyValue<AnimationDirectionProperty>,
|
||||||
|
PropertyValue<AnimationFillModeProperty>,
|
||||||
|
PropertyValue<AnimationPlayStateProperty>,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Token for AnimationProperty {}
|
||||||
|
|
||||||
|
impl ParseToken<AnimationProperty> for CssParser {
|
||||||
|
fn parse_token(&mut self) -> Result<PropertyValue<AnimationProperty>, String> {
|
||||||
|
eprintln!("only full animation is supported!");
|
||||||
|
if let Some(v) = self.try_parse_variable() {
|
||||||
|
return Ok(PropertyValue::Variable(v));
|
||||||
|
}
|
||||||
|
let def = self
|
||||||
|
.peek()
|
||||||
|
.cloned()
|
||||||
|
.ok_or_else(|| "expect to find token but EOF".to_string())?;
|
||||||
|
let p = match def.as_str() {
|
||||||
|
"initial" => {
|
||||||
|
self.expect_consume()?;
|
||||||
|
PropertyValue::Other(AnimationProperty::Initial)
|
||||||
|
}
|
||||||
|
"inherit" => {
|
||||||
|
self.expect_consume()?;
|
||||||
|
PropertyValue::Other(AnimationProperty::Inherit)
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let duration = if self.next_is_semicolon() {
|
||||||
|
PropertyValue::Other(TimeProperty::Seconds(0))
|
||||||
|
} else {
|
||||||
|
let v = self.parse_token()?;
|
||||||
|
self.skip_white();
|
||||||
|
v
|
||||||
|
};
|
||||||
|
let timing = if self.next_is_semicolon() {
|
||||||
|
PropertyValue::Other(AnimationTimingFunctionProperty::Ease)
|
||||||
|
} else {
|
||||||
|
let v = self.parse_token()?;
|
||||||
|
self.skip_white();
|
||||||
|
v
|
||||||
|
};
|
||||||
|
let delay = if self.next_is_semicolon() {
|
||||||
|
PropertyValue::Other(AnimationDelayProperty::Time(PropertyValue::Other(
|
||||||
|
TimeProperty::Seconds(0),
|
||||||
|
)))
|
||||||
|
} else {
|
||||||
|
let v = self.parse_token()?;
|
||||||
|
self.skip_white();
|
||||||
|
v
|
||||||
|
};
|
||||||
|
let iteration_count = if self.next_is_semicolon() {
|
||||||
|
PropertyValue::Other(1)
|
||||||
|
} else {
|
||||||
|
let v = self.expect_consume()?.parse::<usize>().map_err(|_| {
|
||||||
|
format!(
|
||||||
|
"invalid animation iteration count, expect number got {:?}",
|
||||||
|
self.current
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
self.skip_white();
|
||||||
|
PropertyValue::Other(v)
|
||||||
|
};
|
||||||
|
let direction = if self.next_is_semicolon() {
|
||||||
|
PropertyValue::Other(AnimationDirectionProperty::Normal)
|
||||||
|
} else {
|
||||||
|
let v = self.parse_token()?;
|
||||||
|
self.skip_white();
|
||||||
|
v
|
||||||
|
};
|
||||||
|
let fill_mode = if self.next_is_semicolon() {
|
||||||
|
PropertyValue::Other(AnimationFillModeProperty::None)
|
||||||
|
} else {
|
||||||
|
let v = self.parse_token()?;
|
||||||
|
self.skip_white();
|
||||||
|
v
|
||||||
|
};
|
||||||
|
let play_state = if self.next_is_semicolon() {
|
||||||
|
PropertyValue::Other(AnimationPlayStateProperty::Running)
|
||||||
|
} else {
|
||||||
|
let v = self.parse_token()?;
|
||||||
|
self.skip_white();
|
||||||
|
v
|
||||||
|
};
|
||||||
|
let name = self.expect_consume()?;
|
||||||
|
|
||||||
|
PropertyValue::Other(AnimationProperty::Custom(
|
||||||
|
name,
|
||||||
|
duration,
|
||||||
|
timing,
|
||||||
|
delay,
|
||||||
|
iteration_count,
|
||||||
|
direction,
|
||||||
|
fill_mode,
|
||||||
|
play_state,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::prop::CssTokenizer;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// we assume currently we hit property name
|
||||||
|
/// display : block;
|
||||||
|
/// ^
|
||||||
|
/// so we need to add `:`
|
||||||
|
///
|
||||||
|
/// But we also we adding spaces around because they are allowed in css and needs to be skipped
|
||||||
|
fn parse_prop_value(s: &str) -> CssParser {
|
||||||
|
let full = format!(" : {} {}", s, if s.contains(";") { "" } else { ";" });
|
||||||
|
let tokens = CssTokenizer::new(full.as_str()).tokenize();
|
||||||
|
CssParser::new("", tokens)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_animation_timing_function() {
|
||||||
|
let res: ValueResult<AnimationTimingFunctionProperty> =
|
||||||
|
parse_prop_value("linear").parse_token();
|
||||||
|
let expected = Ok(PropertyValue::Other(
|
||||||
|
AnimationTimingFunctionProperty::Linear,
|
||||||
|
));
|
||||||
|
assert_eq!(res, expected);
|
||||||
|
let res: ValueResult<AnimationTimingFunctionProperty> =
|
||||||
|
parse_prop_value("ease").parse_token();
|
||||||
|
let expected = Ok(PropertyValue::Other(AnimationTimingFunctionProperty::Ease));
|
||||||
|
assert_eq!(res, expected);
|
||||||
|
let res: ValueResult<AnimationTimingFunctionProperty> =
|
||||||
|
parse_prop_value("ease-in").parse_token();
|
||||||
|
let expected = Ok(PropertyValue::Other(
|
||||||
|
AnimationTimingFunctionProperty::EaseIn,
|
||||||
|
));
|
||||||
|
assert_eq!(res, expected);
|
||||||
|
let res: ValueResult<AnimationTimingFunctionProperty> =
|
||||||
|
parse_prop_value("ease-out").parse_token();
|
||||||
|
let expected = Ok(PropertyValue::Other(
|
||||||
|
AnimationTimingFunctionProperty::EaseOut,
|
||||||
|
));
|
||||||
|
assert_eq!(res, expected);
|
||||||
|
let res: ValueResult<AnimationTimingFunctionProperty> =
|
||||||
|
parse_prop_value("ease-in-out").parse_token();
|
||||||
|
let expected = Ok(PropertyValue::Other(
|
||||||
|
AnimationTimingFunctionProperty::EaseInOut,
|
||||||
|
));
|
||||||
|
assert_eq!(res, expected);
|
||||||
|
let res: ValueResult<AnimationTimingFunctionProperty> =
|
||||||
|
parse_prop_value("step-start").parse_token();
|
||||||
|
let expected = Ok(PropertyValue::Other(
|
||||||
|
AnimationTimingFunctionProperty::StepStart,
|
||||||
|
));
|
||||||
|
assert_eq!(res, expected);
|
||||||
|
let res: ValueResult<AnimationTimingFunctionProperty> =
|
||||||
|
parse_prop_value("step-end").parse_token();
|
||||||
|
let expected = Ok(PropertyValue::Other(
|
||||||
|
AnimationTimingFunctionProperty::StepEnd,
|
||||||
|
));
|
||||||
|
assert_eq!(res, expected);
|
||||||
|
let res: ValueResult<AnimationTimingFunctionProperty> =
|
||||||
|
parse_prop_value("steps(1,start)").parse_token();
|
||||||
|
let expected = Ok(PropertyValue::Other(
|
||||||
|
AnimationTimingFunctionProperty::Steps(
|
||||||
|
PropertyValue::Other(1),
|
||||||
|
PropertyValue::Other(AnimationTimingFunctionStepsProperty::Start),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
assert_eq!(res, expected);
|
||||||
|
let res: ValueResult<AnimationTimingFunctionProperty> =
|
||||||
|
parse_prop_value("steps(3,end)").parse_token();
|
||||||
|
let expected = Ok(PropertyValue::Other(
|
||||||
|
AnimationTimingFunctionProperty::Steps(
|
||||||
|
PropertyValue::Other(3),
|
||||||
|
PropertyValue::Other(AnimationTimingFunctionStepsProperty::End),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
assert_eq!(res, expected);
|
||||||
|
let res: ValueResult<AnimationTimingFunctionProperty> =
|
||||||
|
parse_prop_value("steps(0,start)").parse_token();
|
||||||
|
let expected = Err(
|
||||||
|
"invalid animation timing function, number of iterations must be greater than 0"
|
||||||
|
.to_string(),
|
||||||
|
);
|
||||||
|
assert_eq!(res, expected);
|
||||||
|
let res: ValueResult<AnimationTimingFunctionProperty> =
|
||||||
|
parse_prop_value("steps(-2,start)").parse_token();
|
||||||
|
let expected =
|
||||||
|
Err("invalid token, expect number greater or equal 0 got \"-2\"".to_string());
|
||||||
|
assert_eq!(res, expected);
|
||||||
|
let res: ValueResult<AnimationTimingFunctionProperty> =
|
||||||
|
parse_prop_value("steps(0,end)").parse_token();
|
||||||
|
let expected = Err(
|
||||||
|
"invalid animation timing function, number of iterations must be greater than 0"
|
||||||
|
.to_string(),
|
||||||
|
);
|
||||||
|
assert_eq!(res, expected);
|
||||||
|
let res: ValueResult<AnimationTimingFunctionProperty> =
|
||||||
|
parse_prop_value("steps(-1,end)").parse_token();
|
||||||
|
let expected =
|
||||||
|
Err("invalid token, expect number greater or equal 0 got \"-1\"".to_string());
|
||||||
|
assert_eq!(res, expected);
|
||||||
|
let res: ValueResult<AnimationTimingFunctionProperty> =
|
||||||
|
parse_prop_value("steps(end)").parse_token();
|
||||||
|
let expected =
|
||||||
|
Err("invalid token, expect number greater or equal 0 got \"end\"".to_string());
|
||||||
|
assert_eq!(res, expected);
|
||||||
|
let res: ValueResult<AnimationTimingFunctionProperty> =
|
||||||
|
parse_prop_value("steps(start)").parse_token();
|
||||||
|
let expected =
|
||||||
|
Err("invalid token, expect number greater or equal 0 got \"start\"".to_string());
|
||||||
|
assert_eq!(res, expected);
|
||||||
|
let res: ValueResult<AnimationTimingFunctionProperty> =
|
||||||
|
parse_prop_value("steps(0)").parse_token();
|
||||||
|
let expected = Err(
|
||||||
|
"invalid animation timing function, number of iterations must be greater than 0"
|
||||||
|
.to_string(),
|
||||||
|
);
|
||||||
|
assert_eq!(res, expected);
|
||||||
|
let res: ValueResult<AnimationTimingFunctionProperty> =
|
||||||
|
parse_prop_value("steps(1)").parse_token();
|
||||||
|
let expected = Err("expect to find token \",\" but found \")\"".to_string());
|
||||||
|
assert_eq!(res, expected);
|
||||||
|
let res: ValueResult<AnimationTimingFunctionProperty> =
|
||||||
|
parse_prop_value("cubic-bezier(0.1,0.2,0.3,0.4)").parse_token();
|
||||||
|
let expected = Ok(PropertyValue::Other(
|
||||||
|
AnimationTimingFunctionProperty::CubicBezier(
|
||||||
|
PropertyValue::Other(0.1),
|
||||||
|
PropertyValue::Other(0.2),
|
||||||
|
PropertyValue::Other(0.3),
|
||||||
|
PropertyValue::Other(0.4),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
assert_eq!(res, expected);
|
||||||
|
let res: ValueResult<AnimationTimingFunctionProperty> =
|
||||||
|
parse_prop_value("cubic-bezier(0.1, 0.2, 0.3, 0.4)").parse_token();
|
||||||
|
let expected = Ok(PropertyValue::Other(
|
||||||
|
AnimationTimingFunctionProperty::CubicBezier(
|
||||||
|
PropertyValue::Other(0.1),
|
||||||
|
PropertyValue::Other(0.2),
|
||||||
|
PropertyValue::Other(0.3),
|
||||||
|
PropertyValue::Other(0.4),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
assert_eq!(res, expected);
|
||||||
|
let res: ValueResult<AnimationTimingFunctionProperty> =
|
||||||
|
parse_prop_value("cubic-bezier(0.1,0.2,0.3)").parse_token();
|
||||||
|
let expected = Err("expect to find token \",\" but found \")\"".to_string());
|
||||||
|
assert_eq!(res, expected);
|
||||||
|
let res: ValueResult<AnimationTimingFunctionProperty> =
|
||||||
|
parse_prop_value("cubic-bezier(0.1,0.2)").parse_token();
|
||||||
|
let expected = Err("expect to find token \",\" but found \")\"".to_string());
|
||||||
|
assert_eq!(res, expected);
|
||||||
|
let res: ValueResult<AnimationTimingFunctionProperty> =
|
||||||
|
parse_prop_value("cubic-bezier(0.1)").parse_token();
|
||||||
|
let expected = Err("expect to find token \",\" but found \")\"".to_string());
|
||||||
|
assert_eq!(res, expected);
|
||||||
|
let res: ValueResult<AnimationTimingFunctionProperty> =
|
||||||
|
parse_prop_value("initial").parse_token();
|
||||||
|
let expected = Ok(PropertyValue::Other(
|
||||||
|
AnimationTimingFunctionProperty::Initial,
|
||||||
|
));
|
||||||
|
assert_eq!(res, expected);
|
||||||
|
let res: ValueResult<AnimationTimingFunctionProperty> =
|
||||||
|
parse_prop_value("inherit").parse_token();
|
||||||
|
let expected = Ok(PropertyValue::Other(
|
||||||
|
AnimationTimingFunctionProperty::Inherit,
|
||||||
|
));
|
||||||
|
assert_eq!(res, expected);
|
||||||
|
}
|
||||||
|
}
|
@ -1,47 +1,190 @@
|
|||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
/// hsla(360, 100%, 100%, 1.0)
|
use crate::prop::{CssParser, ParseToken, Parser, PropertyValue, Token, ValueResult};
|
||||||
/// 012345
|
|
||||||
/// hsl(360, 100%, 100%, 1.0)
|
#[derive(Debug, PartialEq)]
|
||||||
/// 01234
|
pub enum ColorProperty {
|
||||||
pub fn parse_hsla(given: &str, parse_alpha: bool) -> Result<(u16, u8, u8, f64), String> {
|
Name(String),
|
||||||
let start_idx = if parse_alpha { 5 } else { 4 };
|
Rgba(
|
||||||
let v: Vec<String> = given[start_idx..(given.len() - 1)]
|
PropertyValue<u8>,
|
||||||
.split(',')
|
PropertyValue<u8>,
|
||||||
.map(|s| s.to_string())
|
PropertyValue<u8>,
|
||||||
.collect();
|
PropertyValue<u8>,
|
||||||
let h = v
|
),
|
||||||
.get(0)
|
Hsla(
|
||||||
.ok_or_else(|| format!("invalid color {:?}", given))
|
PropertyValue<u16>,
|
||||||
.and_then(|s| s.parse().map_err(|_| format!("invalid color {:?}", given)))?;
|
PropertyValue<u8>,
|
||||||
let s = parse_percent(given, v.get(1))?;
|
PropertyValue<u8>,
|
||||||
let l = parse_percent(given, v.get(2))?;
|
PropertyValue<f64>,
|
||||||
let a = if parse_alpha {
|
),
|
||||||
v.get(3)
|
Current,
|
||||||
.ok_or_else(|| format!("invalid color {:?}", given))
|
|
||||||
.and_then(|s| {
|
|
||||||
s.parse::<f64>()
|
|
||||||
.map_err(|_| format!("invalid color {:?}", given))
|
|
||||||
})?
|
|
||||||
} else {
|
|
||||||
1.0f64
|
|
||||||
};
|
|
||||||
Ok((h, s, l, a))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_percent(s: &str, v: Option<&String>) -> Result<u8, String> {
|
impl Token for ColorProperty {}
|
||||||
v.ok_or_else(|| format!("invalid color {:?}", s))
|
|
||||||
.and_then(|s| {
|
impl ParseToken<ColorProperty> for CssParser {
|
||||||
if s.ends_with('%') {
|
fn parse_token(&mut self) -> ValueResult<ColorProperty> {
|
||||||
Ok(s[0..(s.len() - 1)].to_string())
|
self.skip_white();
|
||||||
} else {
|
let current = self.expect_consume()?;
|
||||||
Err(format!("invalid color {:?}", s))
|
let s = current.trim();
|
||||||
|
|
||||||
|
let p = match s {
|
||||||
|
"currentColor" => ColorProperty::Current,
|
||||||
|
_ if s.len() == 7 && s.starts_with('#') => {
|
||||||
|
let r = u8::from_str_radix(&s[1..=2], 16)
|
||||||
|
.map_err(|_| format!("invalid color {:?}", s))?;
|
||||||
|
let g = u8::from_str_radix(&s[3..=4], 16)
|
||||||
|
.map_err(|_| format!("invalid color {:?}", s))?;
|
||||||
|
let b = u8::from_str_radix(&s[5..=6], 16)
|
||||||
|
.map_err(|_| format!("invalid color {:?}", s))?;
|
||||||
|
ColorProperty::Rgba(
|
||||||
|
PropertyValue::Other(r),
|
||||||
|
PropertyValue::Other(g),
|
||||||
|
PropertyValue::Other(b),
|
||||||
|
PropertyValue::Other(255),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
_ if s.len() == 4 && s.starts_with('#') => {
|
||||||
|
let _x = &s[1..=1];
|
||||||
|
let r = u8::from_str_radix(&s[1..=1].repeat(2), 16)
|
||||||
|
.map_err(|_| format!("invalid color {:?}", s))?;
|
||||||
|
let g = u8::from_str_radix(&s[2..=2].repeat(2), 16)
|
||||||
|
.map_err(|_| format!("invalid color {:?}", s))?;
|
||||||
|
let b = u8::from_str_radix(&s[3..=3].repeat(2), 16)
|
||||||
|
.map_err(|_| format!("invalid color {:?}", s))?;
|
||||||
|
ColorProperty::Rgba(
|
||||||
|
PropertyValue::Other(r),
|
||||||
|
PropertyValue::Other(g),
|
||||||
|
PropertyValue::Other(b),
|
||||||
|
PropertyValue::Other(255),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
_ if s.len() == 9 && s.starts_with('#') => {
|
||||||
|
let (r, g, b, a) = (
|
||||||
|
u8::from_str_radix(&s[1..=2], 16)
|
||||||
|
.map_err(|_| format!("invalid color {:?}", s))?,
|
||||||
|
u8::from_str_radix(&s[3..=4], 16)
|
||||||
|
.map_err(|_| format!("invalid color {:?}", s))?,
|
||||||
|
u8::from_str_radix(&s[5..=6], 16)
|
||||||
|
.map_err(|_| format!("invalid color {:?}", s))?,
|
||||||
|
u8::from_str_radix(&s[7..=8], 16)
|
||||||
|
.map_err(|_| format!("invalid color {:?}", s))?,
|
||||||
|
);
|
||||||
|
ColorProperty::Rgba(
|
||||||
|
PropertyValue::Other(r),
|
||||||
|
PropertyValue::Other(g),
|
||||||
|
PropertyValue::Other(b),
|
||||||
|
PropertyValue::Other(a),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
"rgba" => {
|
||||||
|
self.skip_white();
|
||||||
|
self.consume_expected("(")?;
|
||||||
|
self.skip_white();
|
||||||
|
let r = self.parse_token()?;
|
||||||
|
self.skip_white();
|
||||||
|
self.consume_expected(",")?;
|
||||||
|
self.skip_white();
|
||||||
|
let g = self.parse_token()?;
|
||||||
|
self.skip_white();
|
||||||
|
self.consume_expected(",")?;
|
||||||
|
self.skip_white();
|
||||||
|
let b = self.parse_token()?;
|
||||||
|
self.skip_white();
|
||||||
|
self.consume_expected(",")?;
|
||||||
|
self.skip_white();
|
||||||
|
let a = self.parse_token()?.into_color_alpha();
|
||||||
|
self.skip_white();
|
||||||
|
self.consume_expected(")")?;
|
||||||
|
self.skip_white();
|
||||||
|
ColorProperty::Rgba(r, g, b, a)
|
||||||
|
}
|
||||||
|
"rgb" => {
|
||||||
|
self.skip_white();
|
||||||
|
self.consume_expected("(")?;
|
||||||
|
self.skip_white();
|
||||||
|
let r = self.parse_token()?;
|
||||||
|
self.skip_white();
|
||||||
|
self.consume_expected(",")?;
|
||||||
|
self.skip_white();
|
||||||
|
let g = self.parse_token()?;
|
||||||
|
self.skip_white();
|
||||||
|
self.consume_expected(",")?;
|
||||||
|
self.skip_white();
|
||||||
|
let b = self.parse_token()?;
|
||||||
|
self.skip_white();
|
||||||
|
let a = PropertyValue::Other(255);
|
||||||
|
self.skip_white();
|
||||||
|
self.consume_expected(")")?;
|
||||||
|
self.skip_white();
|
||||||
|
ColorProperty::Rgba(r, g, b, a)
|
||||||
|
}
|
||||||
|
"hsla" => {
|
||||||
|
self.skip_white();
|
||||||
|
self.consume_expected("(")?;
|
||||||
|
self.skip_white();
|
||||||
|
let h = self.parse_token()?;
|
||||||
|
self.skip_white();
|
||||||
|
self.consume_expected(",")?;
|
||||||
|
self.skip_white();
|
||||||
|
let s = self.parse_token()?;
|
||||||
|
self.consume_expected("%")?;
|
||||||
|
self.skip_white();
|
||||||
|
self.consume_expected(",")?;
|
||||||
|
self.skip_white();
|
||||||
|
let l = self.parse_token()?;
|
||||||
|
self.consume_expected("%")?;
|
||||||
|
self.skip_white();
|
||||||
|
self.consume_expected(",")?;
|
||||||
|
self.skip_white();
|
||||||
|
let a = self.parse_token()?;
|
||||||
|
match a {
|
||||||
|
PropertyValue::Other(f) if -0.001f64 > f || f > 1.001f64 => {
|
||||||
|
return Err(format!("out of range hsl alpha value {:?}", a))
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
};
|
||||||
|
self.skip_white();
|
||||||
|
self.consume_expected(")")?;
|
||||||
|
self.skip_white();
|
||||||
|
ColorProperty::Hsla(h, s, l, a)
|
||||||
|
}
|
||||||
|
"hsl" => {
|
||||||
|
self.skip_white();
|
||||||
|
self.consume_expected("(")?;
|
||||||
|
self.skip_white();
|
||||||
|
let h = self.parse_token()?;
|
||||||
|
self.skip_white();
|
||||||
|
self.consume_expected(",")?;
|
||||||
|
self.skip_white();
|
||||||
|
let s = self.parse_token()?;
|
||||||
|
self.consume_expected("%")?;
|
||||||
|
self.skip_white();
|
||||||
|
self.consume_expected(",")?;
|
||||||
|
self.skip_white();
|
||||||
|
let l = self.parse_token()?;
|
||||||
|
self.consume_expected("%")?;
|
||||||
|
let a = PropertyValue::Other(1f64);
|
||||||
|
self.skip_white();
|
||||||
|
self.consume_expected(")")?;
|
||||||
|
self.skip_white();
|
||||||
|
ColorProperty::Hsla(h, s, l, a)
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => s
|
||||||
|
.parse::<Color>()
|
||||||
|
.map(|c| c.to_values())
|
||||||
|
.and_then(|(r, g, b)| {
|
||||||
|
Ok(ColorProperty::Rgba(
|
||||||
|
PropertyValue::Other(r),
|
||||||
|
PropertyValue::Other(g),
|
||||||
|
PropertyValue::Other(b),
|
||||||
|
PropertyValue::Other(255),
|
||||||
|
))
|
||||||
|
})?,
|
||||||
|
};
|
||||||
|
Ok(PropertyValue::Other(p))
|
||||||
}
|
}
|
||||||
})
|
|
||||||
.and_then(|s| {
|
|
||||||
s.parse::<u8>()
|
|
||||||
.map_err(|_| format!("invalid color {:?}", s))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum Color {
|
pub enum Color {
|
File diff suppressed because it is too large
Load Diff
55
jirs-css/tests/full.css
Normal file
55
jirs-css/tests/full.css
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
p > #foo + .bar {
|
||||||
|
align-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
align-self: auto;
|
||||||
|
all: inherit;
|
||||||
|
animation: 3s ease-in 1s 2 reverse both paused slidein;
|
||||||
|
animation-delay: 4ms;
|
||||||
|
animation-direction: alternate-reverse;
|
||||||
|
animation-duration: 5s;
|
||||||
|
animation-iteration-count: 9;
|
||||||
|
animation-name: my-animation;
|
||||||
|
animation-play-state: paused;
|
||||||
|
animation-timing-function: ease-in;
|
||||||
|
backface-visibility: visible;
|
||||||
|
background-attachment: local;
|
||||||
|
background-blend-mode: darken;
|
||||||
|
background-clip: content-box;
|
||||||
|
background-color: rgba(12, 34, 56, 0.6);
|
||||||
|
/* */
|
||||||
|
clear: left;
|
||||||
|
color: aqua;
|
||||||
|
display: block;
|
||||||
|
float: right;
|
||||||
|
justify-content: flex-end;
|
||||||
|
position: relative;
|
||||||
|
z-index: -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
p > #foo ~ .bar {
|
||||||
|
align-content: var(--align-content);
|
||||||
|
align-items: var(--align-items);
|
||||||
|
align-self: var(--align-self);
|
||||||
|
all: var(--all);
|
||||||
|
animation: var(--animation);
|
||||||
|
animation-delay: var(--animation-delay);
|
||||||
|
animation-direction: var(--animation-direction);
|
||||||
|
animation-duration: var(--animation-duration);
|
||||||
|
animation-iteration-count: var(--animation-iteration-count);
|
||||||
|
animation-name: var(--animation-name);
|
||||||
|
animation-play-state: var(--animation-play-state);
|
||||||
|
animation-timing-function: var(--animation-timing-function);
|
||||||
|
backface-visibility: var(--backface-visibility);
|
||||||
|
background-attachment: var(--background-attachment);
|
||||||
|
background-blend-mode: var(--background-blend-mode);
|
||||||
|
background-clip: var(--background-clip);
|
||||||
|
background-color: var(--background-color);
|
||||||
|
/* */
|
||||||
|
clear: var(--clear);
|
||||||
|
color: var(--color);
|
||||||
|
display: var(--display);
|
||||||
|
float: var(--float);
|
||||||
|
justify-content: var(--justify-content);
|
||||||
|
position: var(--position);
|
||||||
|
z-index: var(--z-index);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user