From 3be8fc71037537f56e2e2472c1b98a7ff6054c69 Mon Sep 17 00:00:00 2001 From: Adrian Wozniak Date: Mon, 27 Apr 2020 20:25:25 +0200 Subject: [PATCH] Parse CSS HSL --- jirs-css/src/{predefined.rs => colors.rs} | 80 +++++++++++++ jirs-css/src/main.rs | 2 +- jirs-css/src/prop.rs | 135 ++++++++++------------ 3 files changed, 140 insertions(+), 77 deletions(-) rename jirs-css/src/{predefined.rs => colors.rs} (90%) diff --git a/jirs-css/src/predefined.rs b/jirs-css/src/colors.rs similarity index 90% rename from jirs-css/src/predefined.rs rename to jirs-css/src/colors.rs index 076fcd76..12daba42 100644 --- a/jirs-css/src/predefined.rs +++ b/jirs-css/src/colors.rs @@ -1,5 +1,85 @@ use std::str::FromStr; +/// rgba(255, 255, 255, 1.0) +/// 012345 +/// rgb(255, 255, 255, 1.0) +/// 01234 +pub fn parse_rgba(s: &str, parse_alpha: bool) -> Result<(u8, u8, u8, u8), String> { + let start_idx = if parse_alpha { 5 } else { 4 }; + let v: Vec = s[start_idx..(s.len() - 1)] + .split(',') + .map(|s| s.to_string()) + .collect(); + let r = v + .get(0) + .ok_or_else(|| format!("invalid color {:?}", s)) + .and_then(|s| s.parse().map_err(|_| format!("invalid color {:?}", s)))?; + let g = v + .get(1) + .ok_or_else(|| format!("invalid color {:?}", s)) + .and_then(|s| s.parse().map_err(|_| format!("invalid color {:?}", s)))?; + let b = v + .get(2) + .ok_or_else(|| format!("invalid color {:?}", s)) + .and_then(|s| s.parse().map_err(|_| format!("invalid color {:?}", s)))?; + let a = if parse_alpha { + (v.get(3) + .ok_or_else(|| format!("invalid color {:?}", s)) + .and_then(|s| { + s.parse::() + .map_err(|_| format!("invalid color {:?}", s)) + })? + * 255f64) as u8 + } else { + 255 + }; + Ok((r, g, b, a)) +} + +/// 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 = 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::() + .map_err(|_| format!("invalid color {:?}", given)) + })? + } else { + 1.0f64 + }; + Ok((h, s, l, a)) +} + +fn parse_percent(s: &str, v: Option<&String>) -> Result { + 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)) + } + }) + .and_then(|s| { + s.parse::() + .map_err(|_| format!("invalid color {:?}", s)) + }) +} + pub enum Color { AliceBlue, AntiqueWhite, diff --git a/jirs-css/src/main.rs b/jirs-css/src/main.rs index 9b67becb..9128b190 100644 --- a/jirs-css/src/main.rs +++ b/jirs-css/src/main.rs @@ -12,7 +12,7 @@ const INPUT: &str = "./jirs-client/js/styles.css"; type Css = Arc>; -mod predefined; +mod colors; mod prop; #[derive(Debug)] diff --git a/jirs-css/src/prop.rs b/jirs-css/src/prop.rs index 4fc14488..7b78b192 100644 --- a/jirs-css/src/prop.rs +++ b/jirs-css/src/prop.rs @@ -2,6 +2,8 @@ use std::iter::Peekable; use std::str::{Chars, FromStr}; use std::vec::IntoIter; +use crate::colors::{parse_hsla, parse_rgba}; + #[derive(Debug)] pub struct CssTokenizer<'l> { it: Peekable>, @@ -59,10 +61,24 @@ impl<'l> CssTokenizer<'l> { } } +#[derive(Debug)] +pub struct ParserPosition { + line: usize, + line_character: usize, + character: usize, +} + +impl std::fmt::Display for ParserPosition { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(format!("({}:{}:{})", self.line, self.line_character, self.character).as_str()) + } +} + #[derive(Debug)] pub struct CssParser { it: Peekable>, selectors: Vec, + pos: ParserPosition, } impl CssParser { @@ -70,6 +86,11 @@ impl CssParser { Self { it: tokens.into_iter().peekable(), selectors: vec![], + pos: ParserPosition { + line: 0, + line_character: 0, + character: 0, + }, } } @@ -526,7 +547,21 @@ impl CssParser { } fn consume(&mut self) -> Option { - self.it.next() + let current = self.it.next(); + if let Some(s) = current.as_ref() { + match s.as_str() { + "\n" => { + self.pos.character += s.len(); + self.pos.line += 1; + self.pos.line_character += 0; + } + _ => { + self.pos.character += s.len(); + self.pos.line_character += s.len(); + } + } + } + current } fn expect_consume(&mut self) -> Result { @@ -1111,8 +1146,8 @@ impl FromStr for ZIndexProperty { #[derive(Debug, PartialEq)] pub enum ColorProperty { Name(String), - Rgba(u16, u16, u16, u16), - Hsla(u16, u16, u16, u16), + Rgba(u8, u8, u8, u8), + Hsla(u16, u8, u8, f64), Current, } @@ -1120,99 +1155,48 @@ impl FromStr for ColorProperty { type Err = String; fn from_str(s: &str) -> Result { - let p = match s.trim().to_lowercase().as_str() { - "currentcolor" => ColorProperty::Current, + let p = match s.trim() { + "currentColor" => ColorProperty::Current, _ if s.len() == 7 && s.starts_with('#') => { let (r, g, b) = ( - u16::from_str_radix(&s[1..2], 16) + u8::from_str_radix(&s[1..2], 16) .map_err(|_| format!("invalid color {:?}", s))?, - u16::from_str_radix(&s[3..4], 16) + u8::from_str_radix(&s[3..4], 16) .map_err(|_| format!("invalid color {:?}", s))?, - u16::from_str_radix(&s[5..6], 16) + u8::from_str_radix(&s[5..6], 16) .map_err(|_| format!("invalid color {:?}", s))?, ); ColorProperty::Rgba(r, g, b, 255) } _ if s.len() == 9 && s.starts_with('#') => { let (r, g, b, a) = ( - u16::from_str_radix(&s[1..2], 16) + u8::from_str_radix(&s[1..2], 16) .map_err(|_| format!("invalid color {:?}", s))?, - u16::from_str_radix(&s[3..4], 16) + u8::from_str_radix(&s[3..4], 16) .map_err(|_| format!("invalid color {:?}", s))?, - u16::from_str_radix(&s[5..6], 16) + u8::from_str_radix(&s[5..6], 16) .map_err(|_| format!("invalid color {:?}", s))?, - u16::from_str_radix(&s[7..8], 16) + u8::from_str_radix(&s[7..8], 16) .map_err(|_| format!("invalid color {:?}", s))?, ); ColorProperty::Rgba(r, g, b, a) } - _ if s.starts_with("rgba(") => { - let v: Vec = s[5..(s.len() - 1)] - .split(',') - .map(|s| s.to_string()) - .collect(); - let r = v - .get(0) - .ok_or_else(|| format!("invalid color {:?}", s)) - .and_then(|s| { - s.parse::() - .map_err(|_| format!("invalid color {:?}", s)) - })?; - let g = v - .get(1) - .ok_or_else(|| format!("invalid color {:?}", s)) - .and_then(|s| { - s.parse::() - .map_err(|_| format!("invalid color {:?}", s)) - })?; - let b = v - .get(2) - .ok_or_else(|| format!("invalid color {:?}", s)) - .and_then(|s| { - s.parse::() - .map_err(|_| format!("invalid color {:?}", s)) - })?; - let a = (v - .get(3) - .ok_or_else(|| format!("invalid color {:?}", s)) - .and_then(|s| { - s.parse::() - .map_err(|_| format!("invalid color {:?}", s)) - })? - * 255f64) as u16; + _ if s.trim().to_lowercase().starts_with("rgba(") => { + let (r, g, b, a) = parse_rgba(s.trim(), true)?; ColorProperty::Rgba(r, g, b, a) } - _ if s.starts_with("rgb(") => { - let v: Vec = s[5..(s.len() - 1)] - .split(',') - .map(|s| s.to_string()) - .collect(); - let r = v - .get(0) - .ok_or_else(|| format!("invalid color {:?}", s)) - .and_then(|s| { - s.parse::() - .map_err(|_| format!("invalid color {:?}", s)) - })?; - let g = v - .get(1) - .ok_or_else(|| format!("invalid color {:?}", s)) - .and_then(|s| { - s.parse::() - .map_err(|_| format!("invalid color {:?}", s)) - })?; - let b = v - .get(2) - .ok_or_else(|| format!("invalid color {:?}", s)) - .and_then(|s| { - s.parse::() - .map_err(|_| format!("invalid color {:?}", s)) - })?; - let a = 255; + _ if s.trim().to_lowercase().starts_with("rgb(") => { + let (r, g, b, a) = parse_rgba(s.trim(), false)?; ColorProperty::Rgba(r, g, b, a) } - // _ if s.starts_with("hsla(") => {} - // _ if s.starts_with("hsl(") => {} + _ if s.trim().to_lowercase().starts_with("hsla(") => { + let (h, s, l, a) = parse_hsla(s.trim(), true)?; + ColorProperty::Hsla(h, s, l, a) + } + _ if s.trim().to_lowercase().starts_with("hsl(") => { + let (h, s, l, a) = parse_hsla(s.trim(), false)?; + ColorProperty::Hsla(h, s, l, a) + } _ => return Err(format!("invalid color {:?}", s)), }; Ok(p) @@ -1439,7 +1423,6 @@ pub enum Property { #[cfg(test)] mod tests { - use crate::prop::Property::AnimationDelay; use crate::prop::*; #[test]