Parse CSS HSL

This commit is contained in:
Adrian Wozniak 2020-04-27 20:25:25 +02:00
parent b706da069e
commit 3be8fc7103
3 changed files with 140 additions and 77 deletions

View File

@ -1,5 +1,85 @@
use std::str::FromStr; 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<String> = 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::<f64>()
.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<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))
}
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))
}
})
.and_then(|s| {
s.parse::<u8>()
.map_err(|_| format!("invalid color {:?}", s))
})
}
pub enum Color { pub enum Color {
AliceBlue, AliceBlue,
AntiqueWhite, AntiqueWhite,

View File

@ -12,7 +12,7 @@ const INPUT: &str = "./jirs-client/js/styles.css";
type Css = Arc<RwLock<CssFile>>; type Css = Arc<RwLock<CssFile>>;
mod predefined; mod colors;
mod prop; mod prop;
#[derive(Debug)] #[derive(Debug)]

View File

@ -2,6 +2,8 @@ use std::iter::Peekable;
use std::str::{Chars, FromStr}; use std::str::{Chars, FromStr};
use std::vec::IntoIter; use std::vec::IntoIter;
use crate::colors::{parse_hsla, parse_rgba};
#[derive(Debug)] #[derive(Debug)]
pub struct CssTokenizer<'l> { pub struct CssTokenizer<'l> {
it: Peekable<Chars<'l>>, it: Peekable<Chars<'l>>,
@ -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)] #[derive(Debug)]
pub struct CssParser { pub struct CssParser {
it: Peekable<IntoIter<String>>, it: Peekable<IntoIter<String>>,
selectors: Vec<Selector>, selectors: Vec<Selector>,
pos: ParserPosition,
} }
impl CssParser { impl CssParser {
@ -70,6 +86,11 @@ impl CssParser {
Self { Self {
it: tokens.into_iter().peekable(), it: tokens.into_iter().peekable(),
selectors: vec![], selectors: vec![],
pos: ParserPosition {
line: 0,
line_character: 0,
character: 0,
},
} }
} }
@ -526,7 +547,21 @@ impl CssParser {
} }
fn consume(&mut self) -> Option<String> { fn consume(&mut self) -> Option<String> {
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<String, String> { fn expect_consume(&mut self) -> Result<String, String> {
@ -1111,8 +1146,8 @@ impl FromStr for ZIndexProperty {
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum ColorProperty { pub enum ColorProperty {
Name(String), Name(String),
Rgba(u16, u16, u16, u16), Rgba(u8, u8, u8, u8),
Hsla(u16, u16, u16, u16), Hsla(u16, u8, u8, f64),
Current, Current,
} }
@ -1120,99 +1155,48 @@ impl FromStr for ColorProperty {
type Err = String; type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
let p = match s.trim().to_lowercase().as_str() { let p = match s.trim() {
"currentcolor" => ColorProperty::Current, "currentColor" => ColorProperty::Current,
_ if s.len() == 7 && s.starts_with('#') => { _ if s.len() == 7 && s.starts_with('#') => {
let (r, g, b) = ( 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))?, .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))?, .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))?, .map_err(|_| format!("invalid color {:?}", s))?,
); );
ColorProperty::Rgba(r, g, b, 255) ColorProperty::Rgba(r, g, b, 255)
} }
_ if s.len() == 9 && s.starts_with('#') => { _ if s.len() == 9 && s.starts_with('#') => {
let (r, g, b, a) = ( 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))?, .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))?, .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))?, .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))?, .map_err(|_| format!("invalid color {:?}", s))?,
); );
ColorProperty::Rgba(r, g, b, a) ColorProperty::Rgba(r, g, b, a)
} }
_ if s.starts_with("rgba(") => { _ if s.trim().to_lowercase().starts_with("rgba(") => {
let v: Vec<String> = s[5..(s.len() - 1)] let (r, g, b, a) = parse_rgba(s.trim(), true)?;
.split(',')
.map(|s| s.to_string())
.collect();
let r = v
.get(0)
.ok_or_else(|| format!("invalid color {:?}", s))
.and_then(|s| {
s.parse::<u16>()
.map_err(|_| format!("invalid color {:?}", s))
})?;
let g = v
.get(1)
.ok_or_else(|| format!("invalid color {:?}", s))
.and_then(|s| {
s.parse::<u16>()
.map_err(|_| format!("invalid color {:?}", s))
})?;
let b = v
.get(2)
.ok_or_else(|| format!("invalid color {:?}", s))
.and_then(|s| {
s.parse::<u16>()
.map_err(|_| format!("invalid color {:?}", s))
})?;
let a = (v
.get(3)
.ok_or_else(|| format!("invalid color {:?}", s))
.and_then(|s| {
s.parse::<f64>()
.map_err(|_| format!("invalid color {:?}", s))
})?
* 255f64) as u16;
ColorProperty::Rgba(r, g, b, a) ColorProperty::Rgba(r, g, b, a)
} }
_ if s.starts_with("rgb(") => { _ if s.trim().to_lowercase().starts_with("rgb(") => {
let v: Vec<String> = s[5..(s.len() - 1)] let (r, g, b, a) = parse_rgba(s.trim(), false)?;
.split(',')
.map(|s| s.to_string())
.collect();
let r = v
.get(0)
.ok_or_else(|| format!("invalid color {:?}", s))
.and_then(|s| {
s.parse::<u16>()
.map_err(|_| format!("invalid color {:?}", s))
})?;
let g = v
.get(1)
.ok_or_else(|| format!("invalid color {:?}", s))
.and_then(|s| {
s.parse::<u16>()
.map_err(|_| format!("invalid color {:?}", s))
})?;
let b = v
.get(2)
.ok_or_else(|| format!("invalid color {:?}", s))
.and_then(|s| {
s.parse::<u16>()
.map_err(|_| format!("invalid color {:?}", s))
})?;
let a = 255;
ColorProperty::Rgba(r, g, b, a) ColorProperty::Rgba(r, g, b, a)
} }
// _ if s.starts_with("hsla(") => {} _ if s.trim().to_lowercase().starts_with("hsla(") => {
// _ if s.starts_with("hsl(") => {} 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)), _ => return Err(format!("invalid color {:?}", s)),
}; };
Ok(p) Ok(p)
@ -1439,7 +1423,6 @@ pub enum Property {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::prop::Property::AnimationDelay;
use crate::prop::*; use crate::prop::*;
#[test] #[test]