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>>;
|
||||
|
||||
mod colors;
|
||||
mod prop;
|
||||
|
||||
#[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;
|
||||
|
||||
/// hsla(360, 100%, 100%, 1.0)
|
||||
/// 012345
|
||||
/// hsl(360, 100%, 100%, 1.0)
|
||||
/// 01234
|
||||
pub fn parse_hsla(given: &str, parse_alpha: bool) -> Result<(u16, u8, u8, f64), String> {
|
||||
let start_idx = if parse_alpha { 5 } else { 4 };
|
||||
let v: Vec<String> = given[start_idx..(given.len() - 1)]
|
||||
.split(',')
|
||||
.map(|s| s.to_string())
|
||||
.collect();
|
||||
let h = v
|
||||
.get(0)
|
||||
.ok_or_else(|| format!("invalid color {:?}", given))
|
||||
.and_then(|s| s.parse().map_err(|_| format!("invalid color {:?}", given)))?;
|
||||
let s = parse_percent(given, v.get(1))?;
|
||||
let l = parse_percent(given, v.get(2))?;
|
||||
let a = if parse_alpha {
|
||||
v.get(3)
|
||||
.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))
|
||||
use crate::prop::{CssParser, ParseToken, Parser, PropertyValue, Token, ValueResult};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum ColorProperty {
|
||||
Name(String),
|
||||
Rgba(
|
||||
PropertyValue<u8>,
|
||||
PropertyValue<u8>,
|
||||
PropertyValue<u8>,
|
||||
PropertyValue<u8>,
|
||||
),
|
||||
Hsla(
|
||||
PropertyValue<u16>,
|
||||
PropertyValue<u8>,
|
||||
PropertyValue<u8>,
|
||||
PropertyValue<f64>,
|
||||
),
|
||||
Current,
|
||||
}
|
||||
|
||||
fn parse_percent(s: &str, v: Option<&String>) -> Result<u8, String> {
|
||||
v.ok_or_else(|| format!("invalid color {:?}", s))
|
||||
.and_then(|s| {
|
||||
if s.ends_with('%') {
|
||||
Ok(s[0..(s.len() - 1)].to_string())
|
||||
} else {
|
||||
Err(format!("invalid color {:?}", s))
|
||||
impl Token for ColorProperty {}
|
||||
|
||||
impl ParseToken<ColorProperty> for CssParser {
|
||||
fn parse_token(&mut self) -> ValueResult<ColorProperty> {
|
||||
self.skip_white();
|
||||
let current = self.expect_consume()?;
|
||||
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 {
|
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