Parse CSS HSL
This commit is contained in:
parent
b706da069e
commit
3be8fc7103
@ -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,
|
@ -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)]
|
||||||
|
@ -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]
|
||||||
|
Loading…
Reference in New Issue
Block a user