diff --git a/jirs-css/src/colors.rs b/jirs-css/src/colors.rs index 12daba42..02e18afa 100644 --- a/jirs-css/src/colors.rs +++ b/jirs-css/src/colors.rs @@ -1,41 +1,5 @@ use std::str::FromStr; -/// rgba(255, 255, 255, 1.0) -/// 012345 -/// rgb(255, 255, 255, 1.0) -/// 01234 -pub fn parse_rgba(s: &str, parse_alpha: bool) -> Result<(u8, u8, u8, u8), String> { - let start_idx = if parse_alpha { 5 } else { 4 }; - let v: Vec = s[start_idx..(s.len() - 1)] - .split(',') - .map(|s| s.to_string()) - .collect(); - let r = v - .get(0) - .ok_or_else(|| format!("invalid color {:?}", s)) - .and_then(|s| s.parse().map_err(|_| format!("invalid color {:?}", s)))?; - let g = v - .get(1) - .ok_or_else(|| format!("invalid color {:?}", s)) - .and_then(|s| s.parse().map_err(|_| format!("invalid color {:?}", s)))?; - let b = v - .get(2) - .ok_or_else(|| format!("invalid color {:?}", s)) - .and_then(|s| s.parse().map_err(|_| format!("invalid color {:?}", s)))?; - let a = if parse_alpha { - (v.get(3) - .ok_or_else(|| format!("invalid color {:?}", s)) - .and_then(|s| { - s.parse::() - .map_err(|_| format!("invalid color {:?}", s)) - })? - * 255f64) as u8 - } else { - 255 - }; - Ok((r, g, b, a)) -} - /// hsla(360, 100%, 100%, 1.0) /// 012345 /// hsl(360, 100%, 100%, 1.0) @@ -81,628 +45,590 @@ fn parse_percent(s: &str, v: Option<&String>) -> Result { } pub enum Color { - AliceBlue, - AntiqueWhite, - Aqua, - Aquamarine, - Azure, - Beige, - Bisque, - Black, - BlanchedAlmond, - Blue, - BlueViolet, - Brown, - BurlyWood, - CadetBlue, - Chartreuse, - Chocolate, - Coral, - CornflowerBlue, - Cornsilk, - Crimson, - Cyan, - DarkBlue, - DarkCyan, - DarkGoldenRod, - DarkGray, - DarkGrey, - DarkGreen, - DarkKhaki, - DarkMagenta, - DarkOliveGreen, - DarkOrange, - DarkOrchid, - DarkRed, - DarkSalmon, - DarkSeaGreen, - DarkSlateBlue, - DarkSlateGray, - DarkSlateGrey, - DarkTurquoise, - DarkViolet, - DeepPink, - DeepSkyBlue, - DimGray, - DimGrey, - DodgerBlue, - FireBrick, - FloralWhite, - ForestGreen, - Fuchsia, - Gainsboro, - GhostWhite, - Gold, - GoldenRod, - Gray, - Grey, - Green, - GreenYellow, - HoneyDew, - HotPink, - IndianRed, - Indigo, - Ivory, - Khaki, - Lavender, - LavenderBlush, - LawnGreen, - LemonChiffon, - LightBlue, - LightCoral, - LightCyan, - LightGoldenRodYellow, - LightGray, - LightGrey, - LightGreen, + Pink, LightPink, - LightSalmon, - LightSeaGreen, - LightSkyBlue, - LightSlateGray, - LightSlateGrey, - LightSteelBlue, - LightYellow, - Lime, - LimeGreen, - Linen, - Magenta, - Maroon, - MediumAquaMarine, - MediumBlue, - MediumOrchid, - MediumPurple, - MediumSeaGreen, - MediumSlateBlue, - MediumSpringGreen, - MediumTurquoise, + HotPink, + DeepPink, + PaleVioletRed, MediumVioletRed, - MidnightBlue, - MintCream, - MistyRose, + LightSalmon, + Salmon, + DarkSalmon, + LightCoral, + IndianRed, + Crimson, + Firebrick, + DarkRed, + Red, + OrangeRed, + Tomato, + Coral, + DarkOrange, + Orange, + Yellow, + LightYellow, + LemonChiffon, + LightGoldenrodYellow, + PapayaWhip, Moccasin, + PeachPuff, + PaleGoldenrod, + Khaki, + DarkKhaki, + Gold, + Cornsilk, + BlanchedAlmond, + Bisque, NavajoWhite, - Navy, - OldLace, + Wheat, + Burlywood, + Tan, + RosyBrown, + SandyBrown, + Goldenrod, + DarkGoldenrod, + Peru, + Chocolate, + SaddleBrown, + Sienna, + Brown, + Maroon, + DarkOliveGreen, Olive, OliveDrab, - Orange, - OrangeRed, - Orchid, - PaleGoldenRod, - PaleGreen, - PaleTurquoise, - PaleVioletRed, - PapayaWhip, - PeachPuff, - Peru, - Pink, - Plum, - PowderBlue, - Purple, - RebeccaPurple, - Red, - RosyBrown, - RoyalBlue, - SaddleBrown, - Salmon, - SandyBrown, - SeaGreen, - SeaShell, - Sienna, - Silver, - SkyBlue, - SlateBlue, - SlateGray, - SlateGrey, - Snow, - SpringGreen, - SteelBlue, - Tan, - Teal, - Thistle, - Tomato, - Turquoise, - Violet, - Wheat, - White, - WhiteSmoke, - Yellow, YellowGreen, + LimeGreen, + Lime, + LawnGreen, + Chartreuse, + GreenYellow, + SpringGreen, + MediumSpringGreen, + LightGreen, + PaleGreen, + DarkSeaGreen, + MediumAquamarine, + MediumSeaGreen, + SeaGreen, + ForestGreen, + Green, + DarkGreen, + Aqua, + Cyan, + LightCyan, + PaleTurquoise, + Aquamarine, + Turquoise, + MediumTurquoise, + DarkTurquoise, + LightSeaGreen, + CadetBlue, + DarkCyan, + Teal, + LightSteelBlue, + PowderBlue, + LightBlue, + SkyBlue, + LightSkyBlue, + DeepSkyBlue, + DodgerBlue, + CornflowerBlue, + SteelBlue, + RoyalBlue, + Blue, + MediumBlue, + DarkBlue, + Navy, + MidnightBlue, + Lavender, + Thistle, + Plum, + Violet, + Orchid, + Fuchsia, + Magenta, + MediumOrchid, + MediumPurple, + BlueViolet, + DarkViolet, + DarkOrchid, + DarkMagenta, + Purple, + Indigo, + DarkSlateBlue, + SlateBlue, + MediumSlateBlue, + White, + Snow, + Honeydew, + MintCream, + Azure, + AliceBlue, + GhostWhite, + WhiteSmoke, + Seashell, + Beige, + OldLace, + FloralWhite, + Ivory, + AntiqueWhite, + Linen, + LavenderBlush, + MistyRose, + Gainsboro, + LightGray, + Silver, + DarkGray, + Gray, + DimGray, + LightSlateGray, + SlateGray, + DarkSlateGray, + Black, } impl ToString for Color { fn to_string(&self) -> String { match self { - Color::AliceBlue => "AliceBlue", - Color::AntiqueWhite => "AntiqueWhite", - Color::Aqua => "Aqua", - Color::Aquamarine => "Aquamarine", - Color::Azure => "Azure", - Color::Beige => "Beige", - Color::Bisque => "Bisque", - Color::Black => "Black", - Color::BlanchedAlmond => "BlanchedAlmond", - Color::Blue => "Blue", - Color::BlueViolet => "BlueViolet", - Color::Brown => "Brown", - Color::BurlyWood => "BurlyWood", - Color::CadetBlue => "CadetBlue", - Color::Chartreuse => "Chartreuse", - Color::Chocolate => "Chocolate", - Color::Coral => "Coral", - Color::CornflowerBlue => "CornflowerBlue", - Color::Cornsilk => "Cornsilk", - Color::Crimson => "Crimson", - Color::Cyan => "Cyan", - Color::DarkBlue => "DarkBlue", - Color::DarkCyan => "DarkCyan", - Color::DarkGoldenRod => "DarkGoldenRod", - Color::DarkGray => "DarkGray", - Color::DarkGrey => "DarkGrey", - Color::DarkGreen => "DarkGreen", - Color::DarkKhaki => "DarkKhaki", - Color::DarkMagenta => "DarkMagenta", - Color::DarkOliveGreen => "DarkOliveGreen", - Color::DarkOrange => "DarkOrange", - Color::DarkOrchid => "DarkOrchid", - Color::DarkRed => "DarkRed", - Color::DarkSalmon => "DarkSalmon", - Color::DarkSeaGreen => "DarkSeaGreen", - Color::DarkSlateBlue => "DarkSlateBlue", - Color::DarkSlateGray => "DarkSlateGray", - Color::DarkSlateGrey => "DarkSlateGrey", - Color::DarkTurquoise => "DarkTurquoise", - Color::DarkViolet => "DarkViolet", - Color::DeepPink => "DeepPink", - Color::DeepSkyBlue => "DeepSkyBlue", - Color::DimGray => "DimGray", - Color::DimGrey => "DimGrey", - Color::DodgerBlue => "DodgerBlue", - Color::FireBrick => "FireBrick", - Color::FloralWhite => "FloralWhite", - Color::ForestGreen => "ForestGreen", - Color::Fuchsia => "Fuchsia", - Color::Gainsboro => "Gainsboro", - Color::GhostWhite => "GhostWhite", - Color::Gold => "Gold", - Color::GoldenRod => "GoldenRod", - Color::Gray => "Gray", - Color::Grey => "Grey", - Color::Green => "Green", - Color::GreenYellow => "GreenYellow", - Color::HoneyDew => "HoneyDew", - Color::HotPink => "HotPink", - Color::IndianRed => "IndianRed", - Color::Indigo => "Indigo", - Color::Ivory => "Ivory", - Color::Khaki => "Khaki", - Color::Lavender => "Lavender", - Color::LavenderBlush => "LavenderBlush", - Color::LawnGreen => "LawnGreen", - Color::LemonChiffon => "LemonChiffon", - Color::LightBlue => "LightBlue", - Color::LightCoral => "LightCoral", - Color::LightCyan => "LightCyan", - Color::LightGoldenRodYellow => "LightGoldenRodYellow", - Color::LightGray => "LightGray", - Color::LightGrey => "LightGrey", - Color::LightGreen => "LightGreen", + Color::Pink => "Pink", Color::LightPink => "LightPink", - Color::LightSalmon => "LightSalmon", - Color::LightSeaGreen => "LightSeaGreen", - Color::LightSkyBlue => "LightSkyBlue", - Color::LightSlateGray => "LightSlateGray", - Color::LightSlateGrey => "LightSlateGrey", - Color::LightSteelBlue => "LightSteelBlue", - Color::LightYellow => "LightYellow", - Color::Lime => "Lime", - Color::LimeGreen => "LimeGreen", - Color::Linen => "Linen", - Color::Magenta => "Magenta", - Color::Maroon => "Maroon", - Color::MediumAquaMarine => "MediumAquaMarine", - Color::MediumBlue => "MediumBlue", - Color::MediumOrchid => "MediumOrchid", - Color::MediumPurple => "MediumPurple", - Color::MediumSeaGreen => "MediumSeaGreen", - Color::MediumSlateBlue => "MediumSlateBlue", - Color::MediumSpringGreen => "MediumSpringGreen", - Color::MediumTurquoise => "MediumTurquoise", + Color::HotPink => "HotPink", + Color::DeepPink => "DeepPink", + Color::PaleVioletRed => "PaleVioletRed", Color::MediumVioletRed => "MediumVioletRed", - Color::MidnightBlue => "MidnightBlue", - Color::MintCream => "MintCream", - Color::MistyRose => "MistyRose", + Color::LightSalmon => "LightSalmon", + Color::Salmon => "Salmon", + Color::DarkSalmon => "DarkSalmon", + Color::LightCoral => "LightCoral", + Color::IndianRed => "IndianRed", + Color::Crimson => "Crimson", + Color::Firebrick => "Firebrick", + Color::DarkRed => "DarkRed", + Color::Red => "Red", + Color::OrangeRed => "OrangeRed", + Color::Tomato => "Tomato", + Color::Coral => "Coral", + Color::DarkOrange => "DarkOrange", + Color::Orange => "Orange", + Color::Yellow => "Yellow", + Color::LightYellow => "LightYellow", + Color::LemonChiffon => "LemonChiffon", + Color::LightGoldenrodYellow => "LightGoldenrodYellow", + Color::PapayaWhip => "PapayaWhip", Color::Moccasin => "Moccasin", + Color::PeachPuff => "PeachPuff", + Color::PaleGoldenrod => "PaleGoldenrod", + Color::Khaki => "Khaki", + Color::DarkKhaki => "DarkKhaki", + Color::Gold => "Gold", + Color::Cornsilk => "Cornsilk", + Color::BlanchedAlmond => "BlanchedAlmond", + Color::Bisque => "Bisque", Color::NavajoWhite => "NavajoWhite", - Color::Navy => "Navy", - Color::OldLace => "OldLace", + Color::Wheat => "Wheat", + Color::Burlywood => "Burlywood", + Color::Tan => "Tan", + Color::RosyBrown => "RosyBrown", + Color::SandyBrown => "SandyBrown", + Color::Goldenrod => "Goldenrod", + Color::DarkGoldenrod => "DarkGoldenrod", + Color::Peru => "Peru", + Color::Chocolate => "Chocolate", + Color::SaddleBrown => "SaddleBrown", + Color::Sienna => "Sienna", + Color::Brown => "Brown", + Color::Maroon => "Maroon", + Color::DarkOliveGreen => "DarkOliveGreen", Color::Olive => "Olive", Color::OliveDrab => "OliveDrab", - Color::Orange => "Orange", - Color::OrangeRed => "OrangeRed", - Color::Orchid => "Orchid", - Color::PaleGoldenRod => "PaleGoldenRod", - Color::PaleGreen => "PaleGreen", - Color::PaleTurquoise => "PaleTurquoise", - Color::PaleVioletRed => "PaleVioletRed", - Color::PapayaWhip => "PapayaWhip", - Color::PeachPuff => "PeachPuff", - Color::Peru => "Peru", - Color::Pink => "Pink", - Color::Plum => "Plum", - Color::PowderBlue => "PowderBlue", - Color::Purple => "Purple", - Color::RebeccaPurple => "RebeccaPurple", - Color::Red => "Red", - Color::RosyBrown => "RosyBrown", - Color::RoyalBlue => "RoyalBlue", - Color::SaddleBrown => "SaddleBrown", - Color::Salmon => "Salmon", - Color::SandyBrown => "SandyBrown", - Color::SeaGreen => "SeaGreen", - Color::SeaShell => "SeaShell", - Color::Sienna => "Sienna", - Color::Silver => "Silver", - Color::SkyBlue => "SkyBlue", - Color::SlateBlue => "SlateBlue", - Color::SlateGray => "SlateGray", - Color::SlateGrey => "SlateGrey", - Color::Snow => "Snow", - Color::SpringGreen => "SpringGreen", - Color::SteelBlue => "SteelBlue", - Color::Tan => "Tan", - Color::Teal => "Teal", - Color::Thistle => "Thistle", - Color::Tomato => "Tomato", - Color::Turquoise => "Turquoise", - Color::Violet => "Violet", - Color::Wheat => "Wheat", - Color::White => "White", - Color::WhiteSmoke => "WhiteSmoke", - Color::Yellow => "Yellow", Color::YellowGreen => "YellowGreen", + Color::LimeGreen => "LimeGreen", + Color::Lime => "Lime", + Color::LawnGreen => "LawnGreen", + Color::Chartreuse => "Chartreuse", + Color::GreenYellow => "GreenYellow", + Color::SpringGreen => "SpringGreen", + Color::MediumSpringGreen => "MediumSpringGreen", + Color::LightGreen => "LightGreen", + Color::PaleGreen => "PaleGreen", + Color::DarkSeaGreen => "DarkSeaGreen", + Color::MediumAquamarine => "MediumAquamarine", + Color::MediumSeaGreen => "MediumSeaGreen", + Color::SeaGreen => "SeaGreen", + Color::ForestGreen => "ForestGreen", + Color::Green => "Green", + Color::DarkGreen => "DarkGreen", + Color::Aqua => "Aqua", + Color::Cyan => "Cyan", + Color::LightCyan => "LightCyan", + Color::PaleTurquoise => "PaleTurquoise", + Color::Aquamarine => "Aquamarine", + Color::Turquoise => "Turquoise", + Color::MediumTurquoise => "MediumTurquoise", + Color::DarkTurquoise => "DarkTurquoise", + Color::LightSeaGreen => "LightSeaGreen", + Color::CadetBlue => "CadetBlue", + Color::DarkCyan => "DarkCyan", + Color::Teal => "Teal", + Color::LightSteelBlue => "LightSteelBlue", + Color::PowderBlue => "PowderBlue", + Color::LightBlue => "LightBlue", + Color::SkyBlue => "SkyBlue", + Color::LightSkyBlue => "LightSkyBlue", + Color::DeepSkyBlue => "DeepSkyBlue", + Color::DodgerBlue => "DodgerBlue", + Color::CornflowerBlue => "CornflowerBlue", + Color::SteelBlue => "SteelBlue", + Color::RoyalBlue => "RoyalBlue", + Color::Blue => "Blue", + Color::MediumBlue => "MediumBlue", + Color::DarkBlue => "DarkBlue", + Color::Navy => "Navy", + Color::MidnightBlue => "MidnightBlue", + Color::Lavender => "Lavender", + Color::Thistle => "Thistle", + Color::Plum => "Plum", + Color::Violet => "Violet", + Color::Orchid => "Orchid", + Color::Fuchsia => "Fuchsia", + Color::Magenta => "Magenta", + Color::MediumOrchid => "MediumOrchid", + Color::MediumPurple => "MediumPurple", + Color::BlueViolet => "BlueViolet", + Color::DarkViolet => "DarkViolet", + Color::DarkOrchid => "DarkOrchid", + Color::DarkMagenta => "DarkMagenta", + Color::Purple => "Purple", + Color::Indigo => "Indigo", + Color::DarkSlateBlue => "DarkSlateBlue", + Color::SlateBlue => "SlateBlue", + Color::MediumSlateBlue => "MediumSlateBlue", + Color::White => "White", + Color::Snow => "Snow", + Color::Honeydew => "Honeydew", + Color::MintCream => "MintCream", + Color::Azure => "Azure", + Color::AliceBlue => "AliceBlue", + Color::GhostWhite => "GhostWhite", + Color::WhiteSmoke => "WhiteSmoke", + Color::Seashell => "Seashell", + Color::Beige => "Beige", + Color::OldLace => "OldLace", + Color::FloralWhite => "FloralWhite", + Color::Ivory => "Ivory", + Color::AntiqueWhite => "AntiqueWhite", + Color::Linen => "Linen", + Color::LavenderBlush => "LavenderBlush", + Color::MistyRose => "MistyRose", + Color::Gainsboro => "Gainsboro", + Color::LightGray => "LightGray", + Color::Silver => "Silver", + Color::DarkGray => "DarkGray", + Color::Gray => "Gray", + Color::DimGray => "DimGray", + Color::LightSlateGray => "LightSlateGray", + Color::SlateGray => "SlateGray", + Color::DarkSlateGray => "DarkSlateGray", + Color::Black => "Black", } .to_string() } } impl Color { - pub fn to_hex(&self) -> String { + pub fn to_values(&self) -> (u8, u8, u8) { match self { - Color::AliceBlue => "#f0f8ff", - Color::AntiqueWhite => "#faebd7", - Color::Aqua => "#00ffff", - Color::Aquamarine => "#7fffd4", - Color::Azure => "#f0ffff", - Color::Beige => "#f5f5dc", - Color::Bisque => "#ffe4c4", - Color::Black => "#000000", - Color::BlanchedAlmond => "#ffebcd", - Color::Blue => "#0000ff", - Color::BlueViolet => "#8a2be2", - Color::Brown => "#a52a2a", - Color::BurlyWood => "#deb887", - Color::CadetBlue => "#5f9ea0", - Color::Chartreuse => "#7fff00", - Color::Chocolate => "#d2691e", - Color::Coral => "#ff7f50", - Color::CornflowerBlue => "#6495ed", - Color::Cornsilk => "#fff8dc", - Color::Crimson => "#dc143c", - Color::Cyan => "#00ffff", - Color::DarkBlue => "#00008b", - Color::DarkCyan => "#008b8b", - Color::DarkGoldenRod => "#b8860b", - Color::DarkGray => "#a9a9a9", - Color::DarkGrey => "#a9a9a9", - Color::DarkGreen => "#006400", - Color::DarkKhaki => "#bdb76b", - Color::DarkMagenta => "#8b008b", - Color::DarkOliveGreen => "#556b2f", - Color::DarkOrange => "#ff8c00", - Color::DarkOrchid => "#9932cc", - Color::DarkRed => "#8b0000", - Color::DarkSalmon => "#e9967a", - Color::DarkSeaGreen => "#8fbc8f", - Color::DarkSlateBlue => "#483d8b", - Color::DarkSlateGray => "#2f4f4f", - Color::DarkSlateGrey => "#2f4f4f", - Color::DarkTurquoise => "#00ced1", - Color::DarkViolet => "#9400d3", - Color::DeepPink => "#ff1493", - Color::DeepSkyBlue => "#00bfff", - Color::DimGray => "#696969", - Color::DimGrey => "#696969", - Color::DodgerBlue => "#1e90ff", - Color::FireBrick => "#b22222", - Color::FloralWhite => "#fffaf0", - Color::ForestGreen => "#228b22", - Color::Fuchsia => "#ff00ff", - Color::Gainsboro => "#dcdcdc", - Color::GhostWhite => "#f8f8ff", - Color::Gold => "#ffd700", - Color::GoldenRod => "#daa520", - Color::Gray => "#808080", - Color::Grey => "#808080", - Color::Green => "#008000", - Color::GreenYellow => "#adff2f", - Color::HoneyDew => "#f0fff0", - Color::HotPink => "#ff69b4", - Color::IndianRed => "#cd5c5c", - Color::Indigo => "#4b0082", - Color::Ivory => "#fffff0", - Color::Khaki => "#f0e68c", - Color::Lavender => "#e6e6fa", - Color::LavenderBlush => "#fff0f5", - Color::LawnGreen => "#7cfc00", - Color::LemonChiffon => "#fffacd", - Color::LightBlue => "#add8e6", - Color::LightCoral => "#f08080", - Color::LightCyan => "#e0ffff", - Color::LightGoldenRodYellow => "#fafad2", - Color::LightGray => "#d3d3d3", - Color::LightGrey => "#d3d3d3", - Color::LightGreen => "#90ee90", - Color::LightPink => "#ffb6c1", - Color::LightSalmon => "#ffa07a", - Color::LightSeaGreen => "#20b2aa", - Color::LightSkyBlue => "#87cefa", - Color::LightSlateGray => "#778899", - Color::LightSlateGrey => "#778899", - Color::LightSteelBlue => "#b0c4de", - Color::LightYellow => "#ffffe0", - Color::Lime => "#00ff00", - Color::LimeGreen => "#32cd32", - Color::Linen => "#faf0e6", - Color::Magenta => "#ff00ff", - Color::Maroon => "#800000", - Color::MediumAquaMarine => "#66cdaa", - Color::MediumBlue => "#0000cd", - Color::MediumOrchid => "#ba55d3", - Color::MediumPurple => "#9370db", - Color::MediumSeaGreen => "#3cb371", - Color::MediumSlateBlue => "#7b68ee", - Color::MediumSpringGreen => "#00fa9a", - Color::MediumTurquoise => "#48d1cc", - Color::MediumVioletRed => "#c71585", - Color::MidnightBlue => "#191970", - Color::MintCream => "#f5fffa", - Color::MistyRose => "#ffe4e1", - Color::Moccasin => "#ffe4b5", - Color::NavajoWhite => "#ffdead", - Color::Navy => "#000080", - Color::OldLace => "#fdf5e6", - Color::Olive => "#808000", - Color::OliveDrab => "#6b8e23", - Color::Orange => "#ffa500", - Color::OrangeRed => "#ff4500", - Color::Orchid => "#da70d6", - Color::PaleGoldenRod => "#eee8aa", - Color::PaleGreen => "#98fb98", - Color::PaleTurquoise => "#afeeee", - Color::PaleVioletRed => "#db7093", - Color::PapayaWhip => "#ffefd5", - Color::PeachPuff => "#ffdab9", - Color::Peru => "#cd853f", - Color::Pink => "#ffc0cb", - Color::Plum => "#dda0dd", - Color::PowderBlue => "#b0e0e6", - Color::Purple => "#800080", - Color::RebeccaPurple => "#663399", - Color::Red => "#ff0000", - Color::RosyBrown => "#bc8f8f", - Color::RoyalBlue => "#4169e1", - Color::SaddleBrown => "#8b4513", - Color::Salmon => "#fa8072", - Color::SandyBrown => "#f4a460", - Color::SeaGreen => "#2e8b57", - Color::SeaShell => "#fff5ee", - Color::Sienna => "#a0522d", - Color::Silver => "#c0c0c0", - Color::SkyBlue => "#87ceeb", - Color::SlateBlue => "#6a5acd", - Color::SlateGray => "#708090", - Color::SlateGrey => "#708090", - Color::Snow => "#fffafa", - Color::SpringGreen => "#00ff7f", - Color::SteelBlue => "#4682b4", - Color::Tan => "#d2b48c", - Color::Teal => "#008080", - Color::Thistle => "#d8bfd8", - Color::Tomato => "#ff6347", - Color::Turquoise => "#40e0d0", - Color::Violet => "#ee82ee", - Color::Wheat => "#f5deb3", - Color::White => "#ffffff", - Color::WhiteSmoke => "#f5f5f5", - Color::Yellow => "#ffff00", - Color::YellowGreen => "#9acd32", + Color::Pink => (255, 192, 203), + Color::LightPink => (255, 182, 193), + Color::HotPink => (255, 105, 180), + Color::DeepPink => (255, 20, 147), + Color::PaleVioletRed => (219, 112, 147), + Color::MediumVioletRed => (199, 21, 133), + Color::LightSalmon => (255, 160, 122), + Color::Salmon => (250, 128, 114), + Color::DarkSalmon => (233, 150, 122), + Color::LightCoral => (240, 128, 128), + Color::IndianRed => (205, 92, 92), + Color::Crimson => (220, 20, 60), + Color::Firebrick => (178, 34, 34), + Color::DarkRed => (139, 0, 0), + Color::Red => (255, 0, 0), + Color::OrangeRed => (255, 69, 0), + Color::Tomato => (255, 99, 71), + Color::Coral => (255, 127, 80), + Color::DarkOrange => (255, 140, 0), + Color::Orange => (255, 165, 0), + Color::Yellow => (255, 255, 0), + Color::LightYellow => (255, 255, 224), + Color::LemonChiffon => (255, 250, 205), + Color::LightGoldenrodYellow => (250, 250, 210), + Color::PapayaWhip => (255, 239, 213), + Color::Moccasin => (255, 228, 181), + Color::PeachPuff => (255, 218, 185), + Color::PaleGoldenrod => (238, 232, 170), + Color::Khaki => (240, 230, 140), + Color::DarkKhaki => (189, 183, 107), + Color::Gold => (255, 215, 0), + Color::Cornsilk => (255, 248, 220), + Color::BlanchedAlmond => (255, 235, 205), + Color::Bisque => (255, 228, 196), + Color::NavajoWhite => (255, 222, 173), + Color::Wheat => (245, 222, 179), + Color::Burlywood => (222, 184, 135), + Color::Tan => (210, 180, 140), + Color::RosyBrown => (188, 143, 143), + Color::SandyBrown => (244, 164, 96), + Color::Goldenrod => (218, 165, 32), + Color::DarkGoldenrod => (184, 134, 11), + Color::Peru => (205, 133, 63), + Color::Chocolate => (210, 105, 30), + Color::SaddleBrown => (139, 69, 19), + Color::Sienna => (160, 82, 45), + Color::Brown => (165, 42, 42), + Color::Maroon => (128, 0, 0), + Color::DarkOliveGreen => (85, 107, 47), + Color::Olive => (128, 128, 0), + Color::OliveDrab => (107, 142, 35), + Color::YellowGreen => (154, 205, 50), + Color::LimeGreen => (50, 205, 50), + Color::Lime => (0, 255, 0), + Color::LawnGreen => (124, 252, 0), + Color::Chartreuse => (127, 255, 0), + Color::GreenYellow => (173, 255, 47), + Color::SpringGreen => (0, 255, 127), + Color::MediumSpringGreen => (0, 250, 154), + Color::LightGreen => (144, 238, 144), + Color::PaleGreen => (152, 251, 152), + Color::DarkSeaGreen => (143, 188, 143), + Color::MediumAquamarine => (102, 205, 170), + Color::MediumSeaGreen => (60, 179, 113), + Color::SeaGreen => (46, 139, 87), + Color::ForestGreen => (34, 139, 34), + Color::Green => (0, 128, 0), + Color::DarkGreen => (0, 100, 0), + Color::Aqua => (0, 255, 255), + Color::Cyan => (0, 255, 255), + Color::LightCyan => (224, 255, 255), + Color::PaleTurquoise => (175, 238, 238), + Color::Aquamarine => (127, 255, 212), + Color::Turquoise => (64, 224, 208), + Color::MediumTurquoise => (72, 209, 204), + Color::DarkTurquoise => (0, 206, 209), + Color::LightSeaGreen => (32, 178, 170), + Color::CadetBlue => (95, 158, 160), + Color::DarkCyan => (0, 139, 139), + Color::Teal => (0, 128, 128), + Color::LightSteelBlue => (176, 196, 222), + Color::PowderBlue => (176, 224, 230), + Color::LightBlue => (173, 216, 230), + Color::SkyBlue => (135, 206, 235), + Color::LightSkyBlue => (135, 206, 250), + Color::DeepSkyBlue => (0, 191, 255), + Color::DodgerBlue => (30, 144, 255), + Color::CornflowerBlue => (100, 149, 237), + Color::SteelBlue => (70, 130, 180), + Color::RoyalBlue => (65, 105, 225), + Color::Blue => (0, 0, 255), + Color::MediumBlue => (0, 0, 205), + Color::DarkBlue => (0, 0, 139), + Color::Navy => (0, 0, 128), + Color::MidnightBlue => (25, 25, 112), + Color::Lavender => (230, 230, 250), + Color::Thistle => (216, 191, 216), + Color::Plum => (221, 160, 221), + Color::Violet => (238, 130, 238), + Color::Orchid => (218, 112, 214), + Color::Fuchsia => (255, 0, 255), + Color::Magenta => (255, 0, 255), + Color::MediumOrchid => (186, 85, 211), + Color::MediumPurple => (147, 112, 219), + Color::BlueViolet => (138, 43, 226), + Color::DarkViolet => (148, 0, 211), + Color::DarkOrchid => (153, 50, 204), + Color::DarkMagenta => (139, 0, 139), + Color::Purple => (128, 0, 128), + Color::Indigo => (75, 0, 130), + Color::DarkSlateBlue => (72, 61, 139), + Color::SlateBlue => (106, 90, 205), + Color::MediumSlateBlue => (123, 104, 238), + Color::White => (255, 255, 255), + Color::Snow => (255, 250, 250), + Color::Honeydew => (240, 255, 240), + Color::MintCream => (245, 255, 250), + Color::Azure => (240, 255, 255), + Color::AliceBlue => (240, 248, 255), + Color::GhostWhite => (248, 248, 255), + Color::WhiteSmoke => (245, 245, 245), + Color::Seashell => (255, 245, 238), + Color::Beige => (245, 245, 220), + Color::OldLace => (253, 245, 230), + Color::FloralWhite => (255, 250, 240), + Color::Ivory => (255, 255, 240), + Color::AntiqueWhite => (250, 235, 215), + Color::Linen => (250, 240, 230), + Color::LavenderBlush => (255, 240, 245), + Color::MistyRose => (255, 228, 225), + Color::Gainsboro => (220, 220, 220), + Color::LightGray => (211, 211, 211), + Color::Silver => (192, 192, 192), + Color::DarkGray => (169, 169, 169), + Color::Gray => (128, 128, 128), + Color::DimGray => (105, 105, 105), + Color::LightSlateGray => (119, 136, 153), + Color::SlateGray => (112, 128, 144), + Color::DarkSlateGray => (47, 79, 79), + Color::Black => (0, 0, 0), } - .to_string() } } impl FromStr for Color { type Err = String; - fn from_str(s: &str) -> Result { - let m = if s.starts_with('#') { - s.to_lowercase() - } else { - s.to_string() + fn from_str(s: &str) -> Result { + let p = match s.to_lowercase().trim() { + "pink" | "Pink" => Color::Pink, + "lightpink" | "LightPink" => Color::LightPink, + "hotpink" | "HotPink" => Color::HotPink, + "deeppink" | "DeepPink" => Color::DeepPink, + "palevioletred" | "PaleVioletRed" => Color::PaleVioletRed, + "mediumvioletred" | "MediumVioletRed" => Color::MediumVioletRed, + "lightsalmon" | "LightSalmon" => Color::LightSalmon, + "salmon" | "Salmon" => Color::Salmon, + "darksalmon" | "DarkSalmon" => Color::DarkSalmon, + "lightcoral" | "LightCoral" => Color::LightCoral, + "indianred" | "IndianRed" => Color::IndianRed, + "crimson" | "Crimson" => Color::Crimson, + "firebrick" | "Firebrick" => Color::Firebrick, + "darkred" | "DarkRed" => Color::DarkRed, + "red" | "Red" => Color::Red, + "orangered" | "OrangeRed" => Color::OrangeRed, + "tomato" | "Tomato" => Color::Tomato, + "coral" | "Coral" => Color::Coral, + "darkorange" | "DarkOrange" => Color::DarkOrange, + "orange" | "Orange" => Color::Orange, + "yellow" | "Yellow" => Color::Yellow, + "lightyellow" | "LightYellow" => Color::LightYellow, + "lemonchiffon" | "LemonChiffon" => Color::LemonChiffon, + "lightgoldenrodyellow" | "LightGoldenrodYellow" => Color::LightGoldenrodYellow, + "papayawhip" | "PapayaWhip" => Color::PapayaWhip, + "moccasin" | "Moccasin" => Color::Moccasin, + "peachpuff" | "PeachPuff" => Color::PeachPuff, + "palegoldenrod" | "PaleGoldenrod" => Color::PaleGoldenrod, + "khaki" | "Khaki" => Color::Khaki, + "darkkhaki" | "DarkKhaki" => Color::DarkKhaki, + "gold" | "Gold" => Color::Gold, + "cornsilk" | "Cornsilk" => Color::Cornsilk, + "blanchedalmond" | "BlanchedAlmond" => Color::BlanchedAlmond, + "bisque" | "Bisque" => Color::Bisque, + "navajowhite" | "NavajoWhite" => Color::NavajoWhite, + "wheat" | "Wheat" => Color::Wheat, + "burlywood" | "Burlywood" => Color::Burlywood, + "tan" | "Tan" => Color::Tan, + "rosybrown" | "RosyBrown" => Color::RosyBrown, + "sandybrown" | "SandyBrown" => Color::SandyBrown, + "goldenrod" | "Goldenrod" => Color::Goldenrod, + "darkgoldenrod" | "DarkGoldenrod" => Color::DarkGoldenrod, + "peru" | "Peru" => Color::Peru, + "chocolate" | "Chocolate" => Color::Chocolate, + "saddlebrown" | "SaddleBrown" => Color::SaddleBrown, + "sienna" | "Sienna" => Color::Sienna, + "brown" | "Brown" => Color::Brown, + "maroon" | "Maroon" => Color::Maroon, + "darkolivegreen" | "DarkOliveGreen" => Color::DarkOliveGreen, + "olive" | "Olive" => Color::Olive, + "olivedrab" | "OliveDrab" => Color::OliveDrab, + "yellowgreen" | "YellowGreen" => Color::YellowGreen, + "limegreen" | "LimeGreen" => Color::LimeGreen, + "lime" | "Lime" => Color::Lime, + "lawngreen" | "LawnGreen" => Color::LawnGreen, + "chartreuse" | "Chartreuse" => Color::Chartreuse, + "greenyellow" | "GreenYellow" => Color::GreenYellow, + "springgreen" | "SpringGreen" => Color::SpringGreen, + "mediumspringgreen" | "MediumSpringGreen" => Color::MediumSpringGreen, + "lightgreen" | "LightGreen" => Color::LightGreen, + "palegreen" | "PaleGreen" => Color::PaleGreen, + "darkseagreen" | "DarkSeaGreen" => Color::DarkSeaGreen, + "mediumaquamarine" | "MediumAquamarine" => Color::MediumAquamarine, + "mediumseagreen" | "MediumSeaGreen" => Color::MediumSeaGreen, + "seagreen" | "SeaGreen" => Color::SeaGreen, + "forestgreen" | "ForestGreen" => Color::ForestGreen, + "green" | "Green" => Color::Green, + "darkgreen" | "DarkGreen" => Color::DarkGreen, + "aqua" | "Aqua" => Color::Aqua, + "cyan" | "Cyan" => Color::Cyan, + "lightcyan" | "LightCyan" => Color::LightCyan, + "paleturquoise" | "PaleTurquoise" => Color::PaleTurquoise, + "aquamarine" | "Aquamarine" => Color::Aquamarine, + "turquoise" | "Turquoise" => Color::Turquoise, + "mediumturquoise" | "MediumTurquoise" => Color::MediumTurquoise, + "darkturquoise" | "DarkTurquoise" => Color::DarkTurquoise, + "lightseagreen" | "LightSeaGreen" => Color::LightSeaGreen, + "cadetblue" | "CadetBlue" => Color::CadetBlue, + "darkcyan" | "DarkCyan" => Color::DarkCyan, + "teal" | "Teal" => Color::Teal, + "lightsteelblue" | "LightSteelBlue" => Color::LightSteelBlue, + "powderblue" | "PowderBlue" => Color::PowderBlue, + "lightblue" | "LightBlue" => Color::LightBlue, + "skyblue" | "SkyBlue" => Color::SkyBlue, + "lightskyblue" | "LightSkyBlue" => Color::LightSkyBlue, + "deepskyblue" | "DeepSkyBlue" => Color::DeepSkyBlue, + "dodgerblue" | "DodgerBlue" => Color::DodgerBlue, + "cornflowerblue" | "CornflowerBlue" => Color::CornflowerBlue, + "steelblue" | "SteelBlue" => Color::SteelBlue, + "royalblue" | "RoyalBlue" => Color::RoyalBlue, + "blue" | "Blue" => Color::Blue, + "mediumblue" | "MediumBlue" => Color::MediumBlue, + "darkblue" | "DarkBlue" => Color::DarkBlue, + "navy" | "Navy" => Color::Navy, + "midnightblue" | "MidnightBlue" => Color::MidnightBlue, + "lavender" | "Lavender" => Color::Lavender, + "thistle" | "Thistle" => Color::Thistle, + "plum" | "Plum" => Color::Plum, + "violet" | "Violet" => Color::Violet, + "orchid" | "Orchid" => Color::Orchid, + "fuchsia" | "Fuchsia" => Color::Fuchsia, + "magenta" | "Magenta" => Color::Magenta, + "mediumorchid" | "MediumOrchid" => Color::MediumOrchid, + "mediumpurple" | "MediumPurple" => Color::MediumPurple, + "blueviolet" | "BlueViolet" => Color::BlueViolet, + "darkviolet" | "DarkViolet" => Color::DarkViolet, + "darkorchid" | "DarkOrchid" => Color::DarkOrchid, + "darkmagenta" | "DarkMagenta" => Color::DarkMagenta, + "purple" | "Purple" => Color::Purple, + "indigo" | "Indigo" => Color::Indigo, + "darkslateblue" | "DarkSlateBlue" => Color::DarkSlateBlue, + "slateblue" | "SlateBlue" => Color::SlateBlue, + "mediumslateblue" | "MediumSlateBlue" => Color::MediumSlateBlue, + "white" | "White" => Color::White, + "snow" | "Snow" => Color::Snow, + "honeydew" | "Honeydew" => Color::Honeydew, + "mintcream" | "MintCream" => Color::MintCream, + "azure" | "Azure" => Color::Azure, + "aliceblue" | "AliceBlue" => Color::AliceBlue, + "ghostwhite" | "GhostWhite" => Color::GhostWhite, + "whitesmoke" | "WhiteSmoke" => Color::WhiteSmoke, + "seashell" | "Seashell" => Color::Seashell, + "beige" | "Beige" => Color::Beige, + "oldlace" | "OldLace" => Color::OldLace, + "floralwhite" | "FloralWhite" => Color::FloralWhite, + "ivory" | "Ivory" => Color::Ivory, + "antiquewhite" | "AntiqueWhite" => Color::AntiqueWhite, + "linen" | "Linen" => Color::Linen, + "lavenderblush" | "LavenderBlush" => Color::LavenderBlush, + "mistyrose" | "MistyRose" => Color::MistyRose, + "gainsboro" | "Gainsboro" => Color::Gainsboro, + "lightgray" | "LightGray" => Color::LightGray, + "silver" | "Silver" => Color::Silver, + "darkgray" | "DarkGray" => Color::DarkGray, + "gray" | "Gray" => Color::Gray, + "dimgray" | "DimGray" => Color::DimGray, + "lightslategray" | "LightSlateGray" => Color::LightSlateGray, + "slategray" | "SlateGray" => Color::SlateGray, + "darkslategray" | "DarkSlateGray" => Color::DarkSlateGray, + "black" | "Black" => Color::Black, + _ => return Err(format!("{:?} is not a predefined color", s)), }; - let c = match m.as_str() { - "AliceBlue" | "#f0f8ff" => Color::AliceBlue, - "AntiqueWhite" | "#faebd7" => Color::AntiqueWhite, - "Aqua" | "#00ffff" => Color::Aqua, - "Aquamarine" | "#7fffd4" => Color::Aquamarine, - "Azure" | "#f0ffff" => Color::Azure, - "Beige" | "#f5f5dc" => Color::Beige, - "Bisque" | "#ffe4c4" => Color::Bisque, - "Black" | "#000000" => Color::Black, - "BlanchedAlmond" | "#ffebcd" => Color::BlanchedAlmond, - "Blue" | "#0000ff" => Color::Blue, - "BlueViolet" | "#8a2be2" => Color::BlueViolet, - "Brown" | "#a52a2a" => Color::Brown, - "BurlyWood" | "#deb887" => Color::BurlyWood, - "CadetBlue" | "#5f9ea0" => Color::CadetBlue, - "Chartreuse" | "#7fff00" => Color::Chartreuse, - "Chocolate" | "#d2691e" => Color::Chocolate, - "Coral" | "#ff7f50" => Color::Coral, - "CornflowerBlue" | "#6495ed" => Color::CornflowerBlue, - "Cornsilk" | "#fff8dc" => Color::Cornsilk, - "Crimson" | "#dc143c" => Color::Crimson, - "Cyan" | "#00ffff" => Color::Cyan, - "DarkBlue" | "#00008b" => Color::DarkBlue, - "DarkCyan" | "#008b8b" => Color::DarkCyan, - "DarkGoldenRod" | "#b8860b" => Color::DarkGoldenRod, - "DarkGray" | "#a9a9a9" => Color::DarkGray, - "DarkGrey" | "#a9a9a9" => Color::DarkGrey, - "DarkGreen" | "#006400" => Color::DarkGreen, - "DarkKhaki" | "#bdb76b" => Color::DarkKhaki, - "DarkMagenta" | "#8b008b" => Color::DarkMagenta, - "DarkOliveGreen" | "#556b2f" => Color::DarkOliveGreen, - "DarkOrange" | "#ff8c00" => Color::DarkOrange, - "DarkOrchid" | "#9932cc" => Color::DarkOrchid, - "DarkRed" | "#8b0000" => Color::DarkRed, - "DarkSalmon" | "#e9967a" => Color::DarkSalmon, - "DarkSeaGreen" | "#8fbc8f" => Color::DarkSeaGreen, - "DarkSlateBlue" | "#483d8b" => Color::DarkSlateBlue, - "DarkSlateGray" | "#2f4f4f" => Color::DarkSlateGray, - "DarkSlateGrey" | "#2f4f4f" => Color::DarkSlateGrey, - "DarkTurquoise" | "#00ced1" => Color::DarkTurquoise, - "DarkViolet" | "#9400d3" => Color::DarkViolet, - "DeepPink" | "#ff1493" => Color::DeepPink, - "DeepSkyBlue" | "#00bfff" => Color::DeepSkyBlue, - "DimGray" | "#696969" => Color::DimGray, - "DimGrey" | "#696969" => Color::DimGrey, - "DodgerBlue" | "#1e90ff" => Color::DodgerBlue, - "FireBrick" | "#b22222" => Color::FireBrick, - "FloralWhite" | "#fffaf0" => Color::FloralWhite, - "ForestGreen" | "#228b22" => Color::ForestGreen, - "Fuchsia" | "#ff00ff" => Color::Fuchsia, - "Gainsboro" | "#dcdcdc" => Color::Gainsboro, - "GhostWhite" | "#f8f8ff" => Color::GhostWhite, - "Gold" | "#ffd700" => Color::Gold, - "GoldenRod" | "#daa520" => Color::GoldenRod, - "Gray" | "#808080" => Color::Gray, - "Grey" | "#808080" => Color::Grey, - "Green" | "#008000" => Color::Green, - "GreenYellow" | "#adff2f" => Color::GreenYellow, - "HoneyDew" | "#f0fff0" => Color::HoneyDew, - "HotPink" | "#ff69b4" => Color::HotPink, - "IndianRed" | "#cd5c5c" => Color::IndianRed, - "Indigo" | "#4b0082" => Color::Indigo, - "Ivory" | "#fffff0" => Color::Ivory, - "Khaki" | "#f0e68c" => Color::Khaki, - "Lavender" | "#e6e6fa" => Color::Lavender, - "LavenderBlush" | "#fff0f5" => Color::LavenderBlush, - "LawnGreen" | "#7cfc00" => Color::LawnGreen, - "LemonChiffon" | "#fffacd" => Color::LemonChiffon, - "LightBlue" | "#add8e6" => Color::LightBlue, - "LightCoral" | "#f08080" => Color::LightCoral, - "LightCyan" | "#e0ffff" => Color::LightCyan, - "LightGoldenRodYellow" | "#fafad2" => Color::LightGoldenRodYellow, - "LightGray" | "#d3d3d3" => Color::LightGray, - "LightGrey" | "#d3d3d3" => Color::LightGrey, - "LightGreen" | "#90ee90" => Color::LightGreen, - "LightPink" | "#ffb6c1" => Color::LightPink, - "LightSalmon" | "#ffa07a" => Color::LightSalmon, - "LightSeaGreen" | "#20b2aa" => Color::LightSeaGreen, - "LightSkyBlue" | "#87cefa" => Color::LightSkyBlue, - "LightSlateGray" | "#778899" => Color::LightSlateGray, - "LightSlateGrey" | "#778899" => Color::LightSlateGrey, - "LightSteelBlue" | "#b0c4de" => Color::LightSteelBlue, - "LightYellow" | "#ffffe0" => Color::LightYellow, - "Lime" | "#00ff00" => Color::Lime, - "LimeGreen" | "#32cd32" => Color::LimeGreen, - "Linen" | "#faf0e6" => Color::Linen, - "Magenta" | "#ff00ff" => Color::Magenta, - "Maroon" | "#800000" => Color::Maroon, - "MediumAquaMarine" | "#66cdaa" => Color::MediumAquaMarine, - "MediumBlue" | "#0000cd" => Color::MediumBlue, - "MediumOrchid" | "#ba55d3" => Color::MediumOrchid, - "MediumPurple" | "#9370db" => Color::MediumPurple, - "MediumSeaGreen" | "#3cb371" => Color::MediumSeaGreen, - "MediumSlateBlue" | "#7b68ee" => Color::MediumSlateBlue, - "MediumSpringGreen" | "#00fa9a" => Color::MediumSpringGreen, - "MediumTurquoise" | "#48d1cc" => Color::MediumTurquoise, - "MediumVioletRed" | "#c71585" => Color::MediumVioletRed, - "MidnightBlue" | "#191970" => Color::MidnightBlue, - "MintCream" | "#f5fffa" => Color::MintCream, - "MistyRose" | "#ffe4e1" => Color::MistyRose, - "Moccasin" | "#ffe4b5" => Color::Moccasin, - "NavajoWhite" | "#ffdead" => Color::NavajoWhite, - "Navy" | "#000080" => Color::Navy, - "OldLace" | "#fdf5e6" => Color::OldLace, - "Olive" | "#808000" => Color::Olive, - "OliveDrab" | "#6b8e23" => Color::OliveDrab, - "Orange" | "#ffa500" => Color::Orange, - "OrangeRed" | "#ff4500" => Color::OrangeRed, - "Orchid" | "#da70d6" => Color::Orchid, - "PaleGoldenRod" | "#eee8aa" => Color::PaleGoldenRod, - "PaleGreen" | "#98fb98" => Color::PaleGreen, - "PaleTurquoise" | "#afeeee" => Color::PaleTurquoise, - "PaleVioletRed" | "#db7093" => Color::PaleVioletRed, - "PapayaWhip" | "#ffefd5" => Color::PapayaWhip, - "PeachPuff" | "#ffdab9" => Color::PeachPuff, - "Peru" | "#cd853f" => Color::Peru, - "Pink" | "#ffc0cb" => Color::Pink, - "Plum" | "#dda0dd" => Color::Plum, - "PowderBlue" | "#b0e0e6" => Color::PowderBlue, - "Purple" | "#800080" => Color::Purple, - "RebeccaPurple" | "#663399" => Color::RebeccaPurple, - "Red" | "#ff0000" => Color::Red, - "RosyBrown" | "#bc8f8f" => Color::RosyBrown, - "RoyalBlue" | "#4169e1" => Color::RoyalBlue, - "SaddleBrown" | "#8b4513" => Color::SaddleBrown, - "Salmon" | "#fa8072" => Color::Salmon, - "SandyBrown" | "#f4a460" => Color::SandyBrown, - "SeaGreen" | "#2e8b57" => Color::SeaGreen, - "SeaShell" | "#fff5ee" => Color::SeaShell, - "Sienna" | "#a0522d" => Color::Sienna, - "Silver" | "#c0c0c0" => Color::Silver, - "SkyBlue" | "#87ceeb" => Color::SkyBlue, - "SlateBlue" | "#6a5acd" => Color::SlateBlue, - "SlateGray" | "#708090" => Color::SlateGray, - "SlateGrey" | "#708090" => Color::SlateGrey, - "Snow" | "#fffafa" => Color::Snow, - "SpringGreen" | "#00ff7f" => Color::SpringGreen, - "SteelBlue" | "#4682b4" => Color::SteelBlue, - "Tan" | "#d2b48c" => Color::Tan, - "Teal" | "#008080" => Color::Teal, - "Thistle" | "#d8bfd8" => Color::Thistle, - "Tomato" | "#ff6347" => Color::Tomato, - "Turquoise" | "#40e0d0" => Color::Turquoise, - "Violet" | "#ee82ee" => Color::Violet, - "Wheat" | "#f5deb3" => Color::Wheat, - "White" | "#ffffff" => Color::White, - "WhiteSmoke" | "#f5f5f5" => Color::WhiteSmoke, - "Yellow" | "#ffff00" => Color::Yellow, - "YellowGreen" | "#9acd32" => Color::YellowGreen, - _ => return Err(format!("invalid predefined color {:?}", s)), - }; - Ok(c) + Ok(p) } } diff --git a/jirs-css/src/prop.rs b/jirs-css/src/prop.rs index b21b3bdc..77196792 100644 --- a/jirs-css/src/prop.rs +++ b/jirs-css/src/prop.rs @@ -3,7 +3,15 @@ use std::str::{Chars, FromStr}; use std::vec::IntoIter; use crate::colors::Color; -use crate::colors::{parse_hsla, parse_rgba}; + +pub type ParseResult = Result; +pub type ValueResult = Result, String>; + +pub trait Token {} + +pub trait ParseToken { + fn parse_token(&mut self) -> ValueResult; +} #[derive(Debug)] pub struct CssTokenizer<'l> { @@ -25,7 +33,7 @@ impl<'l> CssTokenizer<'l> { let mut escaped = false; while let Some(c) = self.it.next() { match c { - '{' | '}' | ';' | ':' => { + '{' | '}' | ';' | ':' | ')' | '(' | '"' | '\'' | ',' | '%' => { self.push_buffer(); self.tokens.push(c.to_string()) } @@ -71,34 +79,46 @@ pub struct ParserPosition { impl std::fmt::Display for ParserPosition { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str(format!("({}:{}:{})", self.line, self.line_character, self.character).as_str()) + f.write_str(format!("{}:{}:{}", self.line, self.line_character, self.character).as_str()) } } #[derive(Debug)] pub struct CssParser { + tokens: Vec, it: Peekable>, selectors: Vec, pos: ParserPosition, + current: String, + filename: String, } impl CssParser { - pub fn new(tokens: Vec) -> Self { + pub fn new(filename: S, tokens: Vec) -> Self + where + S: Into, + { Self { + tokens: tokens.clone(), it: tokens.into_iter().peekable(), selectors: vec![], pos: ParserPosition { - line: 0, - line_character: 0, + line: 1, + line_character: 1, character: 0, }, + current: "".to_string(), + filename: filename.into(), } } pub fn parse(mut self) -> Result, String> { - while let Some(token) = self.it.next() { - let selector = self.parse_selector(token.as_str())?; + while let Some(token) = self.consume() { + let selector = self + .parse_selector(token.as_str()) + .map_err(|error| format!("{} ({}:{})", error, self.filename, self.pos))?; self.selectors.push(selector); + self.skip_white(); } Ok(self.selectors) } @@ -108,8 +128,9 @@ impl CssParser { if let Ok(part) = token.parse::() { path.push(part); - self.parse_selector_path(&mut path); + self.parse_selector_path(&mut path)?; } + self.skip_white(); let block = self .expect_consume() .and_then(|s| self.parse_block(s.as_str()))?; @@ -117,13 +138,13 @@ impl CssParser { Ok(Selector { path, block }) } - fn parse_selector_path(&mut self, path: &mut Vec) { + fn parse_selector_path(&mut self, path: &mut Vec) -> Result<(), String> { self.skip_white(); - while let Some(token) = self.it.peek() { + while let Some(token) = self.peek() { if token.as_str() == "{" { break; } - match self.consume().unwrap_or_default().parse::() { + match self.expect_consume()?.parse::() { Ok(part) => { path.push(part); } @@ -131,6 +152,7 @@ impl CssParser { }; self.skip_white(); } + Ok(()) } fn parse_block(&mut self, token: &str) -> Result { @@ -141,7 +163,6 @@ impl CssParser { self.skip_white(); while let Some(token) = self.consume() { if token.as_str() == "}" { - self.consume(); break; } let prop = self.parse_property(token.as_str())?; @@ -153,177 +174,28 @@ impl CssParser { fn parse_property(&mut self, s: &str) -> Result { let prop = match s { - "align-content" => { - self.skip_white(); - self.consume_expected(":")?; - self.skip_white(); - let p = self.parse_expected_prop_value::()?; - self.consume_expected(";")?; - Property::AlignContent(p) - } - "align-items" => { - self.skip_white(); - self.consume_expected(":")?; - self.skip_white(); - let p = self.parse_expected_prop_value::()?; - self.consume_expected(";")?; - Property::AlignItems(p) - } - "align-self" => { - self.skip_white(); - self.consume_expected(":")?; - self.skip_white(); - let p = self.parse_expected_prop_value::()?; - self.consume_expected(";")?; - Property::AlignSelf(p) - } - "all" => { - self.skip_white(); - self.consume_expected(":")?; - self.skip_white(); - let p = self.parse_expected_prop_value::()?; - self.consume_expected(";")?; - Property::All(p) - } - "animation" => { - self.skip_white(); - self.consume_expected(":")?; - self.skip_white(); - let def = self.expect_consume()?; - let p = match def.as_str() { - "initial" => PropertyValue::Other(AnimationProperty::Initial), - "inherit" => PropertyValue::Other(AnimationProperty::Inherit), - _ if def.starts_with("--") => PropertyValue::Variable(def[2..].to_string()), - _ => { - let name = def; - self.skip_white(); - - let duration = if self.current_is_semicolon() { - PropertyValue::Other(TimeProperty::Seconds(0)) - } else { - let v = self.parse_expected_prop_value::()?; - self.skip_white(); - v - }; - let timing = if self.current_is_semicolon() { - PropertyValue::Other(AnimationTimingFunction::Ease) - } else { - let v = self.parse_expected_prop_value::()?; - self.skip_white(); - v - }; - let delay = if self.current_is_semicolon() { - PropertyValue::Other(AnimationDelayProperty::Time( - TimeProperty::Seconds(0), - )) - } else { - let v = self.parse_expected_prop_value::()?; - self.skip_white(); - v - }; - let iteration_count = if self.current_is_semicolon() { - PropertyValue::Other(1) - } else { - let count = self.expect_consume()?; - let v = count.parse::().map_err(|_| { - format!( - "invalid animation iteration count, expect number got {:?}", - count - ) - })?; - self.skip_white(); - PropertyValue::Other(v) - }; - let direction = if self.current_is_semicolon() { - PropertyValue::Other(AnimationDirectionProperty::Normal) - } else { - let v = - self.parse_expected_prop_value::()?; - self.skip_white(); - v - }; - let fill_mode = if self.current_is_semicolon() { - PropertyValue::Other(AnimationFillModeProperty::None) - } else { - let v = - self.parse_expected_prop_value::()?; - self.skip_white(); - v - }; - let play_state = if self.current_is_semicolon() { - PropertyValue::Other(AnimationPlayStateProperty::Running) - } else { - let v = - self.parse_expected_prop_value::()?; - self.skip_white(); - v - }; - - PropertyValue::Other(AnimationProperty::Custom( - name, - duration, - timing, - delay, - iteration_count, - direction, - fill_mode, - play_state, - )) - } - }; - self.consume_expected(";")?; - Property::Animation(p) - } - "animation-delay" => { - let d = self.parse_expected_prop_value::()?; - self.consume_expected(";")?; - Property::AnimationDelay(d) - } - "animation-direction" => { - let p = self.parse_expected_prop_value::()?; - self.consume_expected(";")?; - Property::AnimationDirection(p) - } - "animation-duration" => { - let p = self.parse_expected_prop_value::()?; - self.consume_expected(";")?; - Property::AnimationDuration(p) - } + "align-content" => Property::AlignContent(self.parse_token()?), + "align-items" => Property::AlignItems(self.parse_token()?), + "align-self" => Property::AlignSelf(self.parse_token()?), + "all" => Property::All(self.parse_token()?), + "animation" => Property::Animation(self.parse_token()?), + "animation-delay" => Property::AnimationDelay(self.parse_token()?), + "animation-direction" => Property::AnimationDirection(self.parse_token()?), + "animation-duration" => Property::AnimationDuration(self.parse_token()?), "animation-fill-mode" => { let p = self.parse_expected_prop_value()?; self.consume_expected(";")?; Property::AnimationFillMode(p) } - "animation-iteration-count" => { - let count = self.expect_consume()?; - let p = if count.starts_with("--") { - PropertyValue::Variable(count[2..].to_string()) - } else { - PropertyValue::Other(count.parse::().map_err(|_| { - format!("invalid iteration count, expect number got {:?}", count) - })?) - }; - - self.consume_expected(";")?; - Property::AnimationIterationCount(p) - } - "animation-name" => { - self.consume_expected(";")?; - let name = match self.expect_consume()?.as_str() { - name @ _ if name.starts_with("--") => { - PropertyValue::Variable(name[2..].to_string()) - } - name @ _ => PropertyValue::Other(name.to_string()), - }; - Property::AnimationName(name) - } + "animation-iteration-count" => Property::AnimationIterationCount(self.parse_token()?), + "animation-name" => Property::AnimationName(self.parse_token()?), "animation-play-state" => { let p = self.parse_expected_prop_value()?; self.consume_expected(";")?; Property::AnimationPlayState(p) } "animation-timing-function" => { - let p = self.parse_expected_prop_value()?; + let p = self.parse_token()?; self.consume_expected(";")?; Property::AnimationTimingFunction(p) } @@ -349,7 +221,10 @@ impl CssParser { Property::BackgroundClip(p) } "background-color" => { - let p = self.parse_expected_prop_value()?; + self.skip_white(); + self.consume_expected(":")?; + self.skip_white(); + let p = self.parse_token()?; self.consume_expected(";")?; Property::BackgroundColor(p) } @@ -401,10 +276,21 @@ impl CssParser { // "caption-side" => Property::CaptionSide, // "caret-color" => Property::CaretColor, // "@charset" => Property::AtCharset, - // "clear" => Property::Clear, + "clear" => { + let p = self.parse_expected_prop_value()?; + self.consume_expected(";")?; + Property::Clear(p) + } // "clip" => Property::Clip, // "clip-path" => Property::ClipPath, - // "color" => Property::Color, + "color" => { + self.skip_white(); + self.consume_expected(":")?; + self.skip_white(); + let p = self.parse_token()?; + self.consume_expected(";")?; + Property::Color(p) + } // "column-count" => Property::ColumnCount, // "column-fill" => Property::ColumnFill, // "column-gap" => Property::ColumnGap, @@ -420,14 +306,7 @@ impl CssParser { // "counter-reset" => Property::CounterReset, // "cursor" => Property::Cursor, // "direction" => Property::Direction, - "display" => { - self.skip_white(); - self.consume_expected(":")?; - self.skip_white(); - let p = self.parse_expected::()?; - self.consume_expected(";")?; - Property::Display(p) - } + "display" => Property::Display(self.parse_token()?), // "empty-cells" => Property::EmptyCells, // "filter" => Property::Filter, // "flex" => Property::Flex, @@ -437,7 +316,11 @@ impl CssParser { // "flex-grow" => Property::FlexGrow, // "flex-shrink" => Property::FlexShrink, // "flex-wrap" => Property::FlexWrap, - // "float" => Property::Float, + "float" => { + let p = self.parse_expected_prop_value()?; + self.consume_expected(";")?; + Property::Float(p) + } // "font" => Property::Font, // "@font-face" => Property::AtFontFace, // "font-family" => Property::FontFamily, @@ -473,14 +356,7 @@ impl CssParser { // "hyphens" => Property::Hyphens, // "@import" => Property::AtImport, // "isolation" => Property::Isolation, - "justify-content" => { - self.skip_white(); - self.consume_expected(":")?; - self.skip_white(); - let p = self.parse_expected_prop_value::()?; - self.consume_expected(";")?; - Property::JustifyContent(p) - } + "justify-content" => Property::JustifyContent(self.parse_token()?), // "@keyframes" => Property::AtKeyframes, // "left" => Property::Left, // "letter-spacing" => Property::LetterSpacing, @@ -523,7 +399,11 @@ impl CssParser { // "perspective" => Property::Perspective, // "perspective-origin" => Property::PerspectiveOrigin, // "pointer-events" => Property::PointerEvents, - // "position" => Property::Position, + "position" => { + let p = self.parse_expected_prop_value()?; + self.consume_expected(";")?; + Property::Position(p) + } // "quotes" => Property::Quotes, // "resize" => Property::Resize, // "right" => Property::Right, @@ -582,7 +462,7 @@ impl CssParser { "\n" => { self.pos.character += s.len(); self.pos.line += 1; - self.pos.line_character += 0; + self.pos.line_character = 1; } _ => { self.pos.character += s.len(); @@ -590,6 +470,7 @@ impl CssParser { } } } + self.current = current.as_ref().cloned().unwrap_or_default(); current } @@ -613,7 +494,12 @@ impl CssParser { fn consume_expected(&mut self, expected: &str) -> Result { self.consume() - .ok_or_else(|| "expect to have parsable token but nothing was found".to_string()) + .ok_or_else(|| { + format!( + "expect to have parsable token {:?} but nothing was found", + expected + ) + }) .and_then(|s| { if s.as_str() == expected { Ok(s) @@ -634,15 +520,13 @@ impl CssParser { s.parse::() } - fn parse_expected_prop_value( - &mut self, - ) -> Result, String> + fn parse_expected_prop_value(&mut self) -> ValueResult where ExpectedType: FromStr, { let s = self.expect_consume()?; - if s.starts_with("--") { - Ok(PropertyValue::Variable(s[2..].to_string())) + if Self::check_if_variable(s.as_str()) { + Self::parse_prop_value_variable(s.as_str()) } else { s.parse::().map(|v| PropertyValue::Other(v)) } @@ -651,6 +535,19 @@ impl CssParser { fn current_is_semicolon(&mut self) -> bool { self.peek().map(|s| s.as_str() == ";").unwrap_or_default() } + + fn parse_prop_value_variable(s: &str) -> Result, String> { + if !Self::check_if_variable(s) { + return Err(format!("given string is not a variable {:?}", s)); + } + Ok(PropertyValue::Variable( + s[6..(s.len() - 1)].trim().to_string(), + )) + } + + fn check_if_variable(s: &str) -> bool { + s.starts_with("var(--") && s.ends_with(")") + } } #[derive(Debug, PartialEq)] @@ -709,6 +606,127 @@ pub enum BlockProperty { Selector(Box), } +impl Token for usize {} + +impl ParseToken for CssParser { + fn parse_token(&mut self) -> Result, String> { + let count = self.expect_consume()?; + let p = if Self::check_if_variable(count.as_str()) { + Self::parse_prop_value_variable(count.as_str())? + } else { + PropertyValue::Other( + count + .parse() + .map_err(|_| format!("invalid token, expect number got {:?}", count))?, + ) + }; + + Ok(p) + } +} + +impl Token for u8 {} + +impl ParseToken for CssParser { + fn parse_token(&mut self) -> Result, String> { + let count = self.expect_consume()?; + let p = if Self::check_if_variable(count.as_str()) { + Self::parse_prop_value_variable(count.as_str())? + } else { + PropertyValue::Other(count.parse().map_err(|_| { + format!( + "invalid token, expect short number greater or equal 0 got {:?}", + count + ) + })?) + }; + + Ok(p) + } +} + +impl Token for u16 {} + +impl ParseToken for CssParser { + fn parse_token(&mut self) -> Result, String> { + let count = self.expect_consume()?; + let p = if Self::check_if_variable(count.as_str()) { + Self::parse_prop_value_variable(count.as_str())? + } else { + PropertyValue::Other(count.parse().map_err(|_| { + format!( + "invalid token, expect middle range number greater or equal 0 got {:?}", + count + ) + })?) + }; + + Ok(p) + } +} + +impl Token for u32 {} + +impl ParseToken for CssParser { + fn parse_token(&mut self) -> Result, String> { + let count = self.expect_consume()?; + let p = if Self::check_if_variable(count.as_str()) { + Self::parse_prop_value_variable(count.as_str())? + } else { + PropertyValue::Other(count.parse().map_err(|_| { + format!( + "invalid token, expect number greater or equal 0 got {:?}", + count + ) + })?) + }; + + Ok(p) + } +} + +impl Token for f64 {} + +impl ParseToken for CssParser { + fn parse_token(&mut self) -> Result, String> { + let count = self.expect_consume()?; + let p = if Self::check_if_variable(count.as_str()) { + Self::parse_prop_value_variable(count.as_str())? + } else { + PropertyValue::Other(count.parse().map_err(|_| { + format!( + "invalid token, expect floating point number got {:?}", + count + ) + })?) + }; + + Ok(p) + } +} + +impl PropertyValue { + fn into_color_alpha(self) -> PropertyValue { + match self { + PropertyValue::Variable(v) => PropertyValue::Variable(v), + PropertyValue::Other(f) => PropertyValue::Other((f * 255f64) as u8), + } + } +} + +impl Token for String {} + +impl ParseToken for CssParser { + fn parse_token(&mut self) -> Result, String> { + self.consume_expected(":")?; + let name = match self.expect_consume()?.as_str() { + name @ _ if Self::check_if_variable(name) => Self::parse_prop_value_variable(name)?, + name @ _ => PropertyValue::Other(name.to_string()), + }; + Ok(name) + } +} + #[derive(Debug, PartialEq)] pub enum DisplayProperty { Inline, @@ -736,6 +754,19 @@ pub enum DisplayProperty { Inherit, } +impl Token for DisplayProperty {} + +impl ParseToken for CssParser { + fn parse_token(&mut self) -> Result, String> { + self.skip_white(); + self.consume_expected(":")?; + self.skip_white(); + let p = self.parse_expected::()?; + self.consume_expected(";")?; + Ok(PropertyValue::Other(p)) + } +} + impl FromStr for DisplayProperty { type Err = String; @@ -782,6 +813,19 @@ pub enum AlignContentProperty { Inherit, } +impl Token for AlignContentProperty {} + +impl ParseToken for CssParser { + fn parse_token(&mut self) -> Result, String> { + self.skip_white(); + self.consume_expected(":")?; + self.skip_white(); + let p = self.parse_expected_prop_value()?; + self.consume_expected(";")?; + Ok(p) + } +} + impl FromStr for AlignContentProperty { type Err = String; @@ -812,6 +856,19 @@ pub enum AlignItemsProperty { Inherit, } +impl Token for AlignItemsProperty {} + +impl ParseToken for CssParser { + fn parse_token(&mut self) -> Result, String> { + self.skip_white(); + self.consume_expected(":")?; + self.skip_white(); + let p = self.parse_expected_prop_value()?; + self.consume_expected(";")?; + Ok(p) + } +} + impl FromStr for AlignItemsProperty { type Err = String; @@ -841,6 +898,19 @@ pub enum AlignSelfProperty { Inherit, } +impl Token for AlignSelfProperty {} + +impl ParseToken for CssParser { + fn parse_token(&mut self) -> Result, String> { + self.skip_white(); + self.consume_expected(":")?; + self.skip_white(); + let p = self.parse_expected_prop_value::()?; + self.consume_expected(";")?; + Ok(p) + } +} + impl FromStr for AlignSelfProperty { type Err = String; @@ -866,6 +936,19 @@ pub enum AllProperty { Unset, } +impl Token for AllProperty {} + +impl ParseToken for CssParser { + fn parse_token(&mut self) -> Result, String> { + self.skip_white(); + self.consume_expected(":")?; + self.skip_white(); + let p = self.parse_expected_prop_value::()?; + self.consume_expected(";")?; + Ok(p) + } +} + impl FromStr for AllProperty { type Err = String; @@ -890,6 +973,16 @@ pub enum AnimationDirectionProperty { Inherit, } +impl Token for AnimationDirectionProperty {} + +impl ParseToken for CssParser { + fn parse_token(&mut self) -> Result, String> { + let p = self.parse_expected_prop_value::()?; + self.consume_expected(";")?; + Ok(p) + } +} + impl FromStr for AnimationDirectionProperty { type Err = String; @@ -967,24 +1060,28 @@ pub enum TimeProperty { Inherit, } -impl FromStr for TimeProperty { - type Err = String; +impl Token for TimeProperty {} - fn from_str(s: &str) -> Result { - let p = match s { +impl ParseToken for CssParser { + fn parse_token(&mut self) -> ValueResult { + self.skip_white(); + self.consume_expected(":")?; + self.skip_white(); + let current = self.expect_consume()?; + let p = match current.as_str() { "initial" => TimeProperty::Initial, "inherit" => TimeProperty::Inherit, - _ if s.ends_with("ms") => match s[0..(s.len() - 2)].parse::() { + _ if current.ends_with("ms") => match current[0..(current.len() - 2)].parse::() { Ok(n) => TimeProperty::MilliSeconds(n), _ => return Err("invalid time".to_string()), }, - _ if s.ends_with("s") => match s[0..(s.len() - 1)].parse::() { + _ if current.ends_with("s") => match current[0..(current.len() - 1)].parse::() { Ok(n) => TimeProperty::Seconds(n), _ => return Err("invalid time".to_string()), }, _ => return Err("invalid time".to_string()), }; - Ok(p) + Ok(PropertyValue::Other(p)) } } @@ -994,15 +1091,17 @@ pub enum AnimationTimingFunctionSteps { End, } -impl FromStr for AnimationTimingFunctionSteps { - type Err = String; +impl Token for AnimationTimingFunctionSteps {} - fn from_str(s: &str) -> Result { - match s.to_lowercase().as_str() { - "start" => Ok(AnimationTimingFunctionSteps::Start), - "end" => Ok(AnimationTimingFunctionSteps::End), - _ => Err(format!("invalid animation timing function step {:?}", s)), - } +impl ParseToken for CssParser { + fn parse_token(&mut self) -> ValueResult { + let s = self.expect_consume()?; + let p = match s.to_lowercase().as_str() { + "start" => AnimationTimingFunctionSteps::Start, + "end" => AnimationTimingFunctionSteps::End, + _ => return Err(format!("invalid animation timing function step {:?}", s)), + }; + Ok(PropertyValue::Other(p)) } } @@ -1015,17 +1114,29 @@ pub enum AnimationTimingFunction { EaseInOut, StepStart, StepEnd, - Steps(u32, AnimationTimingFunctionSteps), - CubicBezier(f64, f64, f64, f64), + Steps( + PropertyValue, + PropertyValue, + ), + CubicBezier( + PropertyValue, + PropertyValue, + PropertyValue, + PropertyValue, + ), Initial, Inherit, } -impl FromStr for AnimationTimingFunction { - type Err = String; +impl Token for AnimationTimingFunction {} - fn from_str(s: &str) -> Result { - let p = match s.to_string().as_str() { +impl ParseToken for CssParser { + fn parse_token(&mut self) -> Result, String> { + self.skip_white(); + self.consume_expected(":")?; + self.skip_white(); + let current = self.expect_consume()?; + let p = match current.as_str() { "linear" => AnimationTimingFunction::Linear, "ease" => AnimationTimingFunction::Ease, "ease-in" => AnimationTimingFunction::EaseIn, @@ -1035,61 +1146,74 @@ impl FromStr for AnimationTimingFunction { "step-end" => AnimationTimingFunction::StepEnd, "initial" => AnimationTimingFunction::Initial, "inherit" => AnimationTimingFunction::Inherit, - _ if s.starts_with("steps(") => { - let n_start = "steps(".len(); - let (n_end, _) = s - .char_indices() - .find(|(_idx, c)| *c == ',') - .ok_or_else(|| format!("invalid animation timing function {:?}", s))?; - let b = s[n_start..n_end] - .trim() - .parse::() - .map_err(|_| format!("invalid animation timing function {:?}", s))?; - if b == 0 { - return Err(format!("invalid animation timing function {:?}", s)); + "steps" => { + self.consume_expected("(")?; + self.skip_white(); + let b = self.parse_token()?; + match b { + PropertyValue::Other(n) if n <= 0 => { + return Err(format!("invalid animation timing function, number of iterations must be greater than 0")); + } + _ => (), } - let c = s[(n_end + 1)..(s.len() - 1)] - .trim() - .parse::() - .map_err(|_| format!("invalid animation timing function {:?}", s))?; + self.skip_white(); + self.consume_expected(",")?; + self.skip_white(); + let c = self.parse_token()?; + self.skip_white(); + self.consume_expected(")")?; + self.skip_white(); + self.consume_expected(";")?; AnimationTimingFunction::Steps(b, c) } - _ if s.starts_with("cubic-bezier(") => { - let v: Vec = s["cubic-bezier(".len()..(s.len() - 1)] - .split(',') - .filter_map(|p| p.trim().parse::().ok()) - .collect(); - if v.len() != 4 { - return Err(format!("invalid animation timing function {:?}", s)); - } - AnimationTimingFunction::CubicBezier(v[0], v[1], v[2], v[3]) + "cubic-bezier" => { + self.consume_expected("(")?; + self.skip_white(); + let a = self.parse_token()?; + self.skip_white(); + self.consume_expected(",")?; + self.skip_white(); + let b = self.parse_token()?; + self.skip_white(); + self.consume_expected(",")?; + self.skip_white(); + let c = self.parse_token()?; + self.skip_white(); + self.consume_expected(",")?; + self.skip_white(); + let d = self.parse_token()?; + self.skip_white(); + self.consume_expected(")")?; + self.skip_white(); + self.consume_expected(";")?; + AnimationTimingFunction::CubicBezier(a, b, c, d) } - // "steps(int,start|end)" => AnimationTimingFunction::Steps(int, start | end), - // "cubic-bezier(n,n,n,n)" => AnimationTimingFunction::CubicBezier(n, n, n, n), - _ => return Err(format!("invalid animation timing function {:?}", s)), + _ => return Err(format!("invalid animation timing function {:?}", current)), }; - Ok(p) + Ok(PropertyValue::Other(p)) } } #[derive(Debug, PartialEq)] pub enum AnimationDelayProperty { - Time(TimeProperty), + Time(PropertyValue), Initial, Inherit, } -impl FromStr for AnimationDelayProperty { - type Err = String; +impl Token for AnimationDelayProperty {} - fn from_str(s: &str) -> Result { - match s { - "initial" => Ok(AnimationDelayProperty::Initial), - "inherit" => Ok(AnimationDelayProperty::Inherit), - _ => s - .parse::() - .map(|p| AnimationDelayProperty::Time(p)), - } +impl ParseToken for CssParser { + fn parse_token(&mut self) -> Result, String> { + self.skip_white(); + let current = self.expect_consume()?; + let p = match current.as_str() { + "initial" => AnimationDelayProperty::Initial, + "inherit" => AnimationDelayProperty::Inherit, + _ => AnimationDelayProperty::Time(self.parse_token()?), + }; + self.consume_expected(";")?; + Ok(PropertyValue::Other(p)) } } @@ -1109,6 +1233,99 @@ pub enum AnimationProperty { ), } +impl Token for AnimationProperty {} + +impl ParseToken for CssParser { + fn parse_token(&mut self) -> Result, String> { + self.skip_white(); + self.consume_expected(":")?; + self.skip_white(); + let def = self.expect_consume()?; + let p = match def.as_str() { + "initial" => PropertyValue::Other(AnimationProperty::Initial), + "inherit" => PropertyValue::Other(AnimationProperty::Inherit), + _ if Self::check_if_variable(def.as_str()) => { + Self::parse_prop_value_variable(def.as_str())? + } + _ => { + let name = def; + self.skip_white(); + + let duration = if self.current_is_semicolon() { + PropertyValue::Other(TimeProperty::Seconds(0)) + } else { + let v = self.parse_token()?; + self.skip_white(); + v + }; + let timing = if self.current_is_semicolon() { + PropertyValue::Other(AnimationTimingFunction::Ease) + } else { + let v = self.parse_token()?; + self.skip_white(); + v + }; + let delay = if self.current_is_semicolon() { + PropertyValue::Other(AnimationDelayProperty::Time(PropertyValue::Other( + TimeProperty::Seconds(0), + ))) + } else { + let v = self.parse_token()?; + self.skip_white(); + v + }; + let iteration_count = if self.current_is_semicolon() { + PropertyValue::Other(1) + } else { + let count = self.expect_consume()?; + let v = count.parse::().map_err(|_| { + format!( + "invalid animation iteration count, expect number got {:?}", + count + ) + })?; + self.skip_white(); + PropertyValue::Other(v) + }; + let direction = if self.current_is_semicolon() { + PropertyValue::Other(AnimationDirectionProperty::Normal) + } else { + let v = self.parse_expected_prop_value::()?; + self.skip_white(); + v + }; + let fill_mode = if self.current_is_semicolon() { + PropertyValue::Other(AnimationFillModeProperty::None) + } else { + let v = self.parse_expected_prop_value::()?; + self.skip_white(); + v + }; + let play_state = if self.current_is_semicolon() { + PropertyValue::Other(AnimationPlayStateProperty::Running) + } else { + let v = self.parse_expected_prop_value::()?; + self.skip_white(); + v + }; + + PropertyValue::Other(AnimationProperty::Custom( + name, + duration, + timing, + delay, + iteration_count, + direction, + fill_mode, + play_state, + )) + } + }; + self.consume_expected(";")?; + Ok(p) + } +} + #[derive(Debug, PartialEq)] pub enum JustifyContentProperty { FlexStart, @@ -1120,6 +1337,19 @@ pub enum JustifyContentProperty { Inherit, } +impl Token for JustifyContentProperty {} + +impl ParseToken for CssParser { + fn parse_token(&mut self) -> Result, String> { + self.skip_white(); + self.consume_expected(":")?; + self.skip_white(); + let p = self.parse_expected_prop_value::()?; + self.consume_expected(";")?; + Ok(p) + } +} + impl FromStr for JustifyContentProperty { type Err = String; @@ -1187,18 +1417,66 @@ impl FromStr for ZIndexProperty { } #[derive(Debug, PartialEq)] -pub enum ColorProperty { - Name(String), - Rgba(u8, u8, u8, u8), - Hsla(u16, u8, u8, f64), - Current, +pub enum ClearProperty { + None, + Left, + Right, + Both, + InlineStart, + InlineEnd, + + Initial, + Inherit, + Unset, } -impl FromStr for ColorProperty { +impl FromStr for ClearProperty { type Err = String; fn from_str(s: &str) -> Result { - let p = match s.trim() { + let p = match s { + "none" => ClearProperty::None, + "left" => ClearProperty::Left, + "right" => ClearProperty::Right, + "both" => ClearProperty::Both, + "inline-start" => ClearProperty::InlineStart, + "inline-end" => ClearProperty::InlineEnd, + "initial" => ClearProperty::Initial, + "inherit" => ClearProperty::Inherit, + "unset" => ClearProperty::Unset, + _ => return Err(format!("invalid clear {:?}", s)), + }; + Ok(p) + } +} + +#[derive(Debug, PartialEq)] +pub enum ColorProperty { + Name(String), + Rgba( + PropertyValue, + PropertyValue, + PropertyValue, + PropertyValue, + ), + Hsla( + PropertyValue, + PropertyValue, + PropertyValue, + PropertyValue, + ), + Current, +} + +impl Token for ColorProperty {} + +impl ParseToken for CssParser { + fn parse_token(&mut self) -> ValueResult { + self.skip_white(); + let current = self.expect_consume()?; + let s = current.trim(); + + let p = match s { "currentColor" => ColorProperty::Current, _ if s.len() == 7 && s.starts_with('#') => { let r = u8::from_str_radix(&s[1..=2], 16) @@ -1207,7 +1485,12 @@ impl FromStr for ColorProperty { .map_err(|_| format!("invalid color {:?}", s))?; let b = u8::from_str_radix(&s[5..=6], 16) .map_err(|_| format!("invalid color {:?}", s))?; - ColorProperty::Rgba(r, g, b, 255) + ColorProperty::Rgba( + PropertyValue::Other(r), + PropertyValue::Other(g), + PropertyValue::Other(b), + PropertyValue::Other(255), + ) } _ if s.len() == 4 && s.starts_with('#') => { let _x = &s[1..=1]; @@ -1217,7 +1500,12 @@ impl FromStr for ColorProperty { .map_err(|_| format!("invalid color {:?}", s))?; let b = u8::from_str_radix(&s[3..=3].repeat(2), 16) .map_err(|_| format!("invalid color {:?}", s))?; - ColorProperty::Rgba(r, g, b, 255) + ColorProperty::Rgba( + PropertyValue::Other(r), + PropertyValue::Other(g), + PropertyValue::Other(b), + PropertyValue::Other(255), + ) } _ if s.len() == 9 && s.starts_with('#') => { let (r, g, b, a) = ( @@ -1230,31 +1518,120 @@ impl FromStr for ColorProperty { u8::from_str_radix(&s[7..=8], 16) .map_err(|_| format!("invalid color {:?}", s))?, ); + ColorProperty::Rgba( + PropertyValue::Other(r), + PropertyValue::Other(g), + PropertyValue::Other(b), + PropertyValue::Other(a), + ) + } + "rgba" => { + self.skip_white(); + self.consume_expected("(")?; + self.skip_white(); + let r = self.parse_token()?; + self.skip_white(); + self.consume_expected(",")?; + self.skip_white(); + let g = self.parse_token()?; + self.skip_white(); + self.consume_expected(",")?; + self.skip_white(); + let b = self.parse_token()?; + self.skip_white(); + self.consume_expected(",")?; + self.skip_white(); + let a = self.parse_token()?.into_color_alpha(); + self.skip_white(); + self.consume_expected(")")?; + self.skip_white(); ColorProperty::Rgba(r, g, b, a) } - _ if s.trim().to_lowercase().starts_with("rgba(") => { - let (r, g, b, a) = parse_rgba(s.trim(), true)?; + "rgb" => { + self.skip_white(); + self.consume_expected("(")?; + self.skip_white(); + let r = self.parse_token()?; + self.skip_white(); + self.consume_expected(",")?; + self.skip_white(); + let g = self.parse_token()?; + self.skip_white(); + self.consume_expected(",")?; + self.skip_white(); + let b = self.parse_token()?; + self.skip_white(); + let a = PropertyValue::Other(255); + self.skip_white(); + self.consume_expected(")")?; + self.skip_white(); ColorProperty::Rgba(r, g, b, a) } - _ if s.trim().to_lowercase().starts_with("rgb(") => { - let (r, g, b, a) = parse_rgba(s.trim(), false)?; - ColorProperty::Rgba(r, g, b, a) - } - _ if s.trim().to_lowercase().starts_with("hsla(") => { - let (h, s, l, a) = parse_hsla(s.trim(), true)?; + "hsla" => { + self.skip_white(); + self.consume_expected("(")?; + self.skip_white(); + let h = self.parse_token()?; + self.skip_white(); + self.consume_expected(",")?; + self.skip_white(); + let s = self.parse_token()?; + self.consume_expected("%")?; + self.skip_white(); + self.consume_expected(",")?; + self.skip_white(); + let l = self.parse_token()?; + self.consume_expected("%")?; + self.skip_white(); + self.consume_expected(",")?; + self.skip_white(); + let a = self.parse_token()?; + match a { + PropertyValue::Other(f) if -0.001f64 > f || f > 1.001f64 => { + return Err(format!("out of range hsl alpha value {:?}", a)) + } + _ => (), + }; + self.skip_white(); + self.consume_expected(")")?; + self.skip_white(); ColorProperty::Hsla(h, s, l, a) } - _ if s.trim().to_lowercase().starts_with("hsl(") => { - let (h, s, l, a) = parse_hsla(s.trim(), false)?; + "hsl" => { + self.skip_white(); + self.consume_expected("(")?; + self.skip_white(); + let h = self.parse_token()?; + self.skip_white(); + self.consume_expected(",")?; + self.skip_white(); + let s = self.parse_token()?; + self.consume_expected("%")?; + self.skip_white(); + self.consume_expected(",")?; + self.skip_white(); + let l = self.parse_token()?; + self.consume_expected("%")?; + let a = PropertyValue::Other(1f64); + self.skip_white(); + self.consume_expected(")")?; + self.skip_white(); ColorProperty::Hsla(h, s, l, a) } _ => s .parse::() - .map(|c| c.to_hex()) - .and_then(|s| s.parse::())?, + .map(|c| c.to_values()) + .and_then(|(r, g, b)| { + Ok(ColorProperty::Rgba( + PropertyValue::Other(r), + PropertyValue::Other(g), + PropertyValue::Other(b), + PropertyValue::Other(255), + )) + })?, }; - Ok(p) + Ok(PropertyValue::Other(p)) } } @@ -1352,6 +1729,64 @@ impl FromStr for BackgroundAttachmentProperty { } } +#[derive(Debug, PartialEq)] +pub enum AngleUnit { + Deg(f64), + Grad(f64), + Rad(f64), + Turn(f64), +} + +#[derive(Debug, PartialEq)] +pub enum PositionProperty { + Static, + Relative, + Absolute, + Fixed, + Sticky, +} + +impl FromStr for PositionProperty { + type Err = String; + + fn from_str(s: &str) -> Result { + let p = match s { + "static" => PositionProperty::Static, + "relative" => PositionProperty::Relative, + "absolute" => PositionProperty::Absolute, + "fixed" => PositionProperty::Fixed, + "sticky" => PositionProperty::Sticky, + _ => return Err(format!("invalid position {:?}", s)), + }; + Ok(p) + } +} + +#[derive(Debug, PartialEq)] +pub enum FloatProperty { + Left, + Right, + None, + InlineStart, + InlineEnd, +} + +impl FromStr for FloatProperty { + type Err = String; + + fn from_str(s: &str) -> Result { + let p = match s { + "left " => FloatProperty::Left, + "right " => FloatProperty::Right, + "none " => FloatProperty::None, + "inline-start" => FloatProperty::InlineStart, + "inline-end" => FloatProperty::InlineEnd, + _ => return Err(format!("invalid float {:?}", s)), + }; + Ok(p) + } +} + #[derive(Debug, PartialEq)] pub enum PropertyValue { Variable(String), @@ -1365,7 +1800,7 @@ pub enum Property { AlignSelf(PropertyValue), All(PropertyValue), Animation(PropertyValue), - AnimationDelay(PropertyValue), + AnimationDelay(PropertyValue), AnimationDirection(PropertyValue), AnimationDuration(PropertyValue), AnimationFillMode(PropertyValue), @@ -1427,10 +1862,10 @@ pub enum Property { CaptionSide(String), CaretColor(String), AtCharset(String), - Clear(String), + Clear(PropertyValue), Clip(String), ClipPath(String), - Color(String), + Color(PropertyValue), ColumnCount(String), ColumnFill(String), ColumnGap(String), @@ -1446,7 +1881,7 @@ pub enum Property { CounterReset(String), Cursor(String), Direction(String), - Display(DisplayProperty), + Display(PropertyValue), EmptyCells(String), Filter(String), Flex(String), @@ -1456,7 +1891,7 @@ pub enum Property { FlexGrow(String), FlexShrink(String), FlexWrap(String), - Float(String), + Float(PropertyValue), Font(String), AtFontFace(String), FontFamily(String), @@ -1535,7 +1970,7 @@ pub enum Property { Perspective(String), PerspectiveOrigin(String), PointerEvents(String), - Position(String), + Position(PropertyValue), Quotes(String), Resize(String), Right(String), @@ -1573,56 +2008,143 @@ pub enum Property { WordWrap(String), WritingMode(String), ZIndex(PropertyValue), - Variable(String, String), + Variable(String, PropertyValue), } #[cfg(test)] mod tests { - use crate::prop::*; + use super::*; + + /// we assume currently we hit property name + /// display : block; + /// ^ + /// so we need to add `:` + /// + /// But we also we adding spaces around because they are allowed in css and needs to be skipped + fn parse_prop_value(s: &str) -> CssParser { + let full = format!(" : {} {}", s, if s.contains(";") { "" } else { ";" }); + let tokens = CssTokenizer::new(full.as_str()).tokenize(); + CssParser::new("", tokens) + } + + fn parse_simple_value(s: &str) -> CssParser { + let tokens = CssTokenizer::new(s).tokenize(); + CssParser::new("", tokens) + } + + #[test] + fn parse_prop_value_variable() { + let res: Result, String> = + CssParser::parse_prop_value_variable("var(--a)"); + let expected = Ok(PropertyValue::Variable("a".to_string())); + assert_eq!(res, expected); + let res: Result, String> = + CssParser::parse_prop_value_variable("var(--foo)"); + let expected = Ok(PropertyValue::Variable("foo".to_string())); + assert_eq!(res, expected); + let res: Result, String> = CssParser::parse_prop_value_variable("--foo"); + let expected = Err(r#"given string is not a variable "--foo""#.to_string()); + assert_eq!(res, expected); + let res: Result, String> = CssParser::parse_prop_value_variable("foo"); + let expected = Err(r#"given string is not a variable "foo""#.to_string()); + assert_eq!(res, expected); + } #[test] fn parse_color() { - let res = "#123".parse::(); - let expected = Ok(ColorProperty::Rgba(17, 34, 51, 255)); + let res: ValueResult = parse_simple_value("#123").parse_token(); + let expected = Ok(PropertyValue::Other(ColorProperty::Rgba( + PropertyValue::Other(17), + PropertyValue::Other(34), + PropertyValue::Other(51), + PropertyValue::Other(255), + ))); assert_eq!(res, expected); - let res = "#010203".parse::(); - let expected = Ok(ColorProperty::Rgba(1, 2, 3, 255)); + let res: ValueResult = parse_simple_value("#010203").parse_token(); + let expected = Ok(PropertyValue::Other(ColorProperty::Rgba( + PropertyValue::Other(1), + PropertyValue::Other(2), + PropertyValue::Other(3), + PropertyValue::Other(255), + ))); assert_eq!(res, expected); - let res = "#fff".parse::(); - let expected = Ok(ColorProperty::Rgba(255, 255, 255, 255)); + let res: ValueResult = parse_simple_value("#fff").parse_token(); + let expected = Ok(PropertyValue::Other(ColorProperty::Rgba( + PropertyValue::Other(255), + PropertyValue::Other(255), + PropertyValue::Other(255), + PropertyValue::Other(255), + ))); assert_eq!(res, expected); - let res = "#ffffff".parse::(); - let expected = Ok(ColorProperty::Rgba(255, 255, 255, 255)); + let res: ValueResult = parse_simple_value("#ffffff").parse_token(); + let expected = Ok(PropertyValue::Other(ColorProperty::Rgba( + PropertyValue::Other(255), + PropertyValue::Other(255), + PropertyValue::Other(255), + PropertyValue::Other(255), + ))); assert_eq!(res, expected); - let res = "#abcdef".parse::(); - let expected = Ok(ColorProperty::Rgba(171, 205, 239, 255)); + let res: ValueResult = parse_simple_value("#abcdef").parse_token(); + let expected = Ok(PropertyValue::Other(ColorProperty::Rgba( + PropertyValue::Other(171), + PropertyValue::Other(205), + PropertyValue::Other(239), + PropertyValue::Other(255), + ))); assert_eq!(res, expected); - let res = "currentColor".parse::(); - let expected = Ok(ColorProperty::Current); + let res: ValueResult = parse_simple_value("currentColor").parse_token(); + let expected = Ok(PropertyValue::Other(ColorProperty::Current)); assert_eq!(res, expected); - let res = "rgb(1,2,3)".parse::(); - let expected = Ok(ColorProperty::Rgba(1, 2, 3, 255)); + let res: ValueResult = parse_simple_value("rgb(1,2,3)").parse_token(); + let expected = Ok(PropertyValue::Other(ColorProperty::Rgba( + PropertyValue::Other(1), + PropertyValue::Other(2), + PropertyValue::Other(3), + PropertyValue::Other(255), + ))); assert_eq!(res, expected); - let res = "rgb(1,2,3,.2)".parse::(); - let expected = Ok(ColorProperty::Rgba(1, 2, 3, 255)); + let res: ValueResult = parse_simple_value("rgb(1,2,3,.2)").parse_token(); + let expected = Err("expect to find token \")\" but found \",\"".to_string()); assert_eq!(res, expected); - let res = "rgba(1,2,3,.1)".parse::(); - let expected = Ok(ColorProperty::Rgba(1, 2, 3, 25)); + let res: ValueResult = parse_simple_value("rgba(1,2,3,.1)").parse_token(); + let expected = Ok(PropertyValue::Other(ColorProperty::Rgba( + PropertyValue::Other(1), + PropertyValue::Other(2), + PropertyValue::Other(3), + PropertyValue::Other(25), + ))); assert_eq!(res, expected); - let res = "hsla(100,20%,30%,.4)".parse::(); - let expected = Ok(ColorProperty::Hsla(100, 20, 30, 0.4f64)); + let res: ValueResult = + parse_simple_value("hsla(100,20%,30%,.4)").parse_token(); + let expected = Ok(PropertyValue::Other(ColorProperty::Hsla( + PropertyValue::Other(100), + PropertyValue::Other(20), + PropertyValue::Other(30), + PropertyValue::Other(0.4f64), + ))); assert_eq!(res, expected); - let res = "hsl(100,20%,30%)".parse::(); - let expected = Ok(ColorProperty::Hsla(100, 20, 30, 1.0f64)); + let res: ValueResult = parse_simple_value("hsl(100,20%,30%)").parse_token(); + let expected = Ok(PropertyValue::Other(ColorProperty::Hsla( + PropertyValue::Other(100), + PropertyValue::Other(20), + PropertyValue::Other(30), + PropertyValue::Other(1.0f64), + ))); assert_eq!(res, expected); - let res = "hsl(100,20%,30%,0.5)".parse::(); - let expected = Ok(ColorProperty::Hsla(100, 20, 30, 1.0f64)); + let res: ValueResult = + parse_simple_value("hsl(100,20%,30%,0.5)").parse_token(); + let expected = Err("expect to find token \")\" but found \",\"".to_string()); assert_eq!(res, expected); - let res = "CornflowerBlue".parse::(); - let expected = Ok(ColorProperty::Rgba(100, 149, 237, 255)); + let res: ValueResult = parse_simple_value("CornflowerBlue").parse_token(); + let expected = Ok(PropertyValue::Other(ColorProperty::Rgba( + PropertyValue::Other(100), + PropertyValue::Other(149), + PropertyValue::Other(237), + PropertyValue::Other(255), + ))); assert_eq!(res, expected); - let res = "foo".parse::(); - let expected = Err("invalid predefined color \"foo\"".to_string()); + let res: ValueResult = parse_simple_value("foo").parse_token(); + let expected = Err("\"foo\" is not a predefined color".to_string()); assert_eq!(res, expected); } @@ -1635,113 +2157,149 @@ mod tests { #[test] fn parse_time() { - let res = "".parse::(); + let res: ValueResult = parse_prop_value("").parse_token(); let expected = Err("invalid time".to_string()); assert_eq!(res, expected); - let res = "as".parse::(); + let res: ValueResult = parse_prop_value("as").parse_token(); let expected = Err("invalid time".to_string()); assert_eq!(res, expected); - let res = "3s".parse::(); - let expected = Ok(TimeProperty::Seconds(3)); + let res: ValueResult = parse_prop_value("3s").parse_token(); + let expected = Ok(PropertyValue::Other(TimeProperty::Seconds(3))); assert_eq!(res, expected); - let res = "2s".parse::(); - let expected = Ok(TimeProperty::Seconds(2)); + let res: ValueResult = parse_prop_value("2s").parse_token(); + let expected = Ok(PropertyValue::Other(TimeProperty::Seconds(2))); assert_eq!(res, expected); - let res = "-5s".parse::(); - let expected = Ok(TimeProperty::Seconds(-5)); + let res: ValueResult = parse_prop_value("-5s").parse_token(); + let expected = Ok(PropertyValue::Other(TimeProperty::Seconds(-5))); assert_eq!(res, expected); - let res = "3ms".parse::(); - let expected = Ok(TimeProperty::MilliSeconds(3)); + let res: ValueResult = parse_prop_value("3ms").parse_token(); + let expected = Ok(PropertyValue::Other(TimeProperty::MilliSeconds(3))); assert_eq!(res, expected); - let res = "2ms".parse::(); - let expected = Ok(TimeProperty::MilliSeconds(2)); + let res: ValueResult = parse_prop_value("2ms").parse_token(); + let expected = Ok(PropertyValue::Other(TimeProperty::MilliSeconds(2))); assert_eq!(res, expected); - let res = "-5ms".parse::(); - let expected = Ok(TimeProperty::MilliSeconds(-5)); + let res: ValueResult = parse_prop_value("-5ms").parse_token(); + let expected = Ok(PropertyValue::Other(TimeProperty::MilliSeconds(-5))); assert_eq!(res, expected); } #[test] fn parse_animation_timing_function() { - let res = "linear".parse::(); - let expected = Ok(AnimationTimingFunction::Linear); + let res: ValueResult = parse_prop_value("linear").parse_token(); + let expected = Ok(PropertyValue::Other(AnimationTimingFunction::Linear)); assert_eq!(res, expected); - let res = "ease".parse::(); - let expected = Ok(AnimationTimingFunction::Ease); + let res: ValueResult = parse_prop_value("ease").parse_token(); + let expected = Ok(PropertyValue::Other(AnimationTimingFunction::Ease)); assert_eq!(res, expected); - let res = "ease-in".parse::(); - let expected = Ok(AnimationTimingFunction::EaseIn); + let res: ValueResult = parse_prop_value("ease-in").parse_token(); + let expected = Ok(PropertyValue::Other(AnimationTimingFunction::EaseIn)); assert_eq!(res, expected); - let res = "ease-out".parse::(); - let expected = Ok(AnimationTimingFunction::EaseOut); + let res: ValueResult = parse_prop_value("ease-out").parse_token(); + let expected = Ok(PropertyValue::Other(AnimationTimingFunction::EaseOut)); assert_eq!(res, expected); - let res = "ease-in-out".parse::(); - let expected = Ok(AnimationTimingFunction::EaseInOut); + let res: ValueResult = + parse_prop_value("ease-in-out").parse_token(); + let expected = Ok(PropertyValue::Other(AnimationTimingFunction::EaseInOut)); assert_eq!(res, expected); - let res = "step-start".parse::(); - let expected = Ok(AnimationTimingFunction::StepStart); + let res: ValueResult = + parse_prop_value("step-start").parse_token(); + let expected = Ok(PropertyValue::Other(AnimationTimingFunction::StepStart)); assert_eq!(res, expected); - let res = "step-end".parse::(); - let expected = Ok(AnimationTimingFunction::StepEnd); + let res: ValueResult = parse_prop_value("step-end").parse_token(); + let expected = Ok(PropertyValue::Other(AnimationTimingFunction::StepEnd)); assert_eq!(res, expected); - let res = "steps(1,start)".parse::(); - let expected = Ok(AnimationTimingFunction::Steps( - 1, - AnimationTimingFunctionSteps::Start, - )); + let res: ValueResult = + parse_prop_value("steps(1,start)").parse_token(); + let expected = Ok(PropertyValue::Other(AnimationTimingFunction::Steps( + PropertyValue::Other(1), + PropertyValue::Other(AnimationTimingFunctionSteps::Start), + ))); assert_eq!(res, expected); - let res = "steps(3,end)".parse::(); - let expected = Ok(AnimationTimingFunction::Steps( - 3, - AnimationTimingFunctionSteps::End, - )); + let res: ValueResult = + parse_prop_value("steps(3,end)").parse_token(); + let expected = Ok(PropertyValue::Other(AnimationTimingFunction::Steps( + PropertyValue::Other(3), + PropertyValue::Other(AnimationTimingFunctionSteps::End), + ))); assert_eq!(res, expected); - let res = "steps(0,start)".parse::(); - let expected = Err("invalid animation timing function \"steps(0,start)\"".to_string()); + let res: ValueResult = + parse_prop_value("steps(0,start)").parse_token(); + let expected = Err( + "invalid animation timing function, number of iterations must be greater than 0" + .to_string(), + ); assert_eq!(res, expected); - let res = "steps(-2,start)".parse::(); - let expected = Err("invalid animation timing function \"steps(-2,start)\"".to_string()); - assert_eq!(res, expected); - let res = "steps(0,end)".parse::(); - let expected = Err("invalid animation timing function \"steps(0,end)\"".to_string()); - assert_eq!(res, expected); - let res = "steps(-1,end)".parse::(); - let expected = Err("invalid animation timing function \"steps(-1,end)\"".to_string()); - assert_eq!(res, expected); - let res = "steps(end)".parse::(); - let expected = Err("invalid animation timing function \"steps(end)\"".to_string()); - assert_eq!(res, expected); - let res = "steps(start)".parse::(); - let expected = Err("invalid animation timing function \"steps(start)\"".to_string()); - assert_eq!(res, expected); - let res = "steps(0)".parse::(); - let expected = Err("invalid animation timing function \"steps(0)\"".to_string()); - assert_eq!(res, expected); - let res = "steps(1)".parse::(); - let expected = Err("invalid animation timing function \"steps(1)\"".to_string()); - assert_eq!(res, expected); - let res = "cubic-bezier(0.1,0.2,0.3,0.4)".parse::(); - let expected = Ok(AnimationTimingFunction::CubicBezier(0.1, 0.2, 0.3, 0.4)); - assert_eq!(res, expected); - let res = "cubic-bezier(0.1, 0.2, 0.3, 0.4)".parse::(); - let expected = Ok(AnimationTimingFunction::CubicBezier(0.1, 0.2, 0.3, 0.4)); - assert_eq!(res, expected); - let res = "cubic-bezier(0.1,0.2,0.3)".parse::(); + let res: ValueResult = + parse_prop_value("steps(-2,start)").parse_token(); let expected = - Err(r#"invalid animation timing function "cubic-bezier(0.1,0.2,0.3)""#.to_string()); + Err("invalid token, expect number greater or equal 0 got \"-2\"".to_string()); assert_eq!(res, expected); - let res = "cubic-bezier(0.1,0.2)".parse::(); + let res: ValueResult = + parse_prop_value("steps(0,end)").parse_token(); + let expected = Err( + "invalid animation timing function, number of iterations must be greater than 0" + .to_string(), + ); + assert_eq!(res, expected); + let res: ValueResult = + parse_prop_value("steps(-1,end)").parse_token(); let expected = - Err(r#"invalid animation timing function "cubic-bezier(0.1,0.2)""#.to_string()); + Err("invalid token, expect number greater or equal 0 got \"-1\"".to_string()); assert_eq!(res, expected); - let res = "cubic-bezier(0.1)".parse::(); - let expected = Err(r#"invalid animation timing function "cubic-bezier(0.1)""#.to_string()); + let res: ValueResult = + parse_prop_value("steps(end)").parse_token(); + let expected = + Err("invalid token, expect number greater or equal 0 got \"end\"".to_string()); assert_eq!(res, expected); - let res = "initial".parse::(); - let expected = Ok(AnimationTimingFunction::Initial); + let res: ValueResult = + parse_prop_value("steps(start)").parse_token(); + let expected = + Err("invalid token, expect number greater or equal 0 got \"start\"".to_string()); assert_eq!(res, expected); - let res = "inherit".parse::(); - let expected = Ok(AnimationTimingFunction::Inherit); + let res: ValueResult = parse_prop_value("steps(0)").parse_token(); + let expected = Err( + "invalid animation timing function, number of iterations must be greater than 0" + .to_string(), + ); + assert_eq!(res, expected); + let res: ValueResult = parse_prop_value("steps(1)").parse_token(); + let expected = Err("expect to find token \",\" but found \")\"".to_string()); + assert_eq!(res, expected); + let res: ValueResult = + parse_prop_value("cubic-bezier(0.1,0.2,0.3,0.4)").parse_token(); + let expected = Ok(PropertyValue::Other(AnimationTimingFunction::CubicBezier( + PropertyValue::Other(0.1), + PropertyValue::Other(0.2), + PropertyValue::Other(0.3), + PropertyValue::Other(0.4), + ))); + assert_eq!(res, expected); + let res: ValueResult = + parse_prop_value("cubic-bezier(0.1, 0.2, 0.3, 0.4)").parse_token(); + let expected = Ok(PropertyValue::Other(AnimationTimingFunction::CubicBezier( + PropertyValue::Other(0.1), + PropertyValue::Other(0.2), + PropertyValue::Other(0.3), + PropertyValue::Other(0.4), + ))); + assert_eq!(res, expected); + let res: ValueResult = + parse_prop_value("cubic-bezier(0.1,0.2,0.3)").parse_token(); + let expected = Err("expect to find token \",\" but found \")\"".to_string()); + assert_eq!(res, expected); + let res: ValueResult = + parse_prop_value("cubic-bezier(0.1,0.2)").parse_token(); + let expected = Err("expect to find token \",\" but found \")\"".to_string()); + assert_eq!(res, expected); + let res: ValueResult = + parse_prop_value("cubic-bezier(0.1)").parse_token(); + let expected = Err("expect to find token \",\" but found \")\"".to_string()); + assert_eq!(res, expected); + let res: ValueResult = parse_prop_value("initial").parse_token(); + let expected = Ok(PropertyValue::Other(AnimationTimingFunction::Initial)); + assert_eq!(res, expected); + let res: ValueResult = parse_prop_value("inherit").parse_token(); + let expected = Ok(PropertyValue::Other(AnimationTimingFunction::Inherit)); assert_eq!(res, expected); } @@ -1797,11 +2355,38 @@ mod tests { ) } + #[test] + fn tokenize_p_background_image_url() { + let source = r#"p { background-image: url("hello/world.jpg"); }"#; + let result = CssTokenizer::new(source).tokenize(); + assert_eq!( + result, + vec![ + "p".to_string(), + " ".to_string(), + "{".to_string(), + " ".to_string(), + "background-image".to_string(), + ":".to_string(), + " ".to_string(), + "url".to_string(), + "(".to_string(), + "\"".to_string(), + "hello/world.jpg".to_string(), + "\"".to_string(), + ")".to_string(), + ";".to_string(), + " ".to_string(), + "}".to_string() + ], + ) + } + #[test] fn parse_empty_p_selector() { let source = r#"p{}"#; let tokens = CssTokenizer::new(source).tokenize(); - let result = CssParser::new(tokens).parse(); + let result = CssParser::new("", tokens).parse(); assert_eq!( result, Ok(vec![Selector { @@ -1815,7 +2400,7 @@ mod tests { fn parse_p_align_content() { let source = r#"p { align-content: flex-start; }"#; let tokens = CssTokenizer::new(source).tokenize(); - let result = CssParser::new(tokens).parse(); + let result = CssParser::new("", tokens).parse(); assert_eq!( result, Ok(vec![Selector { @@ -1833,13 +2418,15 @@ mod tests { fn parse_p_display_block() { let source = r#"p { display: block; }"#; let tokens = CssTokenizer::new(source).tokenize(); - let result = CssParser::new(tokens).parse(); + let result = CssParser::new("", tokens).parse(); assert_eq!( result, Ok(vec![Selector { path: vec![SelectorPart::TagName("p".to_string())], block: Block { - properties: vec![Property::Display(DisplayProperty::Block),] + properties: vec![Property::Display(PropertyValue::Other( + DisplayProperty::Block + ))] }, }]), ) @@ -1849,7 +2436,7 @@ mod tests { fn parse_long_path() { let source = r#"p p {}"#; let tokens = CssTokenizer::new(source).tokenize(); - let result = CssParser::new(tokens).parse(); + let result = CssParser::new("", tokens).parse(); assert_eq!( result, Ok(vec![Selector { @@ -1866,7 +2453,7 @@ mod tests { fn parse_long_path_with_parent_bound() { let source = r#"p > p {}"#; let tokens = CssTokenizer::new(source).tokenize(); - let result = CssParser::new(tokens).parse(); + let result = CssParser::new("", tokens).parse(); assert_eq!( result, Ok(vec![Selector { @@ -1884,7 +2471,7 @@ mod tests { fn parse_long_path_with_before_sibling_bound() { let source = r#"p ~ p {}"#; let tokens = CssTokenizer::new(source).tokenize(); - let result = CssParser::new(tokens).parse(); + let result = CssParser::new("", tokens).parse(); assert_eq!( result, Ok(vec![Selector { @@ -1902,7 +2489,7 @@ mod tests { fn parse_long_path_with_after_sibling_bound() { let source = r#"p + p {}"#; let tokens = CssTokenizer::new(source).tokenize(); - let result = CssParser::new(tokens).parse(); + let result = CssParser::new("", tokens).parse(); assert_eq!( result, Ok(vec![Selector { @@ -1920,14 +2507,14 @@ mod tests { fn parse_multiple_properties() { let source = r#"p { display: block; justify-content: initial; }"#; let tokens = CssTokenizer::new(source).tokenize(); - let result = CssParser::new(tokens).parse(); + let result = CssParser::new("", tokens).parse(); assert_eq!( result, Ok(vec![Selector { path: vec![SelectorPart::TagName("p".to_string()),], block: Block { properties: vec![ - Property::Display(DisplayProperty::Block), + Property::Display(PropertyValue::Other(DisplayProperty::Block)), Property::JustifyContent(PropertyValue::Other( JustifyContentProperty::Initial )), @@ -1941,7 +2528,7 @@ mod tests { fn parse_animation() { let source = r#"p { animation: initial; }"#; let tokens = CssTokenizer::new(source).tokenize(); - let result = CssParser::new(tokens).parse(); + let result = CssParser::new("", tokens).parse(); assert_eq!( result, Ok(vec![Selector { @@ -1955,7 +2542,7 @@ mod tests { ); let source = r#"p { animation: inherit; }"#; let tokens = CssTokenizer::new(source).tokenize(); - let result = CssParser::new(tokens).parse(); + let result = CssParser::new("", tokens).parse(); assert_eq!( result, Ok(vec![Selector { @@ -1969,12 +2556,14 @@ mod tests { ); let source = r#"p { animation: some; }"#; let tokens = CssTokenizer::new(source).tokenize(); - let result = CssParser::new(tokens).parse(); + let result = CssParser::new("", tokens).parse(); let animation = Property::Animation(PropertyValue::Other(AnimationProperty::Custom( "some".to_string(), PropertyValue::Other(TimeProperty::Seconds(0)), PropertyValue::Other(AnimationTimingFunction::Ease), - PropertyValue::Other(AnimationDelayProperty::Time(TimeProperty::Seconds(0))), + PropertyValue::Other(AnimationDelayProperty::Time(PropertyValue::Other( + TimeProperty::Seconds(0), + ))), PropertyValue::Other(1), PropertyValue::Other(AnimationDirectionProperty::Normal), PropertyValue::Other(AnimationFillModeProperty::None), @@ -1990,4 +2579,48 @@ mod tests { }]), ); } + + #[test] + fn test_file_a() { + let tokens = CssTokenizer::new(include_str!("../tests/a.css")).tokenize(); + let res = CssParser::new("../tests/a.css", tokens).parse(); + let expected: Result, String> = Ok(vec![ + Selector { + path: vec![SelectorPart::Class("foo".to_string())], + block: Block { properties: vec![] }, + }, + Selector { + path: vec![SelectorPart::Class("bar".to_string())], + block: Block { properties: vec![] }, + }, + Selector { + path: vec![SelectorPart::Class("foz".to_string())], + block: Block { properties: vec![] }, + }, + Selector { + path: vec![SelectorPart::Class("baz".to_string())], + block: Block { + properties: vec![ + Property::Display(PropertyValue::Other(DisplayProperty::Block)), + Property::JustifyContent(PropertyValue::Other( + JustifyContentProperty::SpaceBetween, + )), + Property::Color(PropertyValue::Other(ColorProperty::Rgba( + PropertyValue::Other(255), + PropertyValue::Other(0), + PropertyValue::Other(0), + PropertyValue::Other(255), + ))), + Property::BackgroundColor(PropertyValue::Other(ColorProperty::Rgba( + PropertyValue::Other(66), + PropertyValue::Other(65), + PropertyValue::Other(61), + PropertyValue::Other(255), + ))), + ], + }, + }, + ]); + assert_eq!(res, expected); + } } diff --git a/jirs-css/tests/a.css b/jirs-css/tests/a.css new file mode 100644 index 00000000..e1166aba --- /dev/null +++ b/jirs-css/tests/a.css @@ -0,0 +1,16 @@ +.foo {} + +.bar { +} + +.foz { + + +} + +.baz { + display: block; + justify-content : space-between; + color: red; + background-color: #42413d; +}