Chore: add in style crate, and abort any styligng
This commit is contained in:
1 parent
9dcee01b33
commit
c09b71f473
10 files changed
+7238
-11
No files matched your search
@@ -16,7 +16,6 @@ proc-macro-hack = "0.5.19"
|
|||||||
proc-macro2 = "1.0.6"
|
proc-macro2 = "1.0.6"
|
||||||
quote = "1.0"
|
quote = "1.0"
|
||||||
syn = { version = "1.0.11", features = ["full"] }
|
syn = { version = "1.0.11", features = ["full"] }
|
||||||
style-shared = { git = "https://github.com/derekdreery/style" }
|
|
||||||
|
|
||||||
# testing
|
# testing
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ use {
|
|||||||
proc_macro::TokenStream,
|
proc_macro::TokenStream,
|
||||||
proc_macro2::{Span, TokenStream as TokenStream2},
|
proc_macro2::{Span, TokenStream as TokenStream2},
|
||||||
quote::{quote, ToTokens, TokenStreamExt},
|
quote::{quote, ToTokens, TokenStreamExt},
|
||||||
style_shared::Styles,
|
|
||||||
syn::{
|
syn::{
|
||||||
ext::IdentExt,
|
ext::IdentExt,
|
||||||
parse::{Parse, ParseStream},
|
parse::{Parse, ParseStream},
|
||||||
@@ -267,15 +266,17 @@ impl Parse for Attr {
|
|||||||
let outer;
|
let outer;
|
||||||
syn::braced!(outer in s);
|
syn::braced!(outer in s);
|
||||||
// double brace for inline style.
|
// double brace for inline style.
|
||||||
if outer.peek(token::Brace) {
|
// todo!("Style support not ready yet");
|
||||||
let inner;
|
|
||||||
syn::braced!(inner in outer);
|
// if outer.peek(token::Brace) {
|
||||||
let styles: Styles = inner.parse()?;
|
// let inner;
|
||||||
MaybeExpr::Literal(LitStr::new(&styles.to_string(), Span::call_site()))
|
// syn::braced!(inner in outer);
|
||||||
} else {
|
// let styles: Styles = inner.parse()?;
|
||||||
// just parse as an expression
|
// MaybeExpr::Literal(LitStr::new(&styles.to_string(), Span::call_site()))
|
||||||
MaybeExpr::Expr(outer.parse()?)
|
// } else {
|
||||||
}
|
// just parse as an expression
|
||||||
|
MaybeExpr::Expr(outer.parse()?)
|
||||||
|
// }
|
||||||
} else {
|
} else {
|
||||||
s.parse()?
|
s.parse()?
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ use syn::{
|
|||||||
mod fc;
|
mod fc;
|
||||||
mod htm;
|
mod htm;
|
||||||
mod ifmt;
|
mod ifmt;
|
||||||
|
// mod styles;
|
||||||
|
|
||||||
/// The html! macro makes it easy for developers to write jsx-style markup in their components.
|
/// The html! macro makes it easy for developers to write jsx-style markup in their components.
|
||||||
/// We aim to keep functional parity with html templates.
|
/// We aim to keep functional parity with html templates.
|
||||||
|
|||||||
@@ -0,0 +1,264 @@
|
|||||||
|
//! The `calc` functionality.
|
||||||
|
use crate::LengthPercentage;
|
||||||
|
use ::{
|
||||||
|
proc_macro2::TokenStream,
|
||||||
|
quote::{quote, ToTokens},
|
||||||
|
std::fmt,
|
||||||
|
syn::{
|
||||||
|
custom_keyword, parenthesized,
|
||||||
|
parse::{Parse, ParseStream},
|
||||||
|
Token,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Values that can be a calculaion (currently restricted to length & percentages)
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum Calc {
|
||||||
|
Calculated(CalcSum),
|
||||||
|
Normal(LengthPercentage),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Calc {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Calc::Calculated(inner) => write!(f, "calc({})", inner),
|
||||||
|
Calc::Normal(inner) => write!(f, "{}", inner),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for Calc {
|
||||||
|
fn parse(s: ParseStream) -> syn::Result<Self> {
|
||||||
|
custom_keyword!(calc);
|
||||||
|
if s.peek(calc) {
|
||||||
|
s.parse::<calc>()?;
|
||||||
|
let content;
|
||||||
|
parenthesized!(content in s);
|
||||||
|
Ok(Calc::Calculated(content.parse()?))
|
||||||
|
} else {
|
||||||
|
Ok(Calc::Normal(s.parse()?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToTokens for Calc {
|
||||||
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||||
|
tokens.extend(match self {
|
||||||
|
Calc::Calculated(inner) => quote!(style::Calc::Calculated(#inner)),
|
||||||
|
Calc::Normal(inner) => quote!(style::Calc::Normal(#inner)),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_calc() {
|
||||||
|
for (input, output) in vec![
|
||||||
|
("calc(10% - 20\"em\")", "calc(10% - 20em)"),
|
||||||
|
("calc(100% + 5px)", "calc(100% + 5px)"),
|
||||||
|
("calc(100% - 60px)", "calc(100% - 60px)"),
|
||||||
|
] {
|
||||||
|
assert_eq!(&syn::parse_str::<Calc>(input).unwrap().to_string(), output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub struct CalcSum {
|
||||||
|
pub first: CalcProduct,
|
||||||
|
pub rest: Vec<SumOp>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for CalcSum {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{}", self.first)?;
|
||||||
|
for op in self.rest.iter() {
|
||||||
|
write!(f, "{}", op)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for CalcSum {
|
||||||
|
fn parse(s: ParseStream) -> syn::Result<Self> {
|
||||||
|
let first: CalcProduct = s.parse()?;
|
||||||
|
let mut rest: Vec<SumOp> = vec![];
|
||||||
|
while SumOp::peek(s) {
|
||||||
|
rest.push(s.parse()?);
|
||||||
|
}
|
||||||
|
Ok(CalcSum { first, rest })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToTokens for CalcSum {
|
||||||
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||||
|
let first = &self.first;
|
||||||
|
let rest = self.rest.iter();
|
||||||
|
tokens.extend(quote! {
|
||||||
|
style::calc::CalcSum {
|
||||||
|
first: #first,
|
||||||
|
rest: vec![#(#rest,)*]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum SumOp {
|
||||||
|
Add(CalcProduct),
|
||||||
|
Sub(CalcProduct),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SumOp {
|
||||||
|
fn peek(s: ParseStream) -> bool {
|
||||||
|
s.peek(Token![+]) || s.peek(Token![-])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for SumOp {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
SumOp::Add(inner) => write!(f, " + {}", inner),
|
||||||
|
SumOp::Sub(inner) => write!(f, " - {}", inner),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for SumOp {
|
||||||
|
fn parse(s: ParseStream) -> syn::Result<Self> {
|
||||||
|
let lookahead = s.lookahead1();
|
||||||
|
if lookahead.peek(Token![+]) {
|
||||||
|
s.parse::<Token![+]>()?;
|
||||||
|
Ok(SumOp::Add(s.parse()?))
|
||||||
|
} else if lookahead.peek(Token![-]) {
|
||||||
|
s.parse::<Token![-]>()?;
|
||||||
|
Ok(SumOp::Sub(s.parse()?))
|
||||||
|
} else {
|
||||||
|
Err(lookahead.error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToTokens for SumOp {
|
||||||
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||||
|
tokens.extend(match self {
|
||||||
|
SumOp::Add(inner) => quote!(style::SumOp::Add(#inner)),
|
||||||
|
SumOp::Sub(inner) => quote!(style::SumOp::Sub(#inner)),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub struct CalcProduct {
|
||||||
|
pub first: CalcValue,
|
||||||
|
pub rest: Vec<ProductOp>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for CalcProduct {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{}", self.first)?;
|
||||||
|
for op in self.rest.iter() {
|
||||||
|
write!(f, "{}", op)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for CalcProduct {
|
||||||
|
fn parse(s: ParseStream) -> syn::Result<Self> {
|
||||||
|
let first: CalcValue = s.parse()?;
|
||||||
|
let mut rest: Vec<ProductOp> = vec![];
|
||||||
|
while ProductOp::peek(s) {
|
||||||
|
rest.push(s.parse()?);
|
||||||
|
}
|
||||||
|
Ok(CalcProduct { first, rest })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToTokens for CalcProduct {
|
||||||
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||||
|
let first = &self.first;
|
||||||
|
let rest = self.rest.iter();
|
||||||
|
tokens.extend(quote! {
|
||||||
|
style::calc::CalcProduct {
|
||||||
|
first: #first,
|
||||||
|
rest: vec![#(#rest,)*]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum ProductOp {
|
||||||
|
Mul(CalcValue),
|
||||||
|
// todo Div(Number),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProductOp {
|
||||||
|
pub fn peek(s: ParseStream) -> bool {
|
||||||
|
s.peek(Token![*]) // || s.peek(Token[/])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for ProductOp {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
ProductOp::Mul(inner) => write!(f, "*{}", inner),
|
||||||
|
//ProductOp::Div(inner) => write!(f, "/{}", inner),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for ProductOp {
|
||||||
|
fn parse(s: ParseStream) -> syn::Result<Self> {
|
||||||
|
let lookahead = s.lookahead1();
|
||||||
|
if lookahead.peek(Token![*]) {
|
||||||
|
s.parse::<Token![*]>()?;
|
||||||
|
Ok(ProductOp::Mul(s.parse()?))
|
||||||
|
/*
|
||||||
|
} else if lookahead.peek(Token![/]) {
|
||||||
|
s.parse::<Token![/]>()?;
|
||||||
|
Ok(ProductOp::Div(s.parse()?))
|
||||||
|
*/
|
||||||
|
} else {
|
||||||
|
Err(lookahead.error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToTokens for ProductOp {
|
||||||
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||||
|
tokens.extend(match self {
|
||||||
|
ProductOp::Mul(inner) => quote!(style::ProductOp::Mul(#inner)),
|
||||||
|
//ProductOp::Div(inner) => quote!(style::ProductOp::Div(#inner)),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum CalcValue {
|
||||||
|
LengthPercentage(LengthPercentage),
|
||||||
|
// todo more variants
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for CalcValue {
|
||||||
|
fn parse(s: ParseStream) -> syn::Result<Self> {
|
||||||
|
Ok(CalcValue::LengthPercentage(s.parse()?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for CalcValue {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
CalcValue::LengthPercentage(inner) => write!(f, "{}", inner),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToTokens for CalcValue {
|
||||||
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||||
|
tokens.extend(match self {
|
||||||
|
CalcValue::LengthPercentage(inner) => {
|
||||||
|
quote!(style::CalcValue::LengthPercentage(#inner))
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,854 @@
|
|||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
/// A color that possibly is possibly code, rather than a literal
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum DynamicColor {
|
||||||
|
Literal(Color),
|
||||||
|
/// The type of the block is not checked here (it is checked by typeck).
|
||||||
|
Dynamic(syn::Block),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DynamicColor {
|
||||||
|
pub fn is_dynamic(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
DynamicColor::Dynamic(_) => true,
|
||||||
|
DynamicColor::Literal(_) => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for DynamicColor {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
DynamicColor::Dynamic(_) => Ok(()),
|
||||||
|
DynamicColor::Literal(color) => color.fmt(f),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO other color variants.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub enum Color {
|
||||||
|
HexRGB(u8, u8, u8),
|
||||||
|
HexRGBA(u8, u8, u8, u8),
|
||||||
|
// Invariants: `0 <= .0 < 360`, `0 <= .1 < 100`, `0 <= .2 < 100`.
|
||||||
|
HSL(f64, f64, f64),
|
||||||
|
// Invariants: `0 <= .0 < 360`, `0 <= .1 < 100`, `0 <= .2 < 100`, `0 <= .3 < 1`.
|
||||||
|
HSLA(f64, f64, f64, f64),
|
||||||
|
|
||||||
|
// Red HTML Color Names
|
||||||
|
/// rgb(205, 92, 92)
|
||||||
|
IndianRed,
|
||||||
|
/// rgb(240, 128, 128)
|
||||||
|
LightCoral,
|
||||||
|
/// rgb(250, 128, 114)
|
||||||
|
Salmon,
|
||||||
|
/// rgb(233, 150, 122)
|
||||||
|
DarkSalmon,
|
||||||
|
/// rgb(255, 160, 122)
|
||||||
|
LightSalmon,
|
||||||
|
/// rgb(220, 20, 60)
|
||||||
|
Crimson,
|
||||||
|
/// rgb(255, 0, 0)
|
||||||
|
Red,
|
||||||
|
/// rgb(178, 34, 34)
|
||||||
|
FireBrick,
|
||||||
|
/// rgb(139, 0, 0)
|
||||||
|
DarkRed,
|
||||||
|
// Pink HTML Color Names
|
||||||
|
/// rgb(255, 192, 203)
|
||||||
|
Pink,
|
||||||
|
/// rgb(255, 182, 193)
|
||||||
|
LightPink,
|
||||||
|
/// rgb(255, 105, 180)
|
||||||
|
HotPink,
|
||||||
|
/// rgb(255, 20, 147)
|
||||||
|
DeepPink,
|
||||||
|
/// rgb(199, 21, 133)
|
||||||
|
MediumVioletRed,
|
||||||
|
/// rgb(219, 112, 147)
|
||||||
|
PaleVioletRed,
|
||||||
|
//Orange HTML Color Names
|
||||||
|
// /// rgb(255, 160, 122) redefined
|
||||||
|
// LightSalmon,
|
||||||
|
/// rgb(255, 127, 80)
|
||||||
|
Coral,
|
||||||
|
/// rgb(255, 99, 71)
|
||||||
|
Tomato,
|
||||||
|
/// rgb(255, 69, 0)
|
||||||
|
OrangeRed,
|
||||||
|
/// rgb(255, 140, 0)
|
||||||
|
DarkOrange,
|
||||||
|
/// rgb(255, 165, 0)
|
||||||
|
Orange,
|
||||||
|
// Yellow HTML Color Names
|
||||||
|
/// rgb(255, 215, 0)
|
||||||
|
Gold,
|
||||||
|
/// rgb(255, 255, 0)
|
||||||
|
Yellow,
|
||||||
|
/// rgb(255, 255, 224)
|
||||||
|
LightYellow,
|
||||||
|
/// rgb(255, 250, 205)
|
||||||
|
LemonChiffon,
|
||||||
|
/// rgb(250, 250, 210)
|
||||||
|
LightGoldenrodYellow,
|
||||||
|
/// rgb(255, 239, 213)
|
||||||
|
PapayaWhip,
|
||||||
|
/// rgb(255, 228, 181)
|
||||||
|
Moccasin,
|
||||||
|
/// rgb(255, 218, 185)
|
||||||
|
PeachPuff,
|
||||||
|
/// rgb(238, 232, 170)
|
||||||
|
PaleGoldenrod,
|
||||||
|
/// rgb(240, 230, 140)
|
||||||
|
Khaki,
|
||||||
|
/// rgb(189, 183, 107)
|
||||||
|
DarkKhaki,
|
||||||
|
// Purple HTML Color Names
|
||||||
|
/// rgb(230, 230, 250)
|
||||||
|
Lavender,
|
||||||
|
/// rgb(216, 191, 216)
|
||||||
|
Thistle,
|
||||||
|
/// rgb(221, 160, 221)
|
||||||
|
Plum,
|
||||||
|
/// rgb(238, 130, 238)
|
||||||
|
Violet,
|
||||||
|
/// rgb(218, 112, 214)
|
||||||
|
Orchid,
|
||||||
|
/// rgb(255, 0, 255)
|
||||||
|
Fuchsia,
|
||||||
|
/// rgb(255, 0, 255)
|
||||||
|
Magenta,
|
||||||
|
/// rgb(186, 85, 211)
|
||||||
|
MediumOrchid,
|
||||||
|
/// rgb(147, 112, 219)
|
||||||
|
MediumPurple,
|
||||||
|
/// rgb(102, 51, 153)
|
||||||
|
RebeccaPurple,
|
||||||
|
/// rgb(138, 43, 226)
|
||||||
|
BlueViolet,
|
||||||
|
/// rgb(148, 0, 211)
|
||||||
|
DarkViolet,
|
||||||
|
/// rgb(153, 50, 204)
|
||||||
|
DarkOrchid,
|
||||||
|
/// rgb(139, 0, 139)
|
||||||
|
DarkMagenta,
|
||||||
|
/// rgb(128, 0, 128)
|
||||||
|
Purple,
|
||||||
|
/// rgb(75, 0, 130)
|
||||||
|
Indigo,
|
||||||
|
/// rgb(106, 90, 205)
|
||||||
|
SlateBlue,
|
||||||
|
/// rgb(72, 61, 139)
|
||||||
|
DarkSlateBlue,
|
||||||
|
/// rgb(123, 104, 238)
|
||||||
|
MediumSlateBlue,
|
||||||
|
// Green HTML Color Names
|
||||||
|
/// rgb(173, 255, 47)
|
||||||
|
GreenYellow,
|
||||||
|
/// rgb(127, 255, 0)
|
||||||
|
Chartreuse,
|
||||||
|
/// rgb(124, 252, 0)
|
||||||
|
LawnGreen,
|
||||||
|
/// rgb(0, 255, 0)
|
||||||
|
Lime,
|
||||||
|
/// rgb(50, 205, 50)
|
||||||
|
LimeGreen,
|
||||||
|
/// rgb(152, 251, 152)
|
||||||
|
PaleGreen,
|
||||||
|
/// rgb(144, 238, 144)
|
||||||
|
LightGreen,
|
||||||
|
/// rgb(0, 250, 154)
|
||||||
|
MediumSpringGreen,
|
||||||
|
/// rgb(0, 255, 127)
|
||||||
|
SpringGreen,
|
||||||
|
/// rgb(60, 179, 113)
|
||||||
|
MediumSeaGreen,
|
||||||
|
/// rgb(46, 139, 87)
|
||||||
|
SeaGreen,
|
||||||
|
/// rgb(34, 139, 34)
|
||||||
|
ForestGreen,
|
||||||
|
/// rgb(0, 128, 0)
|
||||||
|
Green,
|
||||||
|
/// rgb(0, 100, 0)
|
||||||
|
DarkGreen,
|
||||||
|
/// rgb(154, 205, 50)
|
||||||
|
YellowGreen,
|
||||||
|
/// rgb(107, 142, 35)
|
||||||
|
OliveDrab,
|
||||||
|
/// rgb(128, 128, 0)
|
||||||
|
Olive,
|
||||||
|
/// rgb(85, 107, 47)
|
||||||
|
DarkOliveGreen,
|
||||||
|
/// rgb(102, 205, 170)
|
||||||
|
MediumAquamarine,
|
||||||
|
/// rgb(143, 188, 139)
|
||||||
|
DarkSeaGreen,
|
||||||
|
/// rgb(32, 178, 170)
|
||||||
|
LightSeaGreen,
|
||||||
|
/// rgb(0, 139, 139)
|
||||||
|
DarkCyan,
|
||||||
|
/// rgb(0, 128, 128)
|
||||||
|
Teal,
|
||||||
|
// Blue HTML Color Names
|
||||||
|
/// rgb(0, 255, 255)
|
||||||
|
Aqua,
|
||||||
|
/// rgb(0, 255, 255)
|
||||||
|
Cyan,
|
||||||
|
/// rgb(224, 255, 255)
|
||||||
|
LightCyan,
|
||||||
|
/// rgb(175, 238, 238)
|
||||||
|
PaleTurquoise,
|
||||||
|
/// rgb(127, 255, 212)
|
||||||
|
Aquamarine,
|
||||||
|
/// rgb(64, 224, 208)
|
||||||
|
Turquoise,
|
||||||
|
/// rgb(72, 209, 204)
|
||||||
|
MediumTurquoise,
|
||||||
|
/// rgb(0, 206, 209)
|
||||||
|
DarkTurquoise,
|
||||||
|
/// rgb(95, 158, 160)
|
||||||
|
CadetBlue,
|
||||||
|
/// rgb(70, 130, 180)
|
||||||
|
SteelBlue,
|
||||||
|
/// rgb(176, 196, 222)
|
||||||
|
LightSteelBlue,
|
||||||
|
/// rgb(176, 224, 230)
|
||||||
|
PowderBlue,
|
||||||
|
/// rgb(173, 216, 230)
|
||||||
|
LightBlue,
|
||||||
|
/// rgb(135, 206, 235)
|
||||||
|
SkyBlue,
|
||||||
|
/// rgb(135, 206, 250)
|
||||||
|
LightSkyBlue,
|
||||||
|
/// rgb(0, 191, 255)
|
||||||
|
DeepSkyBlue,
|
||||||
|
/// rgb(30, 144, 255)
|
||||||
|
DodgerBlue,
|
||||||
|
/// rgb(100, 149, 237)
|
||||||
|
CornflowerBlue,
|
||||||
|
// /// rgb(123, 104, 238) duplicate
|
||||||
|
//MediumSlateBlue,
|
||||||
|
/// rgb(65, 105, 225)
|
||||||
|
RoyalBlue,
|
||||||
|
/// rgb(0, 0, 255)
|
||||||
|
Blue,
|
||||||
|
/// rgb(0, 0, 205)
|
||||||
|
MediumBlue,
|
||||||
|
/// rgb(0, 0, 139)
|
||||||
|
DarkBlue,
|
||||||
|
/// rgb(0, 0, 128)
|
||||||
|
Navy,
|
||||||
|
/// rgb(25, 25, 112)
|
||||||
|
MidnightBlue,
|
||||||
|
// Brown HTML Color Names
|
||||||
|
/// rgb(255, 248, 220)
|
||||||
|
Cornsilk,
|
||||||
|
/// rgb(255, 235, 205)
|
||||||
|
BlanchedAlmond,
|
||||||
|
/// rgb(255, 228, 196)
|
||||||
|
Bisque,
|
||||||
|
/// rgb(255, 222, 173)
|
||||||
|
NavajoWhite,
|
||||||
|
/// rgb(245, 222, 179)
|
||||||
|
Wheat,
|
||||||
|
/// rgb(222, 184, 135)
|
||||||
|
BurlyWood,
|
||||||
|
/// rgb(210, 180, 140)
|
||||||
|
Tan,
|
||||||
|
/// rgb(188, 143, 143)
|
||||||
|
RosyBrown,
|
||||||
|
/// rgb(244, 164, 96)
|
||||||
|
SandyBrown,
|
||||||
|
/// rgb(218, 165, 32)
|
||||||
|
Goldenrod,
|
||||||
|
/// rgb(184, 134, 11)
|
||||||
|
DarkGoldenrod,
|
||||||
|
/// rgb(205, 133, 63)
|
||||||
|
Peru,
|
||||||
|
/// rgb(210, 105, 30)
|
||||||
|
Chocolate,
|
||||||
|
/// rgb(139, 69, 19)
|
||||||
|
SaddleBrown,
|
||||||
|
/// rgb(160, 82, 45)
|
||||||
|
Sienna,
|
||||||
|
/// rgb(165, 42, 42)
|
||||||
|
Brown,
|
||||||
|
/// rgb(128, 0, 0)
|
||||||
|
Maroon,
|
||||||
|
// White HTML Color Names
|
||||||
|
/// rgb(255, 255, 255)
|
||||||
|
White,
|
||||||
|
/// rgb(255, 250, 250)
|
||||||
|
Snow,
|
||||||
|
/// rgb(240, 255, 240)
|
||||||
|
HoneyDew,
|
||||||
|
/// rgb(245, 255, 250)
|
||||||
|
MintCream,
|
||||||
|
/// rgb(240, 255, 255)
|
||||||
|
Azure,
|
||||||
|
/// rgb(240, 248, 255)
|
||||||
|
AliceBlue,
|
||||||
|
/// rgb(248, 248, 255)
|
||||||
|
GhostWhite,
|
||||||
|
/// rgb(245, 245, 245)
|
||||||
|
WhiteSmoke,
|
||||||
|
/// rgb(255, 245, 238)
|
||||||
|
SeaShell,
|
||||||
|
/// rgb(245, 245, 220)
|
||||||
|
Beige,
|
||||||
|
/// rgb(253, 245, 230)
|
||||||
|
OldLace,
|
||||||
|
/// rgb(255, 250, 240)
|
||||||
|
FloralWhite,
|
||||||
|
/// rgb(255, 255, 240)
|
||||||
|
Ivory,
|
||||||
|
/// rgb(250, 235, 215)
|
||||||
|
AntiqueWhite,
|
||||||
|
/// rgb(250, 240, 230)
|
||||||
|
Linen,
|
||||||
|
/// rgb(255, 240, 245)
|
||||||
|
LavenderBlush,
|
||||||
|
/// rgb(255, 228, 225)
|
||||||
|
MistyRose,
|
||||||
|
// Gray HTML Color Names
|
||||||
|
/// rgb(220, 220, 220)
|
||||||
|
Gainsboro,
|
||||||
|
/// rgb(211, 211, 211)
|
||||||
|
LightGray,
|
||||||
|
/// rgb(192, 192, 192)
|
||||||
|
Silver,
|
||||||
|
/// rgb(169, 169, 169)
|
||||||
|
DarkGray,
|
||||||
|
/// rgb(128, 128, 128)
|
||||||
|
Gray,
|
||||||
|
/// rgb(105, 105, 105)
|
||||||
|
DimGray,
|
||||||
|
/// rgb(119, 136, 153)
|
||||||
|
LightSlateGray,
|
||||||
|
/// rgb(112, 128, 144)
|
||||||
|
SlateGray,
|
||||||
|
/// rgb(47, 79, 79)
|
||||||
|
DarkSlateGray,
|
||||||
|
/// rgb(0, 0, 0)
|
||||||
|
Black,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Color {
|
||||||
|
// todo similar for others
|
||||||
|
pub fn to_rgb(self) -> Color {
|
||||||
|
use Color::*;
|
||||||
|
match self {
|
||||||
|
HexRGB(r, g, b) => HexRGB(r, g, b),
|
||||||
|
HexRGBA(r, g, b, _) => HexRGB(r, g, b),
|
||||||
|
HSL(h, s, l) => {
|
||||||
|
let s = s * 0.01; // percent conversion
|
||||||
|
let l = l * 0.01; // percent conversion
|
||||||
|
let (r, g, b) = hsl_to_rgb(h, s, l);
|
||||||
|
HexRGB((r * 255.0) as u8, (g * 255.0) as u8, (b * 255.0) as u8)
|
||||||
|
}
|
||||||
|
HSLA(h, s, l, _) => Color::to_rgb(HSL(h, s, l)),
|
||||||
|
IndianRed => HexRGB(205, 92, 92),
|
||||||
|
LightCoral => HexRGB(240, 128, 128),
|
||||||
|
Salmon => HexRGB(250, 128, 114),
|
||||||
|
DarkSalmon => HexRGB(233, 150, 122),
|
||||||
|
LightSalmon => HexRGB(255, 160, 122),
|
||||||
|
Crimson => HexRGB(220, 20, 60),
|
||||||
|
Red => HexRGB(255, 0, 0),
|
||||||
|
FireBrick => HexRGB(178, 34, 34),
|
||||||
|
DarkRed => HexRGB(139, 0, 0),
|
||||||
|
Pink => HexRGB(255, 192, 203),
|
||||||
|
LightPink => HexRGB(255, 182, 193),
|
||||||
|
HotPink => HexRGB(255, 105, 180),
|
||||||
|
DeepPink => HexRGB(255, 20, 147),
|
||||||
|
MediumVioletRed => HexRGB(199, 21, 133),
|
||||||
|
PaleVioletRed => HexRGB(219, 112, 147),
|
||||||
|
Coral => HexRGB(255, 127, 80),
|
||||||
|
Tomato => HexRGB(255, 99, 71),
|
||||||
|
OrangeRed => HexRGB(255, 69, 0),
|
||||||
|
DarkOrange => HexRGB(255, 140, 0),
|
||||||
|
Orange => HexRGB(255, 165, 0),
|
||||||
|
Gold => HexRGB(255, 215, 0),
|
||||||
|
Yellow => HexRGB(255, 255, 0),
|
||||||
|
LightYellow => HexRGB(255, 255, 224),
|
||||||
|
LemonChiffon => HexRGB(255, 250, 205),
|
||||||
|
LightGoldenrodYellow => HexRGB(250, 250, 210),
|
||||||
|
PapayaWhip => HexRGB(255, 239, 213),
|
||||||
|
Moccasin => HexRGB(255, 228, 181),
|
||||||
|
PeachPuff => HexRGB(255, 218, 185),
|
||||||
|
PaleGoldenrod => HexRGB(238, 232, 170),
|
||||||
|
Khaki => HexRGB(240, 230, 140),
|
||||||
|
DarkKhaki => HexRGB(189, 183, 107),
|
||||||
|
Lavender => HexRGB(230, 230, 250),
|
||||||
|
Thistle => HexRGB(216, 191, 216),
|
||||||
|
Plum => HexRGB(221, 160, 221),
|
||||||
|
Violet => HexRGB(238, 130, 238),
|
||||||
|
Orchid => HexRGB(218, 112, 214),
|
||||||
|
Fuchsia => HexRGB(255, 0, 255),
|
||||||
|
Magenta => HexRGB(255, 0, 255),
|
||||||
|
MediumOrchid => HexRGB(186, 85, 211),
|
||||||
|
MediumPurple => HexRGB(147, 112, 219),
|
||||||
|
RebeccaPurple => HexRGB(102, 51, 153),
|
||||||
|
BlueViolet => HexRGB(138, 43, 226),
|
||||||
|
DarkViolet => HexRGB(148, 0, 211),
|
||||||
|
DarkOrchid => HexRGB(153, 50, 204),
|
||||||
|
DarkMagenta => HexRGB(139, 0, 139),
|
||||||
|
Purple => HexRGB(128, 0, 128),
|
||||||
|
Indigo => HexRGB(75, 0, 130),
|
||||||
|
SlateBlue => HexRGB(106, 90, 205),
|
||||||
|
DarkSlateBlue => HexRGB(72, 61, 139),
|
||||||
|
MediumSlateBlue => HexRGB(123, 104, 238),
|
||||||
|
GreenYellow => HexRGB(173, 255, 47),
|
||||||
|
Chartreuse => HexRGB(127, 255, 0),
|
||||||
|
LawnGreen => HexRGB(124, 252, 0),
|
||||||
|
Lime => HexRGB(0, 255, 0),
|
||||||
|
LimeGreen => HexRGB(50, 205, 50),
|
||||||
|
PaleGreen => HexRGB(152, 251, 152),
|
||||||
|
LightGreen => HexRGB(144, 238, 144),
|
||||||
|
MediumSpringGreen => HexRGB(0, 250, 154),
|
||||||
|
SpringGreen => HexRGB(0, 255, 127),
|
||||||
|
MediumSeaGreen => HexRGB(60, 179, 113),
|
||||||
|
SeaGreen => HexRGB(46, 139, 87),
|
||||||
|
ForestGreen => HexRGB(34, 139, 34),
|
||||||
|
Green => HexRGB(0, 128, 0),
|
||||||
|
DarkGreen => HexRGB(0, 100, 0),
|
||||||
|
YellowGreen => HexRGB(154, 205, 50),
|
||||||
|
OliveDrab => HexRGB(107, 142, 35),
|
||||||
|
Olive => HexRGB(128, 128, 0),
|
||||||
|
DarkOliveGreen => HexRGB(85, 107, 47),
|
||||||
|
MediumAquamarine => HexRGB(102, 205, 170),
|
||||||
|
DarkSeaGreen => HexRGB(143, 188, 139),
|
||||||
|
LightSeaGreen => HexRGB(32, 178, 170),
|
||||||
|
DarkCyan => HexRGB(0, 139, 139),
|
||||||
|
Teal => HexRGB(0, 128, 128),
|
||||||
|
Aqua => HexRGB(0, 255, 255),
|
||||||
|
Cyan => HexRGB(0, 255, 255),
|
||||||
|
LightCyan => HexRGB(224, 255, 255),
|
||||||
|
PaleTurquoise => HexRGB(175, 238, 238),
|
||||||
|
Aquamarine => HexRGB(127, 255, 212),
|
||||||
|
Turquoise => HexRGB(64, 224, 208),
|
||||||
|
MediumTurquoise => HexRGB(72, 209, 204),
|
||||||
|
DarkTurquoise => HexRGB(0, 206, 209),
|
||||||
|
CadetBlue => HexRGB(95, 158, 160),
|
||||||
|
SteelBlue => HexRGB(70, 130, 180),
|
||||||
|
LightSteelBlue => HexRGB(176, 196, 222),
|
||||||
|
PowderBlue => HexRGB(176, 224, 230),
|
||||||
|
LightBlue => HexRGB(173, 216, 230),
|
||||||
|
SkyBlue => HexRGB(135, 206, 235),
|
||||||
|
LightSkyBlue => HexRGB(135, 206, 250),
|
||||||
|
DeepSkyBlue => HexRGB(0, 191, 255),
|
||||||
|
DodgerBlue => HexRGB(30, 144, 255),
|
||||||
|
CornflowerBlue => HexRGB(100, 149, 237),
|
||||||
|
RoyalBlue => HexRGB(65, 105, 225),
|
||||||
|
Blue => HexRGB(0, 0, 255),
|
||||||
|
MediumBlue => HexRGB(0, 0, 205),
|
||||||
|
DarkBlue => HexRGB(0, 0, 139),
|
||||||
|
Navy => HexRGB(0, 0, 128),
|
||||||
|
MidnightBlue => HexRGB(25, 25, 112),
|
||||||
|
Cornsilk => HexRGB(255, 248, 220),
|
||||||
|
BlanchedAlmond => HexRGB(255, 235, 205),
|
||||||
|
Bisque => HexRGB(255, 228, 196),
|
||||||
|
NavajoWhite => HexRGB(255, 222, 173),
|
||||||
|
Wheat => HexRGB(245, 222, 179),
|
||||||
|
BurlyWood => HexRGB(222, 184, 135),
|
||||||
|
Tan => HexRGB(210, 180, 140),
|
||||||
|
RosyBrown => HexRGB(188, 143, 143),
|
||||||
|
SandyBrown => HexRGB(244, 164, 96),
|
||||||
|
Goldenrod => HexRGB(218, 165, 32),
|
||||||
|
DarkGoldenrod => HexRGB(184, 134, 11),
|
||||||
|
Peru => HexRGB(205, 133, 63),
|
||||||
|
Chocolate => HexRGB(210, 105, 30),
|
||||||
|
SaddleBrown => HexRGB(139, 69, 19),
|
||||||
|
Sienna => HexRGB(160, 82, 45),
|
||||||
|
Brown => HexRGB(165, 42, 42),
|
||||||
|
Maroon => HexRGB(128, 0, 0),
|
||||||
|
White => HexRGB(255, 255, 255),
|
||||||
|
Snow => HexRGB(255, 250, 250),
|
||||||
|
HoneyDew => HexRGB(240, 255, 240),
|
||||||
|
MintCream => HexRGB(245, 255, 250),
|
||||||
|
Azure => HexRGB(240, 255, 255),
|
||||||
|
AliceBlue => HexRGB(240, 248, 255),
|
||||||
|
GhostWhite => HexRGB(248, 248, 255),
|
||||||
|
WhiteSmoke => HexRGB(245, 245, 245),
|
||||||
|
SeaShell => HexRGB(255, 245, 238),
|
||||||
|
Beige => HexRGB(245, 245, 220),
|
||||||
|
OldLace => HexRGB(253, 245, 230),
|
||||||
|
FloralWhite => HexRGB(255, 250, 240),
|
||||||
|
Ivory => HexRGB(255, 255, 240),
|
||||||
|
AntiqueWhite => HexRGB(250, 235, 215),
|
||||||
|
Linen => HexRGB(250, 240, 230),
|
||||||
|
LavenderBlush => HexRGB(255, 240, 245),
|
||||||
|
MistyRose => HexRGB(255, 228, 225),
|
||||||
|
Gainsboro => HexRGB(220, 220, 220),
|
||||||
|
LightGray => HexRGB(211, 211, 211),
|
||||||
|
Silver => HexRGB(192, 192, 192),
|
||||||
|
DarkGray => HexRGB(169, 169, 169),
|
||||||
|
Gray => HexRGB(128, 128, 128),
|
||||||
|
DimGray => HexRGB(105, 105, 105),
|
||||||
|
LightSlateGray => HexRGB(119, 136, 153),
|
||||||
|
SlateGray => HexRGB(112, 128, 144),
|
||||||
|
DarkSlateGray => HexRGB(47, 79, 79),
|
||||||
|
Black => HexRGB(0, 0, 0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_named(name: &str) -> Option<Self> {
|
||||||
|
// todo use a faster search (e.g. hashmap, aho-corasick)
|
||||||
|
use Color::*;
|
||||||
|
Some(match name {
|
||||||
|
"indianred" => IndianRed,
|
||||||
|
"lightcoral" => LightCoral,
|
||||||
|
"salmon" => Salmon,
|
||||||
|
"darksalmon" => DarkSalmon,
|
||||||
|
"lightsalmon" => LightSalmon,
|
||||||
|
"crimson" => Crimson,
|
||||||
|
"red" => Red,
|
||||||
|
"firebrick" => FireBrick,
|
||||||
|
"darkred" => DarkRed,
|
||||||
|
"pink" => Pink,
|
||||||
|
"lightpink" => LightPink,
|
||||||
|
"hotpink" => HotPink,
|
||||||
|
"deeppink" => DeepPink,
|
||||||
|
"mediumvioletred" => MediumVioletRed,
|
||||||
|
"palevioletred" => PaleVioletRed,
|
||||||
|
"coral" => Coral,
|
||||||
|
"tomato" => Tomato,
|
||||||
|
"orangered" => OrangeRed,
|
||||||
|
"darkorange" => DarkOrange,
|
||||||
|
"orange" => Orange,
|
||||||
|
"gold" => Gold,
|
||||||
|
"yellow" => Yellow,
|
||||||
|
"lightyellow" => LightYellow,
|
||||||
|
"lemonchiffon" => LemonChiffon,
|
||||||
|
"lightgoldenrodyellow" => LightGoldenrodYellow,
|
||||||
|
"papayawhip" => PapayaWhip,
|
||||||
|
"Moccasin" => Moccasin,
|
||||||
|
"Peachpuff" => PeachPuff,
|
||||||
|
"palegoldenrod" => PaleGoldenrod,
|
||||||
|
"khaki" => Khaki,
|
||||||
|
"darkkhaki" => DarkKhaki,
|
||||||
|
"lavender" => Lavender,
|
||||||
|
"thistle" => Thistle,
|
||||||
|
"plum" => Plum,
|
||||||
|
"violet" => Violet,
|
||||||
|
"orchid" => Orchid,
|
||||||
|
"fuchsia" => Fuchsia,
|
||||||
|
"magenta" => Magenta,
|
||||||
|
"mediumorchid" => MediumOrchid,
|
||||||
|
"mediumpurple" => MediumPurple,
|
||||||
|
"rebeccapurple" => RebeccaPurple,
|
||||||
|
"blueviolet" => BlueViolet,
|
||||||
|
"darkviolet" => DarkViolet,
|
||||||
|
"darkorchid" => DarkOrchid,
|
||||||
|
"darkmagenta" => DarkMagenta,
|
||||||
|
"purple" => Purple,
|
||||||
|
"indigo" => Indigo,
|
||||||
|
"slateblue" => SlateBlue,
|
||||||
|
"darkslateblue" => DarkSlateBlue,
|
||||||
|
"mediumslateblue" => MediumSlateBlue,
|
||||||
|
"greenyellow" => GreenYellow,
|
||||||
|
"chartreuse" => Chartreuse,
|
||||||
|
"lawngreen" => LawnGreen,
|
||||||
|
"lime" => Lime,
|
||||||
|
"limegreen" => LimeGreen,
|
||||||
|
"palegreen" => PaleGreen,
|
||||||
|
"lightgreen" => LightGreen,
|
||||||
|
"mediumspringgreen" => MediumSpringGreen,
|
||||||
|
"springgreen" => SpringGreen,
|
||||||
|
"mediumseagreen" => MediumSeaGreen,
|
||||||
|
"seagreen" => SeaGreen,
|
||||||
|
"forestgreen" => ForestGreen,
|
||||||
|
"green" => Green,
|
||||||
|
"darkgreen" => DarkGreen,
|
||||||
|
"yellowgreen" => YellowGreen,
|
||||||
|
"olivedrab" => OliveDrab,
|
||||||
|
"olive" => Olive,
|
||||||
|
"darkolivegreen" => DarkOliveGreen,
|
||||||
|
"mediumaquamarine" => MediumAquamarine,
|
||||||
|
"darkseagreen" => DarkSeaGreen,
|
||||||
|
"lightseagreen" => LightSeaGreen,
|
||||||
|
"darkcyan" => DarkCyan,
|
||||||
|
"teal" => Teal,
|
||||||
|
"aqua" => Aqua,
|
||||||
|
"cyan" => Cyan,
|
||||||
|
"lightcyan" => LightCyan,
|
||||||
|
"paleturquoise" => PaleTurquoise,
|
||||||
|
"aquamarine" => Aquamarine,
|
||||||
|
"turquoise" => Turquoise,
|
||||||
|
"mediumturquoise" => MediumTurquoise,
|
||||||
|
"darkturquoise" => DarkTurquoise,
|
||||||
|
"cadetblue" => CadetBlue,
|
||||||
|
"steelblue" => SteelBlue,
|
||||||
|
"lightsteelblue" => LightSteelBlue,
|
||||||
|
"powderblue" => PowderBlue,
|
||||||
|
"lightblue" => LightBlue,
|
||||||
|
"skyblue" => SkyBlue,
|
||||||
|
"lightskyblue" => LightSkyBlue,
|
||||||
|
"deepskyblue" => DeepSkyBlue,
|
||||||
|
"dodgerblue" => DodgerBlue,
|
||||||
|
"cornflowerblue" => CornflowerBlue,
|
||||||
|
"royalblue" => RoyalBlue,
|
||||||
|
"blue" => Blue,
|
||||||
|
"mediumblue" => MediumBlue,
|
||||||
|
"darkblue" => DarkBlue,
|
||||||
|
"navy" => Navy,
|
||||||
|
"midnightblue" => MidnightBlue,
|
||||||
|
"cornsilk" => Cornsilk,
|
||||||
|
"blanchedalmond" => BlanchedAlmond,
|
||||||
|
"bisque" => Bisque,
|
||||||
|
"navajowhite" => NavajoWhite,
|
||||||
|
"wheat" => Wheat,
|
||||||
|
"burlywood" => BurlyWood,
|
||||||
|
"tan" => Tan,
|
||||||
|
"rosybrown" => RosyBrown,
|
||||||
|
"sandybrown" => SandyBrown,
|
||||||
|
"goldenrod" => Goldenrod,
|
||||||
|
"darkgoldenrod" => DarkGoldenrod,
|
||||||
|
"peru" => Peru,
|
||||||
|
"chocolate" => Chocolate,
|
||||||
|
"saddlebrown" => SaddleBrown,
|
||||||
|
"sienna" => Sienna,
|
||||||
|
"brown" => Brown,
|
||||||
|
"maroon" => Maroon,
|
||||||
|
"white" => White,
|
||||||
|
"snow" => Snow,
|
||||||
|
"honeydew" => HoneyDew,
|
||||||
|
"mintcream" => MintCream,
|
||||||
|
"azure" => Azure,
|
||||||
|
"aliceblue" => AliceBlue,
|
||||||
|
"ghostwhite" => GhostWhite,
|
||||||
|
"whitesmoke" => WhiteSmoke,
|
||||||
|
"seashell" => SeaShell,
|
||||||
|
"beige" => Beige,
|
||||||
|
"oldlace" => OldLace,
|
||||||
|
"floralwhite" => FloralWhite,
|
||||||
|
"ivory" => Ivory,
|
||||||
|
"antiquewhite" => AntiqueWhite,
|
||||||
|
"linen" => Linen,
|
||||||
|
"lavenderblush" => LavenderBlush,
|
||||||
|
"mistyrose" => MistyRose,
|
||||||
|
"gainsboro" => Gainsboro,
|
||||||
|
"lightgray" => LightGray,
|
||||||
|
"silver" => Silver,
|
||||||
|
"darkgray" => DarkGray,
|
||||||
|
"gray" => Gray,
|
||||||
|
"dimgray" => DimGray,
|
||||||
|
"lightslategray" => LightSlateGray,
|
||||||
|
"slategray" => SlateGray,
|
||||||
|
"darkslategray" => DarkSlateGray,
|
||||||
|
"black" => Black,
|
||||||
|
_ => return None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Color {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
use Color::*;
|
||||||
|
match self {
|
||||||
|
HexRGB(r, g, b) => write!(f, "#{:02x}{:02x}{:02x}", r, g, b),
|
||||||
|
HexRGBA(r, g, b, a) => write!(f, "#{:02x}{:02x}{:02x}{:02x}", r, g, b, a),
|
||||||
|
HSL(h, s, l) => write!(f, "hsl({}, {}%, {}%)", h, s, l),
|
||||||
|
HSLA(h, s, l, a) => write!(f, "hsla({}, {}%, {}%, {})", h, s, l, a),
|
||||||
|
IndianRed => write!(f, "indianred"),
|
||||||
|
LightCoral => write!(f, "lightcoral"),
|
||||||
|
Salmon => write!(f, "salmon"),
|
||||||
|
DarkSalmon => write!(f, "darksalmon"),
|
||||||
|
LightSalmon => write!(f, "lightsalmon"),
|
||||||
|
Crimson => write!(f, "crimson"),
|
||||||
|
Red => write!(f, "red"),
|
||||||
|
FireBrick => write!(f, "firebrick"),
|
||||||
|
DarkRed => write!(f, "darkred"),
|
||||||
|
Pink => write!(f, "pink"),
|
||||||
|
LightPink => write!(f, "lightpink"),
|
||||||
|
HotPink => write!(f, "hotpink"),
|
||||||
|
DeepPink => write!(f, "deeppink"),
|
||||||
|
MediumVioletRed => write!(f, "mediumvioletred"),
|
||||||
|
PaleVioletRed => write!(f, "palevioletred"),
|
||||||
|
Coral => write!(f, "coral"),
|
||||||
|
Tomato => write!(f, "tomato"),
|
||||||
|
OrangeRed => write!(f, "orangered"),
|
||||||
|
DarkOrange => write!(f, "darkorange"),
|
||||||
|
Orange => write!(f, "orange"),
|
||||||
|
Gold => write!(f, "gold"),
|
||||||
|
Yellow => write!(f, "yellow"),
|
||||||
|
LightYellow => write!(f, "lightyellow"),
|
||||||
|
LemonChiffon => write!(f, "lemonchiffon"),
|
||||||
|
LightGoldenrodYellow => write!(f, "lightgoldenrodyellow"),
|
||||||
|
PapayaWhip => write!(f, "papayawhip"),
|
||||||
|
Moccasin => write!(f, "Moccasin"),
|
||||||
|
PeachPuff => write!(f, "Peachpuff"),
|
||||||
|
PaleGoldenrod => write!(f, "palegoldenrod"),
|
||||||
|
Khaki => write!(f, "khaki"),
|
||||||
|
DarkKhaki => write!(f, "darkkhaki"),
|
||||||
|
Lavender => write!(f, "lavender"),
|
||||||
|
Thistle => write!(f, "thistle"),
|
||||||
|
Plum => write!(f, "plum"),
|
||||||
|
Violet => write!(f, "violet"),
|
||||||
|
Orchid => write!(f, "orchid"),
|
||||||
|
Fuchsia => write!(f, "fuchsia"),
|
||||||
|
Magenta => write!(f, "magenta"),
|
||||||
|
MediumOrchid => write!(f, "mediumorchid"),
|
||||||
|
MediumPurple => write!(f, "mediumpurple"),
|
||||||
|
RebeccaPurple => write!(f, "rebeccapurple"),
|
||||||
|
BlueViolet => write!(f, "blueviolet"),
|
||||||
|
DarkViolet => write!(f, "darkviolet"),
|
||||||
|
DarkOrchid => write!(f, "darkorchid"),
|
||||||
|
DarkMagenta => write!(f, "darkmagenta"),
|
||||||
|
Purple => write!(f, "purple"),
|
||||||
|
Indigo => write!(f, "indigo"),
|
||||||
|
SlateBlue => write!(f, "slateblue"),
|
||||||
|
DarkSlateBlue => write!(f, "darkslateblue"),
|
||||||
|
MediumSlateBlue => write!(f, "mediumslateblue"),
|
||||||
|
GreenYellow => write!(f, "greenyellow"),
|
||||||
|
Chartreuse => write!(f, "chartreuse"),
|
||||||
|
LawnGreen => write!(f, "lawngreen"),
|
||||||
|
Lime => write!(f, "lime"),
|
||||||
|
LimeGreen => write!(f, "limegreen"),
|
||||||
|
PaleGreen => write!(f, "palegreen"),
|
||||||
|
LightGreen => write!(f, "lightgreen"),
|
||||||
|
MediumSpringGreen => write!(f, "mediumspringgreen"),
|
||||||
|
SpringGreen => write!(f, "springgreen"),
|
||||||
|
MediumSeaGreen => write!(f, "mediumseagreen"),
|
||||||
|
SeaGreen => write!(f, "seagreen"),
|
||||||
|
ForestGreen => write!(f, "forestgreen"),
|
||||||
|
Green => write!(f, "green"),
|
||||||
|
DarkGreen => write!(f, "darkgreen"),
|
||||||
|
YellowGreen => write!(f, "yellowgreen"),
|
||||||
|
OliveDrab => write!(f, "olivedrab"),
|
||||||
|
Olive => write!(f, "olive"),
|
||||||
|
DarkOliveGreen => write!(f, "darkolivegreen"),
|
||||||
|
MediumAquamarine => write!(f, "mediumaquamarine"),
|
||||||
|
DarkSeaGreen => write!(f, "darkseagreen"),
|
||||||
|
LightSeaGreen => write!(f, "lightseagreen"),
|
||||||
|
DarkCyan => write!(f, "darkcyan"),
|
||||||
|
Teal => write!(f, "teal"),
|
||||||
|
Aqua => write!(f, "aqua"),
|
||||||
|
Cyan => write!(f, "cyan"),
|
||||||
|
LightCyan => write!(f, "lightcyan"),
|
||||||
|
PaleTurquoise => write!(f, "paleturquoise"),
|
||||||
|
Aquamarine => write!(f, "aquamarine"),
|
||||||
|
Turquoise => write!(f, "turquoise"),
|
||||||
|
MediumTurquoise => write!(f, "mediumturquoise"),
|
||||||
|
DarkTurquoise => write!(f, "darkturquoise"),
|
||||||
|
CadetBlue => write!(f, "cadetblue"),
|
||||||
|
SteelBlue => write!(f, "steelblue"),
|
||||||
|
LightSteelBlue => write!(f, "lightsteelblue"),
|
||||||
|
PowderBlue => write!(f, "powderblue"),
|
||||||
|
LightBlue => write!(f, "lightblue"),
|
||||||
|
SkyBlue => write!(f, "skyblue"),
|
||||||
|
LightSkyBlue => write!(f, "lightskyblue"),
|
||||||
|
DeepSkyBlue => write!(f, "deepskyblue"),
|
||||||
|
DodgerBlue => write!(f, "dodgerblue"),
|
||||||
|
CornflowerBlue => write!(f, "cornflowerblue"),
|
||||||
|
RoyalBlue => write!(f, "royalblue"),
|
||||||
|
Blue => write!(f, "blue"),
|
||||||
|
MediumBlue => write!(f, "mediumblue"),
|
||||||
|
DarkBlue => write!(f, "darkblue"),
|
||||||
|
Navy => write!(f, "navy"),
|
||||||
|
MidnightBlue => write!(f, "midnightblue"),
|
||||||
|
Cornsilk => write!(f, "cornsilk"),
|
||||||
|
BlanchedAlmond => write!(f, "blanchedalmond"),
|
||||||
|
Bisque => write!(f, "bisque"),
|
||||||
|
NavajoWhite => write!(f, "navajowhite"),
|
||||||
|
Wheat => write!(f, "wheat"),
|
||||||
|
BurlyWood => write!(f, "burlywood"),
|
||||||
|
Tan => write!(f, "tan"),
|
||||||
|
RosyBrown => write!(f, "rosybrown"),
|
||||||
|
SandyBrown => write!(f, "sandybrown"),
|
||||||
|
Goldenrod => write!(f, "goldenrod"),
|
||||||
|
DarkGoldenrod => write!(f, "darkgoldenrod"),
|
||||||
|
Peru => write!(f, "peru"),
|
||||||
|
Chocolate => write!(f, "chocolate"),
|
||||||
|
SaddleBrown => write!(f, "saddlebrown"),
|
||||||
|
Sienna => write!(f, "sienna"),
|
||||||
|
Brown => write!(f, "brown"),
|
||||||
|
Maroon => write!(f, "maroon"),
|
||||||
|
White => write!(f, "white"),
|
||||||
|
Snow => write!(f, "snow"),
|
||||||
|
HoneyDew => write!(f, "honeydew"),
|
||||||
|
MintCream => write!(f, "mintcream"),
|
||||||
|
Azure => write!(f, "azure"),
|
||||||
|
AliceBlue => write!(f, "aliceblue"),
|
||||||
|
GhostWhite => write!(f, "ghostwhite"),
|
||||||
|
WhiteSmoke => write!(f, "whitesmoke"),
|
||||||
|
SeaShell => write!(f, "seashell"),
|
||||||
|
Beige => write!(f, "beige"),
|
||||||
|
OldLace => write!(f, "oldlace"),
|
||||||
|
FloralWhite => write!(f, "floralwhite"),
|
||||||
|
Ivory => write!(f, "ivory"),
|
||||||
|
AntiqueWhite => write!(f, "antiquewhite"),
|
||||||
|
Linen => write!(f, "linen"),
|
||||||
|
LavenderBlush => write!(f, "lavenderblush"),
|
||||||
|
MistyRose => write!(f, "mistyrose"),
|
||||||
|
Gainsboro => write!(f, "gainsboro"),
|
||||||
|
LightGray => write!(f, "lightgray"),
|
||||||
|
Silver => write!(f, "silver"),
|
||||||
|
DarkGray => write!(f, "darkgray"),
|
||||||
|
Gray => write!(f, "gray"),
|
||||||
|
DimGray => write!(f, "dimgray"),
|
||||||
|
LightSlateGray => write!(f, "lightslategray"),
|
||||||
|
SlateGray => write!(f, "slategray"),
|
||||||
|
DarkSlateGray => write!(f, "darkslategray"),
|
||||||
|
Black => write!(f, "black"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hsl_to_rgb(h: f64, s: f64, l: f64) -> (f64, f64, f64) {
|
||||||
|
debug_assert!(h >= 0.0 && h < 360.0);
|
||||||
|
debug_assert!(s >= 0.0 && s <= 1.0);
|
||||||
|
debug_assert!(l >= 0.0 && l <= 1.0);
|
||||||
|
let c = (1.0 - (2.0 * l - 1.0).abs()) * s;
|
||||||
|
let x = c * (1.0 - ((h / 60.0) % 2.0 - 1.0).abs());
|
||||||
|
let m = l - c * 0.5;
|
||||||
|
let (rp, gp, bp) = if h < 60.0 {
|
||||||
|
(c, x, 0.0)
|
||||||
|
} else if h < 120.0 {
|
||||||
|
(x, c, 0.0)
|
||||||
|
} else if h < 180.0 {
|
||||||
|
(0.0, c, x)
|
||||||
|
} else if h < 240.0 {
|
||||||
|
(0.0, x, c)
|
||||||
|
} else if h < 300.0 {
|
||||||
|
(x, 0.0, c)
|
||||||
|
} else {
|
||||||
|
(c, 0.0, x)
|
||||||
|
};
|
||||||
|
(rp + m, gp + m, bp + m)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_hex(hex: &str) -> Option<Color> {
|
||||||
|
match hex.len() {
|
||||||
|
3 => {
|
||||||
|
let r = u8::from_str_radix(hex.get(0..1)?, 16).ok()?;
|
||||||
|
let g = u8::from_str_radix(hex.get(1..2)?, 16).ok()?;
|
||||||
|
let b = u8::from_str_radix(hex.get(2..3)?, 16).ok()?;
|
||||||
|
// #fff is equivalent to #ffffff
|
||||||
|
Some(Color::HexRGB(r << 4 | r, g << 4 | g, b << 4 | b))
|
||||||
|
}
|
||||||
|
6 => {
|
||||||
|
let r = u8::from_str_radix(hex.get(0..2)?, 16).ok()?;
|
||||||
|
let g = u8::from_str_radix(hex.get(2..4)?, 16).ok()?;
|
||||||
|
let b = u8::from_str_radix(hex.get(4..6)?, 16).ok()?;
|
||||||
|
Some(Color::HexRGB(r, g, b))
|
||||||
|
}
|
||||||
|
8 => {
|
||||||
|
let r = u8::from_str_radix(hex.get(0..2)?, 16).ok()?;
|
||||||
|
let g = u8::from_str_radix(hex.get(2..4)?, 16).ok()?;
|
||||||
|
let b = u8::from_str_radix(hex.get(4..6)?, 16).ok()?;
|
||||||
|
let a = u8::from_str_radix(hex.get(6..8)?, 16).ok()?;
|
||||||
|
Some(Color::HexRGBA(r, g, b, a))
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_color_convert() {
|
||||||
|
let color = Color::HSL(60.0, 0.0, 100.0);
|
||||||
|
assert_eq!(color.to_rgb(), Color::HexRGB(255, 255, 255));
|
||||||
|
let color = Color::HSL(0.0, 100.0, 50.0);
|
||||||
|
assert_eq!(color.to_rgb(), Color::HexRGB(255, 0, 0));
|
||||||
|
}
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,502 @@
|
|||||||
|
//! Parse the various css types from strings directly (avoid pulling in syn if working at runtime)
|
||||||
|
//!
|
||||||
|
//! Differences to spec:
|
||||||
|
//! - Exponential floats are not supported for now.
|
||||||
|
use std::{char, fmt, iter};
|
||||||
|
|
||||||
|
const REPLACEMENT_CHAR: char = '�';
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
|
#[non_exhaustive] // Don't allow user to create
|
||||||
|
pub struct Span {
|
||||||
|
/// Inclusive
|
||||||
|
start: usize,
|
||||||
|
/// Exclusive
|
||||||
|
end: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Span {
|
||||||
|
fn new(start: usize, end: usize) -> Self {
|
||||||
|
assert!(end > start, "end must be greater than start");
|
||||||
|
Span { start, end }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.end - self.start
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct InvalidChar {
|
||||||
|
ch: char,
|
||||||
|
pos: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for InvalidChar {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"invalid character `{}` found at position {}",
|
||||||
|
self.ch.escape_debug(),
|
||||||
|
self.pos
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Lexer<'src> {
|
||||||
|
src: &'src str,
|
||||||
|
cursor: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'src> Lexer<'src> {
|
||||||
|
pub fn new(src: &'src str) -> Result<Lexer<'src>, InvalidChar> {
|
||||||
|
// Check that the user has already replaced characters as specified at
|
||||||
|
// https://www.w3.org/TR/css-syntax-3/#input-preprocessing
|
||||||
|
for (pos, ch) in src.char_indices() {
|
||||||
|
if ch == '\r' || ch == '\u{d}' || ch == '\0' {
|
||||||
|
return Err(InvalidChar { ch, pos });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Lexer { src, cursor: 0 })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn len(&self) -> usize {
|
||||||
|
self.src.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remaining(&self) -> usize {
|
||||||
|
self.src.len() - self.cursor
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn next_token(&mut self) -> Option<Token> {
|
||||||
|
match self.peek() {
|
||||||
|
Some(token) => {
|
||||||
|
self.consume(&token);
|
||||||
|
Some(token)
|
||||||
|
}
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn peek(&self) -> Option<Token> {
|
||||||
|
// https://www.w3.org/TR/css-syntax-3/#tokenizer-definitions
|
||||||
|
if let Some(comment) = self.comment() {
|
||||||
|
return Some(comment);
|
||||||
|
}
|
||||||
|
if let Some(tok) = self.whitespace() {
|
||||||
|
return Some(tok);
|
||||||
|
}
|
||||||
|
if let Some(tok) = self.string() {
|
||||||
|
return Some(tok);
|
||||||
|
}
|
||||||
|
match self.chars().next() {
|
||||||
|
Some(other) => Some(Token::new(
|
||||||
|
TokenKind::Error,
|
||||||
|
Span::new(self.cursor, self.cursor + other.len_utf8()),
|
||||||
|
)),
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn peek_n(&self, n: usize) -> Option<Token> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
todo!() //self.peek().is_none()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_span(&self, span: Span) -> &'src str {
|
||||||
|
if span.end > self.len() {
|
||||||
|
panic!("End of requested span is past the end of the source");
|
||||||
|
}
|
||||||
|
&self.src[span.start..span.end]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create another independent lexer at the given start point
|
||||||
|
fn fork(&self) -> Lexer {
|
||||||
|
Lexer {
|
||||||
|
src: self.src,
|
||||||
|
cursor: self.cursor,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn consume(&mut self, tok: &Token) {
|
||||||
|
assert!(
|
||||||
|
tok.len() <= self.remaining(),
|
||||||
|
"trying to consume a token that would be bigger \
|
||||||
|
than all remaining text"
|
||||||
|
);
|
||||||
|
self.cursor += tok.len();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resolve a position from cursor to position from start of src
|
||||||
|
fn resolve_pos(&self, pos: usize) -> usize {
|
||||||
|
self.cursor + pos
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a span from the current position with the given length
|
||||||
|
fn span(&self, len: usize) -> Span {
|
||||||
|
debug_assert!(self.cursor + len <= self.len());
|
||||||
|
Span::new(self.cursor, self.cursor + len)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a span from the current position to the end
|
||||||
|
fn span_to_end(&self) -> Span {
|
||||||
|
Span::new(self.cursor, self.len())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Iterate over the remaining chars of the input
|
||||||
|
fn chars(&self) -> std::str::Chars {
|
||||||
|
self.src[self.cursor..].chars()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Iterate over the remaining chars of the input
|
||||||
|
fn char_indices(&self) -> std::str::CharIndices {
|
||||||
|
self.src[self.cursor..].char_indices()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parse a comment
|
||||||
|
fn comment(&self) -> Option<Token> {
|
||||||
|
let mut ch_iter = self.char_indices().peekable();
|
||||||
|
if let Some((_, '/')) = ch_iter.next() {
|
||||||
|
if let Some((_, '*')) = ch_iter.next() {
|
||||||
|
loop {
|
||||||
|
match ch_iter.next() {
|
||||||
|
Some((_, '*')) => {
|
||||||
|
if let Some((idx, '/')) = ch_iter.peek() {
|
||||||
|
return Some(Token {
|
||||||
|
kind: TokenKind::Comment,
|
||||||
|
span: self.span(*idx + '/'.len_utf8()),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
return Some(Token::new(
|
||||||
|
TokenKind::UnclosedComment,
|
||||||
|
self.span_to_end(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parse whitespace
|
||||||
|
fn whitespace(&self) -> Option<Token> {
|
||||||
|
let mut ch_iter = self.chars();
|
||||||
|
let mut len = match ch_iter.next() {
|
||||||
|
Some(ch) if ch.is_ascii_whitespace() => ch.len_utf8(),
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
loop {
|
||||||
|
match ch_iter.next() {
|
||||||
|
Some(ch) if ch.is_ascii_whitespace() => len += ch.len_utf8(),
|
||||||
|
_ => break,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(Token {
|
||||||
|
kind: TokenKind::Whitespace,
|
||||||
|
span: self.span(len),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parse either a single or double quoted string
|
||||||
|
fn string(&self) -> Option<Token> {
|
||||||
|
let mut ch_iter = self.char_indices().fuse().peekable();
|
||||||
|
let delim = match ch_iter.next() {
|
||||||
|
Some((_, '"')) => '"',
|
||||||
|
Some((_, '\'')) => '\'',
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
let mut decoded_string = String::new();
|
||||||
|
loop {
|
||||||
|
match ch_iter.next() {
|
||||||
|
Some((end, ch)) if ch == delim => {
|
||||||
|
return Some(Token {
|
||||||
|
kind: TokenKind::String(decoded_string),
|
||||||
|
span: self.span(end + 1), // '"'.len_utf8() == 1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Some((end, '\n')) => {
|
||||||
|
return Some(Token {
|
||||||
|
kind: TokenKind::BadString(decoded_string),
|
||||||
|
span: self.span(end + 1), // '\n'.len_utf8() == 1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Some((_, '\\')) => match ch_iter.peek() {
|
||||||
|
Some((_, ch)) => {
|
||||||
|
if *ch == '\n' {
|
||||||
|
// do nothing - skip the backslash and newline.
|
||||||
|
ch_iter.next().unwrap();
|
||||||
|
} else if let Some(decoded_ch) = unescape(&mut ch_iter) {
|
||||||
|
decoded_string.push(decoded_ch);
|
||||||
|
} else {
|
||||||
|
decoded_string.push(ch_iter.next().unwrap().1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
// The spec says not to add the last '\'.
|
||||||
|
// a bad string will be returned on next pass
|
||||||
|
ch_iter.next().unwrap();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Some((_, ch)) => decoded_string.push(ch),
|
||||||
|
None => {
|
||||||
|
return Some(Token {
|
||||||
|
kind: TokenKind::BadString(decoded_string),
|
||||||
|
span: self.span_to_end(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
fn hash(&self) -> Option<Token> {
|
||||||
|
let mut iter = self.char_indices();
|
||||||
|
match iter.next() {
|
||||||
|
Some((_, '#')) => (),
|
||||||
|
None => return None,
|
||||||
|
};
|
||||||
|
match iter.next() {
|
||||||
|
Some((_, '\\')) => {}
|
||||||
|
_ => Some(Token {
|
||||||
|
kind: TokenKind::Delim('#'),
|
||||||
|
span: self.span(1),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'src> Iterator for Lexer<'src> {
|
||||||
|
type Item = Token;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
self.next_token()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub struct Token {
|
||||||
|
pub kind: TokenKind,
|
||||||
|
pub span: Span,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Token {
|
||||||
|
fn new(kind: TokenKind, span: Span) -> Self {
|
||||||
|
Token { kind, span }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.span.len()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub enum TokenKind {
|
||||||
|
Ident,
|
||||||
|
Function,
|
||||||
|
At,
|
||||||
|
Hash,
|
||||||
|
String(String),
|
||||||
|
BadString(String),
|
||||||
|
Url,
|
||||||
|
BadUrl,
|
||||||
|
Delim(char),
|
||||||
|
Number,
|
||||||
|
Percentage,
|
||||||
|
Dimension,
|
||||||
|
Whitespace,
|
||||||
|
/// <!--
|
||||||
|
CDO,
|
||||||
|
/// -->
|
||||||
|
CDC,
|
||||||
|
/// :
|
||||||
|
Colon,
|
||||||
|
/// ;
|
||||||
|
Semicolon,
|
||||||
|
/// ,
|
||||||
|
Comma,
|
||||||
|
/// [
|
||||||
|
LBracket,
|
||||||
|
/// ]
|
||||||
|
RBracket,
|
||||||
|
/// (
|
||||||
|
LParen,
|
||||||
|
/// )
|
||||||
|
RParen,
|
||||||
|
/// {
|
||||||
|
LBrace,
|
||||||
|
/// }
|
||||||
|
RBrace,
|
||||||
|
Comment,
|
||||||
|
UnclosedComment,
|
||||||
|
/// Could not parse the next token
|
||||||
|
Error,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helpers
|
||||||
|
|
||||||
|
/// Hex to char (up to 6 characters, e.g. "ffffff").
|
||||||
|
///
|
||||||
|
/// For example `"5c" => '\'`. Returns None if first char is not hex. Consumes the hex values.
|
||||||
|
fn unescape(input: &mut iter::Peekable<impl Iterator<Item = (usize, char)>>) -> Option<char> {
|
||||||
|
fn hex_acc(acc: &mut u32, next: char) {
|
||||||
|
debug_assert!(*acc & 0xf0000000 == 0); // make sure we don't overflow
|
||||||
|
(*acc) = (*acc << 4) + next.to_digit(16).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
let (_, ch) = match input.peek() {
|
||||||
|
Some((idx, ch)) if ch.is_ascii_hexdigit() => input.next().unwrap(),
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut acc = 0;
|
||||||
|
let mut count = 0;
|
||||||
|
hex_acc(&mut acc, ch);
|
||||||
|
|
||||||
|
// Here we use that the length of all valid hexdigits in utf8 is 1.
|
||||||
|
while count < 5
|
||||||
|
&& input
|
||||||
|
.peek()
|
||||||
|
.map(|(_, ch)| ch.is_ascii_hexdigit())
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
let ch = input.next().unwrap().1;
|
||||||
|
hex_acc(&mut acc, ch);
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// consume a whitespace char if it's there
|
||||||
|
if input
|
||||||
|
.peek()
|
||||||
|
.map(|(_, ch)| ch.is_ascii_whitespace())
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
input.next().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// maybe we could just directly use `char::from_u32(acc).unwrap_or(REPLACEMENT_CHAR)`
|
||||||
|
// null, surrogate, or too big
|
||||||
|
Some(
|
||||||
|
if acc == 0 || (acc >= 0xd800 && acc < 0xe000) || acc >= 0x110000 {
|
||||||
|
REPLACEMENT_CHAR
|
||||||
|
} else {
|
||||||
|
char::from_u32(acc).unwrap() // there should be no other invalid chars.
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::{Lexer, Span, Token, TokenKind};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn comment() {
|
||||||
|
println!();
|
||||||
|
let mut input = Lexer::new("/* a valid comment */").unwrap();
|
||||||
|
match input.next_token() {
|
||||||
|
Some(Token {
|
||||||
|
kind: TokenKind::Comment,
|
||||||
|
span,
|
||||||
|
}) => {
|
||||||
|
assert_eq!(
|
||||||
|
input.resolve_span(span),
|
||||||
|
"/* a valid comment */".to_string()
|
||||||
|
);
|
||||||
|
assert_eq!(span.len(), 21);
|
||||||
|
}
|
||||||
|
_ => panic!("not a comment"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut input = Lexer::new("/* a comment").unwrap();
|
||||||
|
match input.next_token() {
|
||||||
|
Some(Token {
|
||||||
|
kind: TokenKind::UnclosedComment,
|
||||||
|
span,
|
||||||
|
}) => {
|
||||||
|
assert_eq!(input.resolve_span(span), "/* a comment".to_string());
|
||||||
|
assert_eq!(span.len(), 12);
|
||||||
|
}
|
||||||
|
_ => panic!("not a comment"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut input = Lexer::new("/!* not a comment").unwrap();
|
||||||
|
match input.next_token() {
|
||||||
|
Some(Token {
|
||||||
|
kind: TokenKind::Error,
|
||||||
|
span,
|
||||||
|
}) => {}
|
||||||
|
_ => panic!("not a comment"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn string() {
|
||||||
|
println!("h");
|
||||||
|
let mut input = Lexer::new("\" a vali\\64\\e9 \\\n string \"").unwrap();
|
||||||
|
match input.next_token() {
|
||||||
|
Some(Token {
|
||||||
|
kind: TokenKind::String(s),
|
||||||
|
span,
|
||||||
|
}) => {
|
||||||
|
assert_eq!(s, " a validé string ".to_string());
|
||||||
|
assert_eq!(span.len(), 26);
|
||||||
|
}
|
||||||
|
_ => panic!("not a string"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut input = Lexer::new("' a valid string '").unwrap();
|
||||||
|
match input.next_token() {
|
||||||
|
Some(Token {
|
||||||
|
kind: TokenKind::String(s),
|
||||||
|
span,
|
||||||
|
}) => {
|
||||||
|
assert_eq!(s, " a valid string ".to_string());
|
||||||
|
assert_eq!(span.len(), 18);
|
||||||
|
}
|
||||||
|
_ => panic!("not a string"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut input = Lexer::new("\" a string").unwrap();
|
||||||
|
match input.next_token() {
|
||||||
|
Some(Token {
|
||||||
|
kind: TokenKind::BadString(s),
|
||||||
|
span,
|
||||||
|
}) => {
|
||||||
|
assert_eq!(s, " a string".to_string());
|
||||||
|
assert_eq!(span.len(), 10);
|
||||||
|
}
|
||||||
|
_ => panic!("not a string"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn whitespace() {
|
||||||
|
println!();
|
||||||
|
let mut input = Lexer::new("\n\t ").unwrap();
|
||||||
|
match input.next_token() {
|
||||||
|
Some(Token {
|
||||||
|
kind: TokenKind::Whitespace,
|
||||||
|
span,
|
||||||
|
}) => {
|
||||||
|
assert_eq!(input.resolve_span(span), "\n\t ".to_string());
|
||||||
|
assert_eq!(span.len(), 3);
|
||||||
|
}
|
||||||
|
_ => panic!("not a string"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn escape() {
|
||||||
|
let mut iter = "e9".char_indices().peekable();
|
||||||
|
assert_eq!(super::unescape(&mut iter), Some('é'));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
pub mod lexer;
|
||||||
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user