wip: rename
This commit is contained in:
1 parent
93d4b8ca7c
commit
36d89beb34
20 files changed
+243
-7549
No files matched your search
@@ -20,7 +20,7 @@ fn main() {
|
||||
dioxus::desktop::launch(App, |c| c);
|
||||
}
|
||||
|
||||
fn App((cx, props): Scope<()>) -> Element {
|
||||
fn App(cx: Context, props: &()) -> Element {
|
||||
let text: &mut Vec<String> = cx.use_hook(|_| vec![String::from("abc=def")], |f| f);
|
||||
|
||||
let first = text.get_mut(0).unwrap();
|
||||
@@ -39,11 +39,7 @@ struct C1Props<'a> {
|
||||
text: &'a mut String,
|
||||
}
|
||||
|
||||
impl<'a> Drop for C1Props<'a> {
|
||||
fn drop(&mut self) {}
|
||||
}
|
||||
|
||||
fn Child1<'a>((cx, props): Scope<'a, C1Props>) -> Element<'a> {
|
||||
fn Child1(cx: Context, props: &C1Props) -> Element {
|
||||
let (left, right) = props.text.split_once("=").unwrap();
|
||||
|
||||
cx.render(rsx! {
|
||||
@@ -59,7 +55,7 @@ struct C2Props<'a> {
|
||||
text: &'a str,
|
||||
}
|
||||
|
||||
fn Child2<'a>((cx, props): Scope<'a, C2Props>) -> Element<'a> {
|
||||
fn Child2(cx: Context, props: &C2Props) -> Element {
|
||||
cx.render(rsx! {
|
||||
Child3 {
|
||||
text: props.text
|
||||
@@ -72,7 +68,7 @@ struct C3Props<'a> {
|
||||
text: &'a str,
|
||||
}
|
||||
|
||||
fn Child3<'a>((cx, props): Scope<'a, C3Props>) -> Element<'a> {
|
||||
fn Child3(cx: Context, props: &C3Props) -> Element {
|
||||
cx.render(rsx! {
|
||||
div { "{props.text}"}
|
||||
})
|
||||
|
||||
@@ -189,39 +189,9 @@ pub fn routable_derive_impl(input: Routable) -> TokenStream {
|
||||
let from_path = input.build_from_path();
|
||||
let to_path = input.build_to_path();
|
||||
|
||||
let not_found_route = match not_found_route {
|
||||
Some(route) => quote! { ::std::option::Option::Some(Self::#route) },
|
||||
None => quote! { ::std::option::Option::None },
|
||||
};
|
||||
|
||||
let cache_thread_local_ident = Ident::new(
|
||||
&format!("__{}_ROUTER_CURRENT_ROUTE_CACHE", ident),
|
||||
ident.span(),
|
||||
);
|
||||
|
||||
quote! {
|
||||
// ::std::thread_local! {
|
||||
// #[doc(hidden)]
|
||||
// #[allow(non_upper_case_globals)]
|
||||
// static #cache_thread_local_ident: ::std::cell::RefCell<::std::option::Option<#ident>> = ::std::cell::RefCell::new(::std::option::Option::None);
|
||||
// }
|
||||
|
||||
// #[automatically_derived]
|
||||
// impl ::dioxus::router::Routable for #ident {
|
||||
// #from_path
|
||||
// #to_path
|
||||
|
||||
// // fn routes() -> ::std::vec::Vec<&'static str> {
|
||||
// // ::std::vec![#(#ats),*]
|
||||
// // }
|
||||
|
||||
// fn not_found_route() -> ::std::option::Option<Self> {
|
||||
// #not_found_route
|
||||
// }
|
||||
|
||||
// // fn current_route() -> ::std::option::Option<Self> {
|
||||
// // #cache_thread_local_ident.with(|val| ::std::clone::Clone::clone(&*val.borrow()))
|
||||
// // }
|
||||
|
||||
// fn recognize(pathname: &str) -> ::std::option::Option<Self> {
|
||||
// todo!()
|
||||
@@ -237,12 +207,6 @@ pub fn routable_derive_impl(input: Routable) -> TokenStream {
|
||||
// // }
|
||||
// // route
|
||||
// }
|
||||
|
||||
// // fn cleanup() {
|
||||
// // #cache_thread_local_ident.with(move |val| {
|
||||
// // *val.borrow_mut() = ::std::option::Option::None;
|
||||
// // });
|
||||
// // }
|
||||
// }
|
||||
}
|
||||
}
|
||||
@@ -1,264 +0,0 @@
|
||||
//! 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
@@ -1,854 +0,0 @@
|
||||
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
@@ -1,502 +0,0 @@
|
||||
//! 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('é'));
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
pub mod lexer;
|
||||
File diff suppressed because it is too large.
Load diff
@@ -22,9 +22,7 @@ longest-increasing-subsequence = "0.1.0"
|
||||
# internall used
|
||||
log = { version = "0.4", features = ["release_max_level_off"] }
|
||||
|
||||
futures-util = { version = "0.3.15", default-features = false, features = [
|
||||
"std",
|
||||
] }
|
||||
futures-util = { version = "0.3.15", default-features = false }
|
||||
|
||||
smallvec = "1.6.1"
|
||||
|
||||
@@ -39,6 +37,8 @@ indexmap = "1.7.0"
|
||||
|
||||
# Serialize the Edits for use in Webview/Liveview instances
|
||||
serde = { version = "1", features = ["derive"], optional = true }
|
||||
|
||||
# todo: I want to get rid of this
|
||||
backtrace = "0.3.63"
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
@@ -201,3 +201,8 @@ Open questions:
|
||||
- react says no - they are continuous
|
||||
- but if we received both - then we don't need to diff, do we? run as many as we can and then finally diff?
|
||||
|
||||
|
||||
|
||||
## Render functions
|
||||
|
||||
One main issue of components is type eras
|
||||
@@ -5,12 +5,12 @@
|
||||
//! if the type supports PartialEq. The Properties trait is used by the rsx! and html! macros to generate the type-safe builder
|
||||
//! that ensures compile-time required and optional fields on cx.
|
||||
|
||||
use crate::innerlude::{Context, Element, LazyNodes, NodeLink};
|
||||
use crate::innerlude::{Context, Element, LazyNodes, VPortal};
|
||||
|
||||
pub struct FragmentProps(Element);
|
||||
pub struct FragmentBuilder<const BUILT: bool>(Element);
|
||||
impl FragmentBuilder<false> {
|
||||
pub fn children(self, children: Option<NodeLink>) -> FragmentBuilder<true> {
|
||||
pub fn children(self, children: Option<VPortal>) -> FragmentBuilder<true> {
|
||||
FragmentBuilder(children)
|
||||
}
|
||||
}
|
||||
|
||||
+109
-94
@@ -198,12 +198,13 @@ impl<'bump> DiffStack<'bump> {
|
||||
}
|
||||
}
|
||||
|
||||
fn push_subtree(&mut self) {
|
||||
self.nodes_created_stack.push(0);
|
||||
self.instructions.push(DiffInstruction::Mount {
|
||||
and: MountType::Append,
|
||||
});
|
||||
}
|
||||
// todo: subtrees
|
||||
// fn push_subtree(&mut self) {
|
||||
// self.nodes_created_stack.push(0);
|
||||
// self.instructions.push(DiffInstruction::Mount {
|
||||
// and: MountType::Append,
|
||||
// });
|
||||
// }
|
||||
|
||||
fn push_nodes_created(&mut self, count: usize) {
|
||||
self.nodes_created_stack.push(count);
|
||||
@@ -289,7 +290,7 @@ impl<'bump> DiffState<'bump> {
|
||||
1
|
||||
}
|
||||
|
||||
VNode::Linked(linked) => {
|
||||
VNode::Portal(linked) => {
|
||||
let node = unsafe { &*linked.node };
|
||||
let node: &VNode = unsafe { std::mem::transmute(node) };
|
||||
self.push_all_nodes(node)
|
||||
@@ -357,8 +358,8 @@ impl<'bump> DiffState<'bump> {
|
||||
VNode::Placeholder(anchor) => self.create_anchor_node(anchor, node),
|
||||
VNode::Element(element) => self.create_element_node(element, node),
|
||||
VNode::Fragment(frag) => self.create_fragment_node(frag),
|
||||
VNode::Component(component) => self.create_component_node(component),
|
||||
VNode::Linked(linked) => self.create_linked_node(linked),
|
||||
VNode::Component(component) => self.create_component_node(*component),
|
||||
VNode::Portal(linked) => self.create_linked_node(linked),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -422,14 +423,18 @@ impl<'bump> DiffState<'bump> {
|
||||
self.mutations.set_attribute(attr, real_id.as_u64());
|
||||
}
|
||||
|
||||
if !children.is_empty() && children.len() == 1 {
|
||||
if let VNode::Text(vtext) = children[0] {
|
||||
self.mutations.set_text(vtext.text, real_id.as_u64());
|
||||
return;
|
||||
}
|
||||
}
|
||||
// todo: the settext optimization
|
||||
|
||||
self.stack.create_children(children, MountType::Append);
|
||||
// if children.len() == 1 {
|
||||
// if let VNode::Text(vtext) = children[0] {
|
||||
// self.mutations.set_text(vtext.text, real_id.as_u64());
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
|
||||
if !children.is_empty() {
|
||||
self.stack.create_children(children, MountType::Append);
|
||||
}
|
||||
}
|
||||
|
||||
fn create_fragment_node(&mut self, frag: &'bump VFragment<'bump>) {
|
||||
@@ -454,8 +459,6 @@ impl<'bump> DiffState<'bump> {
|
||||
self.scopes
|
||||
.new_with_key(fc_ptr, caller, parent_scope, container, height, subtree);
|
||||
|
||||
log::debug!("container for scope {:?} is {:?}", new_idx, container);
|
||||
|
||||
// Actually initialize the caller's slot with the right address
|
||||
vcomponent.associated_scope.set(Some(new_idx));
|
||||
|
||||
@@ -463,18 +466,12 @@ impl<'bump> DiffState<'bump> {
|
||||
let cur_scope = self.scopes.get_scope(&parent_idx).unwrap();
|
||||
let extended = unsafe { std::mem::transmute(vcomponent) };
|
||||
cur_scope.items.borrow_mut().borrowed_props.push(extended);
|
||||
} else {
|
||||
// the props are currently bump allocated but we need to move them to the heap
|
||||
}
|
||||
|
||||
// TODO:
|
||||
// add noderefs to current noderef list Noderefs
|
||||
// add effects to current effect list Effects
|
||||
let new_component = self.scopes.get_scope(&new_idx).unwrap();
|
||||
|
||||
log::debug!(
|
||||
"initializing component {:?} with height {:?}",
|
||||
new_idx,
|
||||
height + 1
|
||||
);
|
||||
// TODO: add noderefs to current noderef list Noderefs
|
||||
let _new_component = self.scopes.get_scope(&new_idx).unwrap();
|
||||
|
||||
// Run the scope for one iteration to initialize it
|
||||
if self.scopes.run_scope(&new_idx) {
|
||||
@@ -482,16 +479,17 @@ impl<'bump> DiffState<'bump> {
|
||||
let nextnode = self.scopes.fin_head(&new_idx);
|
||||
self.stack.create_component(new_idx, nextnode);
|
||||
|
||||
if new_component.is_subtree_root.get() {
|
||||
self.stack.push_subtree();
|
||||
}
|
||||
// todo: subtrees
|
||||
// if new_component.is_subtree_root.get() {
|
||||
// self.stack.push_subtree();
|
||||
// }
|
||||
}
|
||||
|
||||
// Finally, insert this scope as a seen node.
|
||||
self.seen_scopes.insert(new_idx);
|
||||
}
|
||||
|
||||
fn create_linked_node(&mut self, link: &'bump NodeLink) {
|
||||
fn create_linked_node(&mut self, link: &'bump VPortal) {
|
||||
if link.scope_id.get().is_none() {
|
||||
if let Some(cur_scope) = self.stack.current_scope() {
|
||||
link.scope_id.set(Some(cur_scope));
|
||||
@@ -513,17 +511,17 @@ impl<'bump> DiffState<'bump> {
|
||||
self.diff_text_nodes(old, new, old_node, new_node);
|
||||
}
|
||||
(Component(old), Component(new)) => {
|
||||
self.diff_component_nodes(old_node, new_node, old, new)
|
||||
self.diff_component_nodes(old_node, new_node, *old, *new)
|
||||
}
|
||||
(Fragment(old), Fragment(new)) => self.diff_fragment_nodes(old, new),
|
||||
(Placeholder(old), Placeholder(new)) => new.dom_id.set(old.dom_id.get()),
|
||||
(Element(old), Element(new)) => self.diff_element_nodes(old, new, old_node, new_node),
|
||||
(Linked(old), Linked(new)) => self.diff_linked_nodes(old, new),
|
||||
(Portal(old), Portal(new)) => self.diff_linked_nodes(old, new),
|
||||
|
||||
// Anything else is just a basic replace and create
|
||||
(
|
||||
Linked(_) | Component(_) | Fragment(_) | Text(_) | Element(_) | Placeholder(_),
|
||||
Linked(_) | Component(_) | Fragment(_) | Text(_) | Element(_) | Placeholder(_),
|
||||
Portal(_) | Component(_) | Fragment(_) | Text(_) | Element(_) | Placeholder(_),
|
||||
Portal(_) | Component(_) | Fragment(_) | Text(_) | Element(_) | Placeholder(_),
|
||||
) => self
|
||||
.stack
|
||||
.create_node(new_node, MountType::Replace { old: old_node }),
|
||||
@@ -636,60 +634,76 @@ impl<'bump> DiffState<'bump> {
|
||||
}
|
||||
}
|
||||
|
||||
match (old.children.len(), new.children.len()) {
|
||||
(0, 0) => {}
|
||||
(1, 1) => {
|
||||
let old1 = &old.children[0];
|
||||
let new1 = &new.children[0];
|
||||
|
||||
match (old1, new1) {
|
||||
(VNode::Text(old_text), VNode::Text(new_text)) => {
|
||||
if old_text.text != new_text.text {
|
||||
self.mutations.set_text(new_text.text, root.as_u64());
|
||||
}
|
||||
}
|
||||
(VNode::Text(_old_text), _) => {
|
||||
self.stack.element_stack.push(root);
|
||||
self.stack.instructions.push(DiffInstruction::PopElement);
|
||||
self.stack.create_node(new1, MountType::Append);
|
||||
}
|
||||
(_, VNode::Text(new_text)) => {
|
||||
self.remove_nodes([old1], false);
|
||||
self.mutations.set_text(new_text.text, root.as_u64());
|
||||
}
|
||||
_ => {
|
||||
self.stack.element_stack.push(root);
|
||||
self.stack.instructions.push(DiffInstruction::PopElement);
|
||||
self.diff_children(old.children, new.children);
|
||||
}
|
||||
}
|
||||
}
|
||||
(0, 1) => {
|
||||
if let VNode::Text(text) = &new.children[0] {
|
||||
self.mutations.set_text(text.text, root.as_u64());
|
||||
} else {
|
||||
self.stack.element_stack.push(root);
|
||||
self.stack.instructions.push(DiffInstruction::PopElement);
|
||||
}
|
||||
}
|
||||
(0, _) => {
|
||||
self.mutations.edits.push(PushRoot {
|
||||
root: root.as_u64(),
|
||||
});
|
||||
self.stack.element_stack.push(root);
|
||||
self.stack.instructions.push(DiffInstruction::PopElement);
|
||||
self.stack.create_children(new.children, MountType::Append);
|
||||
}
|
||||
(_, 0) => {
|
||||
self.remove_nodes(old.children, false);
|
||||
self.mutations.set_text("", root.as_u64());
|
||||
}
|
||||
(_, _) => {
|
||||
self.stack.element_stack.push(root);
|
||||
self.stack.instructions.push(DiffInstruction::PopElement);
|
||||
self.diff_children(old.children, new.children);
|
||||
}
|
||||
if old.children.is_empty() && !new.children.is_empty() {
|
||||
self.mutations.edits.push(PushRoot {
|
||||
root: root.as_u64(),
|
||||
});
|
||||
self.stack.element_stack.push(root);
|
||||
self.stack.instructions.push(DiffInstruction::PopElement);
|
||||
self.stack.create_children(new.children, MountType::Append);
|
||||
} else {
|
||||
self.stack.element_stack.push(root);
|
||||
self.stack.instructions.push(DiffInstruction::PopElement);
|
||||
self.diff_children(old.children, new.children);
|
||||
}
|
||||
|
||||
// todo: this is for the "settext" optimization
|
||||
// it works, but i'm not sure if it's the direction we want to take
|
||||
|
||||
// match (old.children.len(), new.children.len()) {
|
||||
// (0, 0) => {}
|
||||
// (1, 1) => {
|
||||
// let old1 = &old.children[0];
|
||||
// let new1 = &new.children[0];
|
||||
|
||||
// match (old1, new1) {
|
||||
// (VNode::Text(old_text), VNode::Text(new_text)) => {
|
||||
// if old_text.text != new_text.text {
|
||||
// self.mutations.set_text(new_text.text, root.as_u64());
|
||||
// }
|
||||
// }
|
||||
// (VNode::Text(_old_text), _) => {
|
||||
// self.stack.element_stack.push(root);
|
||||
// self.stack.instructions.push(DiffInstruction::PopElement);
|
||||
// self.stack.create_node(new1, MountType::Append);
|
||||
// }
|
||||
// (_, VNode::Text(new_text)) => {
|
||||
// self.remove_nodes([old1], false);
|
||||
// self.mutations.set_text(new_text.text, root.as_u64());
|
||||
// }
|
||||
// _ => {
|
||||
// self.stack.element_stack.push(root);
|
||||
// self.stack.instructions.push(DiffInstruction::PopElement);
|
||||
// self.diff_children(old.children, new.children);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// (0, 1) => {
|
||||
// if let VNode::Text(text) = &new.children[0] {
|
||||
// self.mutations.set_text(text.text, root.as_u64());
|
||||
// } else {
|
||||
// self.stack.element_stack.push(root);
|
||||
// self.stack.instructions.push(DiffInstruction::PopElement);
|
||||
// }
|
||||
// }
|
||||
// (0, _) => {
|
||||
// self.mutations.edits.push(PushRoot {
|
||||
// root: root.as_u64(),
|
||||
// });
|
||||
// self.stack.element_stack.push(root);
|
||||
// self.stack.instructions.push(DiffInstruction::PopElement);
|
||||
// self.stack.create_children(new.children, MountType::Append);
|
||||
// }
|
||||
// (_, 0) => {
|
||||
// self.remove_nodes(old.children, false);
|
||||
// self.mutations.set_text("", root.as_u64());
|
||||
// }
|
||||
// (_, _) => {
|
||||
// self.stack.element_stack.push(root);
|
||||
// self.stack.instructions.push(DiffInstruction::PopElement);
|
||||
// self.diff_children(old.children, new.children);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
fn diff_component_nodes(
|
||||
@@ -721,6 +735,7 @@ impl<'bump> DiffState<'bump> {
|
||||
.get_scope_mut(&scope_addr)
|
||||
.unwrap_or_else(|| panic!("could not find {:?}", scope_addr))
|
||||
};
|
||||
|
||||
scope.caller = unsafe { std::mem::transmute(new.caller) };
|
||||
|
||||
// React doesn't automatically memoize, but we do.
|
||||
@@ -754,7 +769,7 @@ impl<'bump> DiffState<'bump> {
|
||||
self.diff_children(old.children, new.children);
|
||||
}
|
||||
|
||||
fn diff_linked_nodes(&mut self, old: &'bump NodeLink, new: &'bump NodeLink) {
|
||||
fn diff_linked_nodes(&mut self, old: &'bump VPortal, new: &'bump VPortal) {
|
||||
if !std::ptr::eq(old.node, new.node) {
|
||||
// if the ptrs are the same then theyr're the same
|
||||
let old: &VNode = unsafe { std::mem::transmute(&*old.node) };
|
||||
@@ -1207,7 +1222,7 @@ impl<'bump> DiffState<'bump> {
|
||||
VNode::Text(t) => break t.dom_id.get(),
|
||||
VNode::Element(t) => break t.dom_id.get(),
|
||||
VNode::Placeholder(t) => break t.dom_id.get(),
|
||||
VNode::Linked(l) => {
|
||||
VNode::Portal(l) => {
|
||||
let node: &VNode = unsafe { std::mem::transmute(&*l.node) };
|
||||
self.find_last_element(node);
|
||||
}
|
||||
@@ -1235,7 +1250,7 @@ impl<'bump> DiffState<'bump> {
|
||||
let scope_id = el.associated_scope.get().unwrap();
|
||||
search_node = Some(self.scopes.root_node(&scope_id));
|
||||
}
|
||||
VNode::Linked(link) => {
|
||||
VNode::Portal(link) => {
|
||||
let node = unsafe { std::mem::transmute(&*link.node) };
|
||||
search_node = Some(node);
|
||||
}
|
||||
@@ -1281,7 +1296,7 @@ impl<'bump> DiffState<'bump> {
|
||||
self.scopes.try_remove(&scope_id).unwrap();
|
||||
}
|
||||
|
||||
VNode::Linked(l) => {
|
||||
VNode::Portal(l) => {
|
||||
let node: &'bump VNode<'bump> = unsafe { std::mem::transmute(&*l.node) };
|
||||
self.replace_node(node, nodes_created);
|
||||
}
|
||||
@@ -1330,7 +1345,7 @@ impl<'bump> DiffState<'bump> {
|
||||
self.remove_nodes(f.children, gen_muts);
|
||||
}
|
||||
|
||||
VNode::Linked(l) => {
|
||||
VNode::Portal(l) => {
|
||||
let node = unsafe { std::mem::transmute(&*l.node) };
|
||||
self.remove_nodes(Some(node), gen_muts);
|
||||
}
|
||||
|
||||
@@ -31,14 +31,14 @@ pub(crate) mod innerlude {
|
||||
pub use crate::scopearena::*;
|
||||
pub use crate::virtual_dom::*;
|
||||
|
||||
pub type Element = Option<NodeLink>;
|
||||
pub type Element = Option<VPortal>;
|
||||
pub type FC<P> = for<'a> fn(Context<'a>, &'a P) -> Element;
|
||||
}
|
||||
|
||||
pub use crate::innerlude::{
|
||||
Attribute, Context, DioxusElement, DomEdit, Element, ElementId, EventPriority, IntoVNode,
|
||||
LazyNodes, Listener, MountType, Mutations, NodeFactory, Properties, SchedulerMsg, ScopeId,
|
||||
UserEvent, VElement, VFragment, VNode, VPlaceholder, VSuspended, VirtualDom, FC,
|
||||
UserEvent, VElement, VFragment, VNode, VirtualDom, FC,
|
||||
};
|
||||
|
||||
pub mod prelude {
|
||||
|
||||
+48
-62
@@ -129,7 +129,7 @@ pub enum VNode<'src> {
|
||||
///
|
||||
/// let node: NodeLink = vdom.render_vnode(rsx!( "hello" ));
|
||||
/// ```
|
||||
Linked(NodeLink),
|
||||
Portal(VPortal),
|
||||
}
|
||||
|
||||
impl<'src> VNode<'src> {
|
||||
@@ -139,10 +139,9 @@ impl<'src> VNode<'src> {
|
||||
VNode::Element(el) => el.key,
|
||||
VNode::Component(c) => c.key,
|
||||
VNode::Fragment(f) => f.key,
|
||||
|
||||
VNode::Text(_t) => None,
|
||||
VNode::Placeholder(_f) => None,
|
||||
VNode::Linked(_c) => None,
|
||||
VNode::Portal(_c) => None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,8 +160,7 @@ impl<'src> VNode<'src> {
|
||||
VNode::Text(el) => el.dom_id.get(),
|
||||
VNode::Element(el) => el.dom_id.get(),
|
||||
VNode::Placeholder(el) => el.dom_id.get(),
|
||||
|
||||
VNode::Linked(_) => None,
|
||||
VNode::Portal(_) => None,
|
||||
VNode::Fragment(_) => None,
|
||||
VNode::Component(_) => None,
|
||||
}
|
||||
@@ -171,7 +169,6 @@ impl<'src> VNode<'src> {
|
||||
pub(crate) fn children(&self) -> &[VNode<'src>] {
|
||||
match &self {
|
||||
VNode::Fragment(f) => f.children,
|
||||
VNode::Component(_c) => todo!("children are not accessible through this"),
|
||||
_ => &[],
|
||||
}
|
||||
}
|
||||
@@ -187,7 +184,7 @@ impl<'src> VNode<'src> {
|
||||
children: f.children,
|
||||
key: f.key,
|
||||
}),
|
||||
VNode::Linked(c) => VNode::Linked(NodeLink {
|
||||
VNode::Portal(c) => VNode::Portal(VPortal {
|
||||
scope_id: c.scope_id.clone(),
|
||||
link_idx: c.link_idx.clone(),
|
||||
node: c.node,
|
||||
@@ -204,15 +201,13 @@ impl Debug for VNode<'_> {
|
||||
.field("name", &el.tag_name)
|
||||
.field("key", &el.key)
|
||||
.finish(),
|
||||
|
||||
VNode::Text(t) => write!(s, "VNode::VText {{ text: {} }}", t.text),
|
||||
VNode::Placeholder(_) => write!(s, "VNode::VAnchor"),
|
||||
|
||||
VNode::Fragment(frag) => {
|
||||
write!(s, "VNode::VFragment {{ children: {:?} }}", frag.children)
|
||||
}
|
||||
VNode::Component(comp) => write!(s, "VNode::VComponent {{ fc: {:?}}}", comp.user_fc),
|
||||
VNode::Linked(c) => write!(s, "VNode::VCached {{ scope_id: {:?} }}", c.scope_id.get()),
|
||||
VNode::Portal(c) => write!(s, "VNode::VCached {{ scope_id: {:?} }}", c.scope_id.get()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -348,6 +343,32 @@ pub struct Listener<'bump> {
|
||||
RefCell<Option<BumpBox<'bump, dyn FnMut(std::sync::Arc<dyn Any + Send + Sync>) + 'bump>>>,
|
||||
}
|
||||
|
||||
/// A cached node is a "pointer" to a "rendered" node in a particular scope
|
||||
///
|
||||
/// It does not provide direct access to the node, so it doesn't carry any lifetime information with it
|
||||
///
|
||||
/// It is used during the diffing/rendering process as a runtime key into an existing set of nodes. The "render" key
|
||||
/// is essentially a unique key to guarantee safe usage of the Node.
|
||||
///
|
||||
/// Linked VNodes can only be made through the [`Context::render`] method
|
||||
///
|
||||
/// Typically, NodeLinks are found *not* in a VNode. When NodeLinks are in a VNode, the NodeLink was passed into
|
||||
/// an `rsx!` call.
|
||||
///
|
||||
/// todo: remove the raw pointer and use runtime checks instead
|
||||
#[derive(Debug)]
|
||||
pub struct VPortal {
|
||||
pub(crate) link_idx: Cell<usize>,
|
||||
pub(crate) scope_id: Cell<Option<ScopeId>>,
|
||||
pub(crate) node: *const VNode<'static>,
|
||||
}
|
||||
|
||||
impl PartialEq for VPortal {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.node == other.node
|
||||
}
|
||||
}
|
||||
|
||||
/// Virtual Components for custom user-defined components
|
||||
/// Only supports the functional syntax
|
||||
pub struct VComponent<'src> {
|
||||
@@ -366,45 +387,14 @@ pub struct VComponent<'src> {
|
||||
pub(crate) bump_props: *const (),
|
||||
|
||||
// during the "teardown" process we'll take the caller out so it can be dropped properly
|
||||
// pub(crate) caller: Option<VCompCaller<'src>>,
|
||||
pub(crate) caller: &'src dyn Fn(&'src Scope) -> Element,
|
||||
|
||||
pub(crate) comparator: Option<&'src dyn Fn(&VComponent) -> bool>,
|
||||
|
||||
// todo: re-add this
|
||||
pub(crate) drop_props: RefCell<Option<BumpBox<'src, dyn FnMut()>>>,
|
||||
}
|
||||
|
||||
pub struct VSuspended {
|
||||
pub task_id: usize,
|
||||
pub scope: Cell<Option<ScopeId>>,
|
||||
pub dom_id: Cell<Option<ElementId>>,
|
||||
pub parent: Cell<Option<ElementId>>,
|
||||
}
|
||||
|
||||
/// A cached node is a "pointer" to a "rendered" node in a particular scope
|
||||
///
|
||||
/// It does not provide direct access to the node, so it doesn't carry any lifetime information with it
|
||||
///
|
||||
/// It is used during the diffing/rendering process as a runtime key into an existing set of nodes. The "render" key
|
||||
/// is essentially a unique key to guarantee safe usage of the Node.
|
||||
///
|
||||
/// Linked VNodes can only be made through the [`Context::render`] method
|
||||
///
|
||||
/// Typically, NodeLinks are found *not* in a VNode. When NodeLinks are in a VNode, the NodeLink was passed into
|
||||
/// an `rsx!` call.
|
||||
#[derive(Debug)]
|
||||
pub struct NodeLink {
|
||||
pub(crate) link_idx: Cell<usize>,
|
||||
pub(crate) scope_id: Cell<Option<ScopeId>>,
|
||||
pub(crate) node: *const VNode<'static>,
|
||||
}
|
||||
|
||||
impl PartialEq for NodeLink {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.node == other.node
|
||||
}
|
||||
}
|
||||
|
||||
/// This struct provides an ergonomic API to quickly build VNodes.
|
||||
///
|
||||
/// NodeFactory is used to build VNodes in the component's memory space.
|
||||
@@ -624,8 +614,8 @@ impl<'a> NodeFactory<'a> {
|
||||
callback: BumpBox<'a, dyn FnMut(Arc<dyn Any + Send + Sync>) + 'a>,
|
||||
) -> Listener<'a> {
|
||||
Listener {
|
||||
mounted_node: Cell::new(None),
|
||||
event,
|
||||
mounted_node: Cell::new(None),
|
||||
callback: RefCell::new(Some(callback)),
|
||||
}
|
||||
}
|
||||
@@ -634,15 +624,14 @@ impl<'a> NodeFactory<'a> {
|
||||
self,
|
||||
node_iter: impl IntoIterator<Item = impl IntoVNode<'a> + 'c> + 'b,
|
||||
) -> VNode<'a> {
|
||||
let bump = self.bump;
|
||||
let mut nodes = bumpalo::collections::Vec::new_in(bump);
|
||||
let mut nodes = bumpalo::collections::Vec::new_in(self.bump);
|
||||
|
||||
for node in node_iter {
|
||||
nodes.push(node.into_vnode(self));
|
||||
}
|
||||
|
||||
if nodes.is_empty() {
|
||||
nodes.push(VNode::Placeholder(bump.alloc(VPlaceholder {
|
||||
nodes.push(VNode::Placeholder(self.bump.alloc(VPlaceholder {
|
||||
dom_id: empty_cell(),
|
||||
})));
|
||||
}
|
||||
@@ -657,26 +646,23 @@ impl<'a> NodeFactory<'a> {
|
||||
self,
|
||||
node_iter: impl IntoIterator<Item = impl IntoVNode<'a> + 'c> + 'b,
|
||||
) -> VNode<'a> {
|
||||
let bump = self.bump;
|
||||
let mut nodes = bumpalo::collections::Vec::new_in(bump);
|
||||
let mut nodes = bumpalo::collections::Vec::new_in(self.bump);
|
||||
|
||||
for node in node_iter {
|
||||
nodes.push(node.into_vnode(self));
|
||||
}
|
||||
|
||||
if nodes.is_empty() {
|
||||
nodes.push(VNode::Placeholder(bump.alloc(VPlaceholder {
|
||||
nodes.push(VNode::Placeholder(self.bump.alloc(VPlaceholder {
|
||||
dom_id: empty_cell(),
|
||||
})));
|
||||
}
|
||||
|
||||
let children = nodes.into_bump_slice();
|
||||
|
||||
// todo: add a backtrace
|
||||
if cfg!(debug_assertions) && children.len() > 1 && children.last().unwrap().key().is_none()
|
||||
{
|
||||
use backtrace::Backtrace;
|
||||
let bt = Backtrace::new();
|
||||
// todo: make the backtrace prettier or remove it altogether
|
||||
log::error!(
|
||||
r#"
|
||||
Warning: Each child in an array or iterator should have a unique "key" prop.
|
||||
@@ -685,7 +671,7 @@ impl<'a> NodeFactory<'a> {
|
||||
-------------
|
||||
{:?}
|
||||
"#,
|
||||
bt
|
||||
backtrace::Backtrace::new()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -740,7 +726,7 @@ impl<'a> NodeFactory<'a> {
|
||||
key: None,
|
||||
});
|
||||
let ptr = self.bump.alloc(frag) as *const _;
|
||||
Some(NodeLink {
|
||||
Some(VPortal {
|
||||
link_idx: Default::default(),
|
||||
scope_id: Default::default(),
|
||||
node: unsafe { std::mem::transmute(ptr) },
|
||||
@@ -847,10 +833,10 @@ impl IntoVNode<'_> for Arguments<'_> {
|
||||
}
|
||||
|
||||
// called cx.render from a helper function
|
||||
impl IntoVNode<'_> for Option<NodeLink> {
|
||||
impl IntoVNode<'_> for Option<VPortal> {
|
||||
fn into_vnode(self, _cx: NodeFactory) -> VNode {
|
||||
match self {
|
||||
Some(node) => VNode::Linked(node),
|
||||
Some(node) => VNode::Portal(node),
|
||||
None => {
|
||||
todo!()
|
||||
}
|
||||
@@ -860,10 +846,10 @@ impl IntoVNode<'_> for Option<NodeLink> {
|
||||
|
||||
// essentially passing elements through props
|
||||
// just build a new element in place
|
||||
impl IntoVNode<'_> for &Option<NodeLink> {
|
||||
impl IntoVNode<'_> for &Option<VPortal> {
|
||||
fn into_vnode(self, _cx: NodeFactory) -> VNode {
|
||||
match self {
|
||||
Some(node) => VNode::Linked(NodeLink {
|
||||
Some(node) => VNode::Portal(VPortal {
|
||||
link_idx: node.link_idx.clone(),
|
||||
scope_id: node.scope_id.clone(),
|
||||
node: node.node,
|
||||
@@ -876,15 +862,15 @@ impl IntoVNode<'_> for &Option<NodeLink> {
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoVNode<'_> for NodeLink {
|
||||
impl IntoVNode<'_> for VPortal {
|
||||
fn into_vnode(self, _cx: NodeFactory) -> VNode {
|
||||
VNode::Linked(self)
|
||||
VNode::Portal(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoVNode<'_> for &NodeLink {
|
||||
impl IntoVNode<'_> for &VPortal {
|
||||
fn into_vnode(self, _cx: NodeFactory) -> VNode {
|
||||
VNode::Linked(NodeLink {
|
||||
VNode::Portal(VPortal {
|
||||
link_idx: self.link_idx.clone(),
|
||||
scope_id: self.scope_id.clone(),
|
||||
node: self.node,
|
||||
|
||||
+44
-103
@@ -43,6 +43,7 @@ pub type Context<'a> = &'a Scope;
|
||||
/// use case they might have.
|
||||
pub struct Scope {
|
||||
pub(crate) parent_scope: Option<*mut Scope>,
|
||||
|
||||
pub(crate) container: ElementId,
|
||||
|
||||
pub(crate) our_arena_idx: ScopeId,
|
||||
@@ -61,7 +62,9 @@ pub struct Scope {
|
||||
|
||||
pub(crate) items: RefCell<SelfReferentialItems<'static>>,
|
||||
|
||||
pub(crate) hooks: HookList,
|
||||
pub(crate) hook_arena: Bump,
|
||||
pub(crate) hook_vals: RefCell<SmallVec<[*mut dyn Any; 5]>>,
|
||||
pub(crate) hook_idx: Cell<usize>,
|
||||
|
||||
pub(crate) shared_contexts: RefCell<HashMap<TypeId, Rc<dyn Any>>>,
|
||||
|
||||
@@ -100,7 +103,9 @@ impl Scope {
|
||||
///
|
||||
/// assert_eq!(base.subtree(), 0);
|
||||
/// ```
|
||||
pub fn subtree(&self) -> u32 {
|
||||
///
|
||||
/// todo: enable
|
||||
pub(crate) fn _subtree(&self) -> u32 {
|
||||
self.subtree.get()
|
||||
}
|
||||
|
||||
@@ -119,14 +124,13 @@ impl Scope {
|
||||
/// rsx!(cx, div { "Subtree {id}"})
|
||||
/// };
|
||||
/// ```
|
||||
pub fn create_subtree(&self) -> Option<u32> {
|
||||
///
|
||||
/// todo: enable subtree
|
||||
pub(crate) fn _create_subtree(&self) -> Option<u32> {
|
||||
if self.is_subtree_root.get() {
|
||||
None
|
||||
} else {
|
||||
todo!()
|
||||
// let cur = self.subtree().get();
|
||||
// self.shared.cur_subtree.set(cur + 1);
|
||||
// Some(cur)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,6 +169,7 @@ impl Scope {
|
||||
/// assert_eq!(base.parent(), None);
|
||||
/// ```
|
||||
pub fn parent(&self) -> Option<ScopeId> {
|
||||
// safety: the pointer to our parent is *always* valid thanks to the bump arena
|
||||
self.parent_scope.map(|p| unsafe { &*p }.our_arena_idx)
|
||||
}
|
||||
|
||||
@@ -189,11 +194,8 @@ impl Scope {
|
||||
///
|
||||
/// ## Notice: you should prefer using prepare_update and get_scope_id
|
||||
pub fn schedule_update(&self) -> Rc<dyn Fn() + 'static> {
|
||||
// pub fn schedule_update(&self) -> Rc<dyn Fn() + 'static> {
|
||||
let chan = self.sender.clone();
|
||||
let id = self.scope_id();
|
||||
let (chan, id) = (self.sender.clone(), self.scope_id());
|
||||
Rc::new(move || {
|
||||
// log::debug!("set on channel an update for scope {:?}", id);
|
||||
let _ = chan.unbounded_send(SchedulerMsg::Immediate(id));
|
||||
})
|
||||
}
|
||||
@@ -281,7 +283,11 @@ impl Scope {
|
||||
}
|
||||
}
|
||||
|
||||
/// Pushes the future onto the poll queue to be polled
|
||||
/// Pushes the future onto the poll queue to be polled after the component renders.
|
||||
///
|
||||
///
|
||||
///
|
||||
///
|
||||
/// The future is forcibly dropped if the component is not ready by the next render
|
||||
pub fn push_task<'src, F: Future<Output = ()>>(
|
||||
&'src self,
|
||||
@@ -292,7 +298,7 @@ impl Scope {
|
||||
F: 'src,
|
||||
{
|
||||
self.sender
|
||||
.unbounded_send(SchedulerMsg::TaskPushed(self.our_arena_idx))
|
||||
.unbounded_send(SchedulerMsg::NewTask(self.our_arena_idx))
|
||||
.unwrap();
|
||||
|
||||
// allocate the future
|
||||
@@ -326,7 +332,7 @@ impl Scope {
|
||||
/// cx.render(lazy_tree)
|
||||
/// }
|
||||
///```
|
||||
pub fn render<'src>(&'src self, rsx: Option<LazyNodes<'src, '_>>) -> Option<NodeLink> {
|
||||
pub fn render<'src>(&'src self, rsx: Option<LazyNodes<'src, '_>>) -> Option<VPortal> {
|
||||
let frame = self.wip_frame();
|
||||
let bump = &frame.bump;
|
||||
let factory = NodeFactory { bump };
|
||||
@@ -336,7 +342,7 @@ impl Scope {
|
||||
let node_ptr = node as *mut _;
|
||||
let node_ptr = unsafe { std::mem::transmute(node_ptr) };
|
||||
|
||||
let link = NodeLink {
|
||||
let link = VPortal {
|
||||
scope_id: Cell::new(Some(self.our_arena_idx)),
|
||||
link_idx: Cell::new(0),
|
||||
node: node_ptr,
|
||||
@@ -371,24 +377,35 @@ impl Scope {
|
||||
initializer: impl FnOnce(usize) -> State,
|
||||
runner: impl FnOnce(&'src mut State) -> Output,
|
||||
) -> Output {
|
||||
if self.hooks.at_end() {
|
||||
self.hooks.push_hook(initializer(self.hooks.len()));
|
||||
let mut vals = self.hook_vals.borrow_mut();
|
||||
let hook_len = vals.len();
|
||||
let cur_idx = self.hook_idx.get();
|
||||
|
||||
if cur_idx >= hook_len {
|
||||
let val = self.hook_arena.alloc(initializer(hook_len));
|
||||
vals.push(val);
|
||||
}
|
||||
|
||||
const HOOK_ERR_MSG: &str = r###"
|
||||
Unable to retrieve the hook that was initialized at this index.
|
||||
Consult the `rules of hooks` to understand how to use hooks properly.
|
||||
let state = vals
|
||||
.get(cur_idx)
|
||||
.and_then(|inn| {
|
||||
self.hook_idx.set(cur_idx + 1);
|
||||
let raw_box = unsafe { &mut **inn };
|
||||
raw_box.downcast_mut::<State>()
|
||||
})
|
||||
.expect(
|
||||
r###"
|
||||
Unable to retrieve the hook that was initialized at this index.
|
||||
Consult the `rules of hooks` to understand how to use hooks properly.
|
||||
|
||||
You likely used the hook in a conditional. Hooks rely on consistent ordering between renders.
|
||||
Functions prefixed with "use" should never be called conditionally.
|
||||
"###;
|
||||
You likely used the hook in a conditional. Hooks rely on consistent ordering between renders.
|
||||
Functions prefixed with "use" should never be called conditionally.
|
||||
"###,
|
||||
);
|
||||
|
||||
runner(self.hooks.next::<State>().expect(HOOK_ERR_MSG))
|
||||
runner(state)
|
||||
}
|
||||
}
|
||||
|
||||
// Important internal methods
|
||||
impl Scope {
|
||||
/// The "work in progress frame" represents the frame that is currently being worked on.
|
||||
pub(crate) fn wip_frame(&self) -> &BumpFrame {
|
||||
match self.generation.get() & 1 == 0 {
|
||||
@@ -449,11 +466,7 @@ impl BumpFrame {
|
||||
Self { bump, nodes }
|
||||
}
|
||||
|
||||
pub fn _allocated_bytes(&self) -> usize {
|
||||
self.bump.allocated_bytes()
|
||||
}
|
||||
|
||||
pub fn assign_nodelink(&self, node: &NodeLink) {
|
||||
pub fn assign_nodelink(&self, node: &VPortal) {
|
||||
let mut nodes = self.nodes.borrow_mut();
|
||||
|
||||
let len = nodes.len();
|
||||
@@ -463,78 +476,6 @@ impl BumpFrame {
|
||||
}
|
||||
}
|
||||
|
||||
/// An abstraction over internally stored data using a hook-based memory layout.
|
||||
///
|
||||
/// Hooks are allocated using Boxes and then our stored references are given out.
|
||||
///
|
||||
/// It's unsafe to "reset" the hooklist, but it is safe to add hooks into it.
|
||||
///
|
||||
/// Todo: this could use its very own bump arena, but that might be a tad overkill
|
||||
#[derive(Default)]
|
||||
pub(crate) struct HookList {
|
||||
arena: Bump,
|
||||
vals: RefCell<SmallVec<[*mut dyn Any; 5]>>,
|
||||
idx: Cell<usize>,
|
||||
}
|
||||
|
||||
impl HookList {
|
||||
pub fn new(capacity: usize) -> Self {
|
||||
Self {
|
||||
arena: Bump::with_capacity(capacity),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn next<T: 'static>(&self) -> Option<&mut T> {
|
||||
self.vals.borrow().get(self.idx.get()).and_then(|inn| {
|
||||
self.idx.set(self.idx.get() + 1);
|
||||
let raw_box = unsafe { &mut **inn };
|
||||
raw_box.downcast_mut::<T>()
|
||||
})
|
||||
}
|
||||
|
||||
/// This resets the internal iterator count
|
||||
/// It's okay that we've given out each hook, but now we have the opportunity to give it out again
|
||||
/// Therefore, resetting is considered unsafe
|
||||
///
|
||||
/// This should only be ran by Dioxus itself before "running scope".
|
||||
/// Dioxus knows how to descend through the tree to prevent mutable aliasing.
|
||||
pub(crate) unsafe fn reset(&self) {
|
||||
self.idx.set(0);
|
||||
}
|
||||
|
||||
pub(crate) fn push_hook<T: 'static>(&self, new: T) {
|
||||
let val = self.arena.alloc(new);
|
||||
self.vals.borrow_mut().push(val)
|
||||
}
|
||||
|
||||
pub(crate) fn len(&self) -> usize {
|
||||
self.vals.borrow().len()
|
||||
}
|
||||
|
||||
pub(crate) fn cur_idx(&self) -> usize {
|
||||
self.idx.get()
|
||||
}
|
||||
|
||||
pub(crate) fn at_end(&self) -> bool {
|
||||
self.cur_idx() >= self.len()
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.vals.borrow_mut().drain(..).for_each(|state| {
|
||||
let as_mut = unsafe { &mut *state };
|
||||
let boxed = unsafe { bumpalo::boxed::Box::from_raw(as_mut) };
|
||||
drop(boxed);
|
||||
});
|
||||
}
|
||||
|
||||
/// Get the ammount of memory a hooklist uses
|
||||
/// Used in heuristics
|
||||
pub fn _get_hook_arena_size(&self) -> usize {
|
||||
self.arena.allocated_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sizeof() {
|
||||
dbg!(std::mem::size_of::<Scope>());
|
||||
|
||||
@@ -180,7 +180,6 @@ impl ScopeArena {
|
||||
caller,
|
||||
generation: 0.into(),
|
||||
|
||||
hooks: HookList::new(hook_capacity),
|
||||
shared_contexts: Default::default(),
|
||||
|
||||
items: RefCell::new(SelfReferentialItems {
|
||||
@@ -188,6 +187,10 @@ impl ScopeArena {
|
||||
borrowed_props: Default::default(),
|
||||
tasks: Default::default(),
|
||||
}),
|
||||
|
||||
hook_arena: Bump::new(),
|
||||
hook_vals: RefCell::new(smallvec::SmallVec::with_capacity(hook_capacity)),
|
||||
hook_idx: Default::default(),
|
||||
});
|
||||
|
||||
let any_item = self.scopes.borrow_mut().insert(new_scope_id, scope);
|
||||
@@ -208,7 +211,14 @@ impl ScopeArena {
|
||||
let scope = unsafe { &mut *self.scopes.borrow_mut().remove(id).unwrap() };
|
||||
|
||||
// we're just reusing scopes so we need to clear it out
|
||||
scope.hooks.clear();
|
||||
scope.hook_vals.get_mut().drain(..).for_each(|state| {
|
||||
let as_mut = unsafe { &mut *state };
|
||||
let boxed = unsafe { bumpalo::boxed::Box::from_raw(as_mut) };
|
||||
drop(boxed);
|
||||
});
|
||||
scope.hook_idx.set(0);
|
||||
scope.hook_arena.reset();
|
||||
|
||||
scope.shared_contexts.get_mut().clear();
|
||||
scope.parent_scope = None;
|
||||
scope.generation.set(0);
|
||||
@@ -310,7 +320,13 @@ impl ScopeArena {
|
||||
// Safety:
|
||||
// - We dropped the listeners, so no more &mut T can be used while these are held
|
||||
// - All children nodes that rely on &mut T are replaced with a new reference
|
||||
unsafe { scope.hooks.reset() };
|
||||
scope.hook_vals.get_mut().drain(..).for_each(|state| {
|
||||
let as_mut = unsafe { &mut *state };
|
||||
let boxed = unsafe { bumpalo::boxed::Box::from_raw(as_mut) };
|
||||
drop(boxed);
|
||||
});
|
||||
scope.hook_idx.set(0);
|
||||
scope.hook_arena.reset();
|
||||
|
||||
// Safety:
|
||||
// - We've dropped all references to the wip bump frame with "ensure_drop_safety"
|
||||
|
||||
@@ -8,10 +8,7 @@ use futures_util::{Future, StreamExt};
|
||||
use fxhash::FxHashSet;
|
||||
use indexmap::IndexSet;
|
||||
use smallvec::SmallVec;
|
||||
use std::pin::Pin;
|
||||
use std::sync::Arc;
|
||||
use std::task::Poll;
|
||||
use std::{any::Any, collections::VecDeque};
|
||||
use std::{any::Any, collections::VecDeque, pin::Pin, sync::Arc, task::Poll};
|
||||
|
||||
/// A virtual node system that progresses user events and diffs UI trees.
|
||||
///
|
||||
@@ -286,7 +283,7 @@ impl VirtualDom {
|
||||
if let Some(msg) = self.pending_messages.pop_back() {
|
||||
match msg {
|
||||
// just keep looping, the task is now saved but we should actually poll it
|
||||
SchedulerMsg::TaskPushed(id) => {
|
||||
SchedulerMsg::NewTask(id) => {
|
||||
self.scopes.pending_futures.borrow_mut().insert(id);
|
||||
}
|
||||
SchedulerMsg::UiEvent(event) => {
|
||||
@@ -546,7 +543,7 @@ pub enum SchedulerMsg {
|
||||
Immediate(ScopeId),
|
||||
|
||||
// an async task pushed from an event handler (or just spawned)
|
||||
TaskPushed(ScopeId),
|
||||
NewTask(ScopeId),
|
||||
}
|
||||
|
||||
/// User Events are events that are shuttled from the renderer into the VirtualDom trhough the scheduler channel.
|
||||
@@ -675,9 +672,9 @@ impl<'a> Future for PollTasks<'a> {
|
||||
// I think the futures neeed to be pinned using bumpbox or something
|
||||
// right now, they're bump allocated so this shouldn't matter anyway - they're not going to move
|
||||
let task_mut = task.as_mut();
|
||||
let unpinned = unsafe { Pin::new_unchecked(task_mut) };
|
||||
let pinned = unsafe { Pin::new_unchecked(task_mut) };
|
||||
|
||||
if unpinned.poll(cx).is_ready() {
|
||||
if pinned.poll(cx).is_ready() {
|
||||
all_pending = false
|
||||
} else {
|
||||
unfinished_tasks.push(task);
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
//!
|
||||
//! It does not validated that component lifecycles work properly. This is done in another test file.
|
||||
|
||||
use dioxus::{prelude::*, DomEdit, VSuspended};
|
||||
use dioxus::{prelude::*, DomEdit};
|
||||
use dioxus_core as dioxus;
|
||||
use dioxus_core_macro::*;
|
||||
use dioxus_html as dioxus_elements;
|
||||
|
||||
@@ -158,7 +158,7 @@ impl<'a> TextRenderer<'a, '_> {
|
||||
}
|
||||
write!(f, "<!-- -->")?;
|
||||
}
|
||||
VNode::Linked(link) => {
|
||||
VNode::Portal(link) => {
|
||||
todo!();
|
||||
}
|
||||
VNode::Element(el) => {
|
||||
|
||||
Reference in new issue
Block a user