From 75d69927447d84f3279a91e8fa88e5a3c5c94d6e Mon Sep 17 00:00:00 2001 From: Savchenko Ivan <73419411+Aiving@users.noreply.github.com> Date: Sat, 13 Jul 2024 08:24:41 +0000 Subject: [PATCH 01/24] rethink parsing --- crates/components/src/graph.rs | 2 +- crates/devtools/src/property.rs | 2 +- crates/hooks/src/use_animation.rs | 6 +- crates/hooks/tests/use_animation.rs | 2 +- crates/renderer/src/config.rs | 2 +- crates/state/src/cursor.rs | 18 +- crates/state/src/font_style.rs | 94 ++++---- crates/state/src/layout.rs | 53 +++-- crates/state/src/lexing.rs | 178 +++++++++++++++ crates/state/src/lib.rs | 2 + crates/state/src/parsing.rs | 120 +++++++++- crates/state/src/style.rs | 56 +++-- crates/state/src/values/alignment.rs | 20 +- crates/state/src/values/border.rs | 42 ++-- crates/state/src/values/color.rs | 263 ++++++++++++---------- crates/state/src/values/content.rs | 12 +- crates/state/src/values/corner_radius.rs | 66 ++---- crates/state/src/values/cursor.rs | 12 +- crates/state/src/values/decoration.rs | 44 ++-- crates/state/src/values/fill.rs | 11 +- crates/state/src/values/font.rs | 131 ++++++----- crates/state/src/values/gaps.rs | 88 ++------ crates/state/src/values/gradient.rs | 72 +++--- crates/state/src/values/highlight.rs | 14 +- crates/state/src/values/overflow.rs | 16 +- crates/state/src/values/position.rs | 12 +- crates/state/src/values/shadow.rs | 48 +--- crates/state/src/values/size.rs | 103 +++++---- crates/state/src/values/text_shadow.rs | 27 +-- crates/state/src/viewport.rs | 8 +- crates/state/tests/parse_align.rs | 12 +- crates/state/tests/parse_border.rs | 33 ++- crates/state/tests/parse_color.rs | 36 +-- crates/state/tests/parse_corner_radius.rs | 14 +- crates/state/tests/parse_decoration.rs | 26 ++- crates/state/tests/parse_display.rs | 17 +- crates/state/tests/parse_gaps.rs | 8 +- crates/state/tests/parse_gradient.rs | 30 +-- crates/state/tests/parse_highlight.rs | 9 +- crates/state/tests/parse_shadows.rs | 8 +- crates/state/tests/parse_size.rs | 8 +- crates/state/tests/parse_text_shadow.rs | 2 +- examples/custom_tokio_rt.rs | 4 +- examples/render_canvas.rs | 3 +- examples/shader_editor.rs | 2 +- 45 files changed, 1014 insertions(+), 722 deletions(-) create mode 100644 crates/state/src/lexing.rs diff --git a/crates/components/src/graph.rs b/crates/components/src/graph.rs index e73f4c2ac..02accb0f7 100644 --- a/crates/components/src/graph.rs +++ b/crates/components/src/graph.rs @@ -106,7 +106,7 @@ pub fn Graph(props: GraphProps) -> Element { paint.set_anti_alias(true); paint.set_style(PaintStyle::Fill); - paint.set_color(Color::parse(&line.color).unwrap()); + paint.set_color(Color::parse_value(&line.color).unwrap()); paint.set_stroke_width(3.0); let mut previous_x = None; diff --git a/crates/devtools/src/property.rs b/crates/devtools/src/property.rs index a58f7b753..98cfd6dab 100644 --- a/crates/devtools/src/property.rs +++ b/crates/devtools/src/property.rs @@ -195,7 +195,7 @@ pub fn BorderProperty(name: String, border: Border) -> Element { text { font_size: "15", color: "rgb(252,181,172)", - "{border.width} {border.style:?} {border.alignment:?}" + "{border.width:?} {border.style:?} {border.alignment:?}" } } rect { diff --git a/crates/hooks/src/use_animation.rs b/crates/hooks/src/use_animation.rs index 35371d945..b296d4761 100644 --- a/crates/hooks/src/use_animation.rs +++ b/crates/hooks/src/use_animation.rs @@ -133,13 +133,13 @@ pub struct AnimColor { impl AnimColor { pub fn new(origin: &str, destination: &str) -> Self { Self { - origin: Color::parse(origin).unwrap(), - destination: Color::parse(destination).unwrap(), + origin: Color::parse_value(origin).unwrap(), + destination: Color::parse_value(destination).unwrap(), time: Duration::default(), ease: Ease::default(), function: Function::default(), - value: Color::parse(origin).unwrap(), + value: Color::parse_value(origin).unwrap(), } } diff --git a/crates/hooks/tests/use_animation.rs b/crates/hooks/tests/use_animation.rs index 25be6d45d..2b559aa0e 100644 --- a/crates/hooks/tests/use_animation.rs +++ b/crates/hooks/tests/use_animation.rs @@ -168,7 +168,7 @@ pub async fn animate_color() { assert_eq!( utils.root().get(0).style().background, - Fill::Color(Color::parse("rgb(50, 100, 200)").unwrap()) + Fill::Color(Color::parse_value("rgb(50, 100, 200)").unwrap()) ); } diff --git a/crates/renderer/src/config.rs b/crates/renderer/src/config.rs index 05dc9d50f..bfccd095c 100644 --- a/crates/renderer/src/config.rs +++ b/crates/renderer/src/config.rs @@ -155,7 +155,7 @@ impl<'a, T: Clone> LaunchConfig<'a, T> { /// Specify the Window background color. pub fn with_background(mut self, background: &str) -> Self { - self.window_config.background = Color::parse(background).unwrap_or(Color::WHITE); + self.window_config.background = Color::parse_value(background).unwrap_or(Color::WHITE); self } diff --git a/crates/state/src/cursor.rs b/crates/state/src/cursor.rs index 0ad05b87a..93ff403f8 100644 --- a/crates/state/src/cursor.rs +++ b/crates/state/src/cursor.rs @@ -22,9 +22,11 @@ use crate::{ CursorReference, CustomAttributeValues, HighlightMode, + Lexer, Parse, ParseAttribute, ParseError, + Parser, }; #[derive(Clone, Debug, PartialEq, Component)] @@ -69,12 +71,16 @@ impl ParseAttribute for CursorState { } AttributeName::CursorColor => { if let Some(value) = attr.value.as_text() { - self.color = Color::parse(value)?; + let mut parser = Parser::new(Lexer::parse(value)); + + self.color = Color::parse(&mut parser)?; } } AttributeName::CursorMode => { if let Some(value) = attr.value.as_text() { - self.mode = CursorMode::parse(value)?; + let mut parser = Parser::new(Lexer::parse(value)); + + self.mode = CursorMode::parse(&mut parser)?; } } AttributeName::CursorId => { @@ -91,12 +97,16 @@ impl ParseAttribute for CursorState { } AttributeName::HighlightColor => { if let Some(value) = attr.value.as_text() { - self.highlight_color = Color::parse(value)?; + let mut parser = Parser::new(Lexer::parse(value)); + + self.highlight_color = Color::parse(&mut parser)?; } } AttributeName::HighlightMode => { if let Some(value) = attr.value.as_text() { - self.highlight_mode = HighlightMode::parse(value)?; + let mut parser = Parser::new(Lexer::parse(value)); + + self.highlight_mode = HighlightMode::parse(&mut parser)?; } } AttributeName::CursorReference => { diff --git a/crates/state/src/font_style.rs b/crates/state/src/font_style.rs index 7f529dfbf..5029ac92e 100644 --- a/crates/state/src/font_style.rs +++ b/crates/state/src/font_style.rs @@ -22,10 +22,13 @@ use torin::torin::Torin; use crate::{ CustomAttributeValues, - ExtSplit, + Lexer, Parse, ParseAttribute, + ParseError, + Parser, TextOverflow, + Token, }; #[derive(Debug, Clone, PartialEq, Component)] @@ -105,28 +108,36 @@ impl ParseAttribute for FontStyleState { fn parse_attribute( &mut self, attr: freya_native_core::prelude::OwnedAttributeView, - ) -> Result<(), crate::ParseError> { + ) -> Result<(), ParseError> { match attr.attribute { AttributeName::Color => { if let Some(value) = attr.value.as_text() { // Make an exception for the "inherit" as in this case we don't want to pass // a color at all but use the inherited one. if value != "inherit" { - self.color = Color::parse(value)?; + let mut parser = Parser::new(Lexer::parse(value)); + + self.color = Color::parse(&mut parser)?; } } } AttributeName::TextShadow => { if let Some(value) = attr.value.as_text() { - self.text_shadows = value - .split_excluding_group(',', '(', ')') - .map(|chunk| TextShadow::parse(chunk).unwrap_or_default()) - .collect(); + let mut parser = Parser::new(Lexer::parse(value)); + + let mut shadows = vec![TextShadow::parse(&mut parser)?]; + + while parser.try_consume(&Token::Comma) { + shadows.push(TextShadow::parse(&mut parser)?); + } + + self.text_shadows = shadows; } } AttributeName::FontFamily => { if let Some(value) = attr.value.as_text() { let families = value.split(','); + self.font_family = families .into_iter() .map(|f| f.trim().to_string()) @@ -149,84 +160,75 @@ impl ParseAttribute for FontStyleState { } AttributeName::TextAlign => { if let Some(value) = attr.value.as_text() { - if let Ok(text_align) = TextAlign::parse(value) { - self.text_align = text_align; - } + let mut parser = Parser::new(Lexer::parse(value)); + + self.text_align = TextAlign::parse(&mut parser)?; } } AttributeName::MaxLines => { if let Some(value) = attr.value.as_text() { - if let Ok(max_lines) = value.parse() { - self.max_lines = Some(max_lines); - } + self.max_lines = Some(value.parse().map_err(|_| ParseError)?); } } AttributeName::TextOverflow => { - let value = attr.value.as_text(); - if let Some(value) = value { - if let Ok(text_overflow) = TextOverflow::parse(value) { - self.text_overflow = text_overflow; - } + if let Some(value) = attr.value.as_text() { + let mut parser = Parser::new(Lexer::parse(value)); + + self.text_overflow = TextOverflow::parse(&mut parser)?; } } AttributeName::FontStyle => { if let Some(value) = attr.value.as_text() { - if let Ok(font_slant) = Slant::parse(value) { - self.font_slant = font_slant; - } + let mut parser = Parser::new(Lexer::parse(value)); + + self.font_slant = Slant::parse(&mut parser)?; } } AttributeName::FontWeight => { if let Some(value) = attr.value.as_text() { - if let Ok(font_weight) = Weight::parse(value) { - self.font_weight = font_weight; - } + let mut parser = Parser::new(Lexer::parse(value)); + + self.font_weight = Weight::parse(&mut parser)?; } } AttributeName::FontWidth => { if let Some(value) = attr.value.as_text() { - if let Ok(font_width) = Width::parse(value) { - self.font_width = font_width; - } + let mut parser = Parser::new(Lexer::parse(value)); + + self.font_width = Width::parse(&mut parser)?; } } AttributeName::Decoration => { if let Some(value) = attr.value.as_text() { - if let Ok(decoration) = TextDecoration::parse(value) { - self.decoration.ty = decoration; - } + let mut parser = Parser::new(Lexer::parse(value)); + + self.decoration.ty = TextDecoration::parse(&mut parser)?; } } AttributeName::DecorationStyle => { if let Some(value) = attr.value.as_text() { - if let Ok(style) = TextDecorationStyle::parse(value) { - self.decoration.style = style; - } + let mut parser = Parser::new(Lexer::parse(value)); + + self.decoration.style = TextDecorationStyle::parse(&mut parser)?; } } AttributeName::DecorationColor => { if let Some(value) = attr.value.as_text() { - if let Ok(new_decoration_color) = Color::parse(value) { - self.decoration.color = new_decoration_color; - } + let mut parser = Parser::new(Lexer::parse(value)); + + self.decoration.color = Color::parse(&mut parser)?; } else { self.decoration.color = self.color; } } AttributeName::WordSpacing => { - let value = attr.value.as_text(); - if let Some(value) = value { - if let Ok(word_spacing) = value.parse() { - self.word_spacing = word_spacing; - } + if let Some(value) = attr.value.as_text() { + self.word_spacing = value.parse().map_err(|_| ParseError)?; } } AttributeName::LetterSpacing => { - let value = attr.value.as_text(); - if let Some(value) = value { - if let Ok(letter_spacing) = value.parse() { - self.letter_spacing = letter_spacing; - } + if let Some(value) = attr.value.as_text() { + self.letter_spacing = value.parse().map_err(|_| ParseError)?; } } _ => {} diff --git a/crates/state/src/layout.rs b/crates/state/src/layout.rs index b8be67a31..b5f2c5728 100644 --- a/crates/state/src/layout.rs +++ b/crates/state/src/layout.rs @@ -23,10 +23,12 @@ use torin::prelude::*; use crate::{ CustomAttributeValues, + Lexer, NodeReference, Parse, ParseAttribute, ParseError, + Parser, }; #[derive(Default, Clone, Debug, Component, PartialEq)] @@ -58,49 +60,66 @@ impl ParseAttribute for LayoutState { match attr.attribute { AttributeName::Width => { if let Some(value) = attr.value.as_text() { - self.width = Size::parse(value)?; + let mut parser = Parser::new(Lexer::parse(value)); + + self.width = Size::parse(&mut parser)?; } } AttributeName::Height => { if let Some(value) = attr.value.as_text() { - self.height = Size::parse(value)?; + let mut parser = Parser::new(Lexer::parse(value)); + + self.height = Size::parse(&mut parser)?; } } AttributeName::MinHeight => { if let Some(value) = attr.value.as_text() { - self.minimum_height = Size::parse(value)?; + let mut parser = Parser::new(Lexer::parse(value)); + + self.minimum_height = Size::parse(&mut parser)?; } } AttributeName::MinWidth => { if let Some(value) = attr.value.as_text() { - self.minimum_width = Size::parse(value)?; + let mut parser = Parser::new(Lexer::parse(value)); + + self.minimum_width = Size::parse(&mut parser)?; } } AttributeName::MaxHeight => { if let Some(value) = attr.value.as_text() { - self.maximum_height = Size::parse(value)?; + let mut parser = Parser::new(Lexer::parse(value)); + + self.maximum_height = Size::parse(&mut parser)?; } } AttributeName::MaxWidth => { if let Some(value) = attr.value.as_text() { - self.maximum_width = Size::parse(value)?; + let mut parser = Parser::new(Lexer::parse(value)); + + self.maximum_width = Size::parse(&mut parser)?; } } AttributeName::Padding => { if let Some(value) = attr.value.as_text() { - self.padding = Gaps::parse(value)?; + let mut parser = Parser::new(Lexer::parse(value)); + + self.padding = Gaps::parse(&mut parser)?; } } AttributeName::Margin => { if let Some(value) = attr.value.as_text() { - self.margin = Gaps::parse(value)?; + let mut parser = Parser::new(Lexer::parse(value)); + + self.margin = Gaps::parse(&mut parser)?; } } AttributeName::Direction => { if let Some(value) = attr.value.as_text() { self.direction = match value { "horizontal" => DirectionMode::Horizontal, - _ => DirectionMode::Vertical, + "vertical" => DirectionMode::Vertical, + _ => return Err(ParseError), } } } @@ -116,18 +135,24 @@ impl ParseAttribute for LayoutState { } AttributeName::MainAlign => { if let Some(value) = attr.value.as_text() { - self.main_alignment = Alignment::parse(value)?; + let mut parser = Parser::new(Lexer::parse(value)); + + self.main_alignment = Alignment::parse(&mut parser)?; } } AttributeName::CrossAlign => { if let Some(value) = attr.value.as_text() { - self.cross_alignment = Alignment::parse(value)?; + let mut parser = Parser::new(Lexer::parse(value)); + + self.cross_alignment = Alignment::parse(&mut parser)?; } } AttributeName::Position => { if let Some(value) = attr.value.as_text() { if self.position.is_empty() { - self.position = Position::parse(value)?; + let mut parser = Parser::new(Lexer::parse(value)); + + self.position = Position::parse(&mut parser)?; } } } @@ -157,7 +182,9 @@ impl ParseAttribute for LayoutState { } AttributeName::Content => { if let Some(value) = attr.value.as_text() { - self.content = Content::parse(value)?; + let mut parser = Parser::new(Lexer::parse(value)); + + self.content = Content::parse(&mut parser)?; } } AttributeName::Reference => { diff --git a/crates/state/src/lexing.rs b/crates/state/src/lexing.rs new file mode 100644 index 000000000..e9b08bea3 --- /dev/null +++ b/crates/state/src/lexing.rs @@ -0,0 +1,178 @@ +use std::iter; + +#[derive(Debug, PartialEq, Clone)] +pub enum Token { + Ident(String), + Float(f32), + Number(i64), + ParenOpen, + ParenClose, + Minus, + Plus, + Slash, + Star, + Pound, + Percent, + Comma, + Unknown(char), +} + +impl Token { + pub fn ident>(value: T) -> Self { + Self::Ident(value.into()) + } + + pub fn is_ident(&self) -> bool { + matches!(self, Token::Ident(_)) + } + + pub fn is_float(&self) -> bool { + matches!(self, Token::Float(_)) + } + + pub fn is_number(&self) -> bool { + matches!(self, Token::Number(_)) + } + + pub fn is_integer(&self) -> bool { + matches!(self, Token::Number(_) | Token::Float(_)) + } + + pub fn into_string(self) -> String { + if let Token::Ident(value) = self { + value + } else { + unreachable!() + } + } + + pub fn into_float(self) -> f32 { + if let Token::Float(value) = self { + value + } else if let Token::Number(value) = self { + value as f32 + } else { + unreachable!() + } + } + + pub fn into_number(self) -> i64 { + if let Token::Number(value) = self { + value + } else { + unreachable!() + } + } + + pub fn as_str(&self) -> &str { + if let Token::Ident(value) = self { + value.as_str() + } else { + unreachable!() + } + } + + pub fn as_string(&self) -> Option<&str> { + if let Token::Ident(value) = self { + Some(value.as_str()) + } else { + None + } + } + + pub fn as_float(&self) -> Option { + if let Token::Float(value) = self { + Some(*value) + } else if let Token::Number(value) = self { + Some(*value as f32) + } else { + None + } + } + + pub fn as_number(&self) -> Option { + if let Token::Number(value) = self { + Some(*value) + } else { + None + } + } +} + +pub struct Lexer; + +impl Lexer { + pub fn parse>(data: T) -> Vec { + let mut tokens = vec![]; + let mut chars = data.as_ref().chars().peekable(); + + while let Some(character) = chars.next() { + match character { + ' ' => continue, + 'A'..='z' => { + tokens.push(Token::Ident( + iter::once(character) + .chain(iter::from_fn(|| { + chars + .by_ref() + .next_if(|s| s.is_ascii_alphanumeric() || s == &'-') + })) + .collect::() + .parse() + .unwrap(), + )); + } + '0'..='9' => { + let value = iter::once(character) + .chain(iter::from_fn(|| { + chars.by_ref().next_if(|s| s.is_ascii_digit() || s == &'.') + })) + .collect::(); + + if value.contains('.') { + tokens.push(Token::Float(value.parse().unwrap())); + } else { + tokens.push(Token::Number(value.parse().unwrap())); + } + } + '(' => tokens.push(Token::ParenOpen), + ')' => tokens.push(Token::ParenClose), + '+' => tokens.push(Token::Plus), + '-' => { + if chars.peek().is_some_and(char::is_ascii_digit) { + let value = iter::once(character) + .chain(iter::from_fn(|| { + chars.by_ref().next_if(|s| s.is_ascii_digit() || s == &'.') + })) + .collect::(); + + if value.contains('.') { + tokens.push(Token::Float(value.parse().unwrap())); + } else { + tokens.push(Token::Number(value.parse().unwrap())); + } + } else { + tokens.push(Token::Minus); + } + } + '*' => tokens.push(Token::Star), + '/' => tokens.push(Token::Slash), + '#' => { + tokens.push(Token::Pound); + + if chars.peek().is_some_and(char::is_ascii_alphanumeric) { + tokens.push(Token::Ident( + iter::from_fn(|| chars.by_ref().next_if(char::is_ascii_alphanumeric)) + .collect::(), + )); + } + } + '%' => tokens.push(Token::Percent), + ',' => tokens.push(Token::Comma), + character => tokens.push(Token::Unknown(character)), + } + } + + tokens + } +} diff --git a/crates/state/src/lib.rs b/crates/state/src/lib.rs index 4fae5a39a..1e713a84a 100644 --- a/crates/state/src/lib.rs +++ b/crates/state/src/lib.rs @@ -4,6 +4,7 @@ mod custom_attributes; mod font_style; mod layer; mod layout; +mod lexing; mod parsing; mod references; mod style; @@ -17,6 +18,7 @@ pub use custom_attributes::*; pub use font_style::*; pub use layer::*; pub use layout::*; +pub use lexing::*; pub use parsing::*; pub use references::*; pub use style::*; diff --git a/crates/state/src/parsing.rs b/crates/state/src/parsing.rs index 43d239c36..2d1ab97c7 100644 --- a/crates/state/src/parsing.rs +++ b/crates/state/src/parsing.rs @@ -1,15 +1,126 @@ -use std::str::CharIndices; +use std::{ + iter::Peekable, + str::CharIndices, + vec::IntoIter, +}; use freya_native_core::prelude::OwnedAttributeView; -use crate::CustomAttributeValues; +use crate::{ + CustomAttributeValues, + Lexer, + Token, +}; #[derive(Clone, Debug, PartialEq)] pub struct ParseError; +pub struct Parser { + tokens: Peekable>, +} + +impl Parser { + pub fn new(tokens: Vec) -> Self { + Self { + tokens: tokens.into_iter().peekable(), + } + } + + /// Consumes the current token only if it exists and is equal to `value`. + pub fn try_consume(&mut self, value: &Token) -> bool { + if self.peek().is_some_and(|v| v == value) { + self.next(); + + true + } else { + false + } + } + + /// Checks if the next token exists and it is equal to `value`. + pub fn check(&mut self, value: &Token) -> bool { + self.peek().is_some_and(|v| v == value) + } + + /// Returns the `bool` result of `func` if the next token exists. + pub fn check_if bool>(&mut self, func: F) -> bool { + self.peek().is_some_and(func) + } + + /// Consumes the current token if it exists and is equal to `value`, otherwise returning `ParseError`. + pub fn consume(&mut self, value: &Token) -> Result { + if self.check(value) { + Ok(self.next().unwrap()) + } else { + Err(ParseError) + } + } + + /// Consumes the current token if it exists and is equal to one of the values inside `values`, otherwise returning `ParseError`. + pub fn consume_one_of(&mut self, values: &[Token]) -> Result { + if self.check_if(|value| values.contains(value)) { + Ok(self.next().unwrap()) + } else { + Err(ParseError) + } + } + + /// Consumes the current token if it exists and the result of `func` is `true`, otherwise returning `ParseError`. + pub fn consume_if bool>(&mut self, func: F) -> Result { + if self.check_if(func) { + Ok(self.next().unwrap()) + } else { + Err(ParseError) + } + } + + /// Consumes the current token if it exists and the result of the `func` is `Some(T)`, otherwise returning `ParseError`. + pub fn consume_map Option>(&mut self, func: F) -> Result { + if let Some(value) = self.peek().and_then(func) { + self.next(); + + Ok(value) + } else { + Err(ParseError) + } + } + + /// Consumes the current token and returns it wrapped in `Some` if it exists, otherwise returning `None`. + #[allow(clippy::should_implement_trait)] + pub fn next(&mut self) -> Option { + self.tokens.next() + } + + /// Peeks the current token and returns a reference to it wrapped in `Some` if it exists, otherwise returning `None`. + pub fn peek(&mut self) -> Option<&Token> { + self.tokens.peek() + } + + /// Consumes the current token and returns it wrapped in `Some` if the result of the `func` function is `true`, otherwise returning `None`. + pub fn next_if bool>(&mut self, func: F) -> Option { + if self.check_if(func) { + self.next() + } else { + None + } + } +} + // FromStr but we own it so we can impl it on torin and skia_safe types. pub trait Parse: Sized { - fn parse(value: &str) -> Result; + fn parse(parser: &mut Parser) -> Result; + + fn parse_value(value: &str) -> Result { + let mut parser = Parser::new(Lexer::parse(value)); + + let value = Self::parse(&mut parser); + + if parser.tokens.len() > 0 { + Err(ParseError) + } else { + value + } + } } pub trait ParseAttribute: Sized { @@ -22,10 +133,11 @@ pub trait ParseAttribute: Sized { #[cfg(debug_assertions)] { let error_attr = attr.clone(); + if self.parse_attribute(attr).is_err() { panic!( "Failed to parse attribute '{:?}' with value '{:?}'", - error_attr.attribute, error_attr.value + error_attr.attribute, error_attr.value, ); } } diff --git a/crates/state/src/style.rs b/crates/state/src/style.rs index 8b142059c..d44988940 100644 --- a/crates/state/src/style.rs +++ b/crates/state/src/style.rs @@ -14,18 +14,20 @@ use freya_native_core::{ use freya_native_core_macro::partial_derive_state; use crate::{ - parsing::ExtSplit, AttributesBytes, Border, BorderAlignment, CornerRadius, CustomAttributeValues, Fill, + Lexer, OverflowMode, Parse, ParseAttribute, ParseError, + Parser, Shadow, + Token, }; #[derive(Default, Debug, Clone, PartialEq, Component)] @@ -51,45 +53,63 @@ impl ParseAttribute for StyleState { if value == "none" { return Ok(()); } - self.background = Fill::parse(value)?; + + let mut parser = Parser::new(Lexer::parse(value)); + + self.background = Fill::parse(&mut parser)?; } } AttributeName::Border => { if let Some(value) = attr.value.as_text() { - let mut border = Border::parse(value)?; + let mut parser = Parser::new(Lexer::parse(value)); + + let mut border = Border::parse(&mut parser)?; + border.alignment = self.border.alignment; + self.border = border; } } AttributeName::BorderAlign => { if let Some(value) = attr.value.as_text() { - self.border.alignment = BorderAlignment::parse(value)?; + let mut parser = Parser::new(Lexer::parse(value)); + + self.border.alignment = BorderAlignment::parse(&mut parser)?; } } AttributeName::Shadow => { if let Some(value) = attr.value.as_text() { - self.shadows = value - .split_excluding_group(',', '(', ')') - .map(|chunk| Shadow::parse(chunk).unwrap_or_default()) - .collect(); + let mut parser = Parser::new(Lexer::parse(value)); + + let mut shadows = vec![Shadow::parse(&mut parser)?]; + + while parser.try_consume(&Token::Comma) { + shadows.push(Shadow::parse(&mut parser)?); + } + + self.shadows = shadows; } } AttributeName::CornerRadius => { if let Some(value) = attr.value.as_text() { - let mut radius = CornerRadius::parse(value)?; + let mut parser = Parser::new(Lexer::parse(value)); + + let mut radius = CornerRadius::parse(&mut parser)?; + radius.smoothing = self.corner_radius.smoothing; + self.corner_radius = radius; } } AttributeName::CornerSmoothing => { if let Some(value) = attr.value.as_text() { - if value.ends_with('%') { - let smoothing = value - .replacen('%', "", 1) - .parse::() - .map_err(|_| ParseError)?; - self.corner_radius.smoothing = (smoothing / 100.0).clamp(0.0, 1.0); - } + let mut parser = Parser::new(Lexer::parse(value)); + + let smoothing = parser.consume_map(Token::as_float)?; + + parser.consume(&Token::Percent)?; + + self.corner_radius.smoothing = (smoothing / 100.0).clamp(0.0, 1.0); } } AttributeName::ImageData => { @@ -111,7 +131,9 @@ impl ParseAttribute for StyleState { } AttributeName::Overflow => { if let Some(value) = attr.value.as_text() { - self.overflow = OverflowMode::parse(value)?; + let mut parser = Parser::new(Lexer::parse(value)); + + self.overflow = OverflowMode::parse(&mut parser)?; } } AttributeName::Opacity => { diff --git a/crates/state/src/values/alignment.rs b/crates/state/src/values/alignment.rs index 93e4d5820..135c91673 100644 --- a/crates/state/src/values/alignment.rs +++ b/crates/state/src/values/alignment.rs @@ -3,17 +3,21 @@ use torin::alignment::Alignment; use crate::{ Parse, ParseError, + Parser, }; impl Parse for Alignment { - fn parse(value: &str) -> Result { - Ok(match value { - "center" => Alignment::Center, - "end" => Alignment::End, - "space-between" => Alignment::SpaceBetween, - "space-evenly" => Alignment::SpaceEvenly, - "space-around" => Alignment::SpaceAround, - _ => Alignment::Start, + fn parse(parser: &mut Parser) -> Result { + parser.consume_map(|value| { + value.as_string().and_then(|value| match value { + "start" => Some(Self::Start), + "center" => Some(Self::Center), + "end" => Some(Self::End), + "space-between" => Some(Self::SpaceBetween), + "space-evenly" => Some(Self::SpaceEvenly), + "space-around" => Some(Self::SpaceAround), + _ => None, + }) }) } } diff --git a/crates/state/src/values/border.rs b/crates/state/src/values/border.rs index b0b50ef57..dd8609186 100644 --- a/crates/state/src/values/border.rs +++ b/crates/state/src/values/border.rs @@ -6,6 +6,8 @@ use crate::{ Fill, Parse, ParseError, + Parser, + Token, }; #[derive(Default, Clone, Copy, Debug, PartialEq)] @@ -32,12 +34,14 @@ pub enum BorderAlignment { } impl Parse for BorderAlignment { - fn parse(value: &str) -> Result { - Ok(match value { - "inner" => BorderAlignment::Inner, - "outer" => BorderAlignment::Outer, - "center" => BorderAlignment::Center, - _ => BorderAlignment::default(), + fn parse(parser: &mut Parser) -> Result { + parser.consume_map(|value| { + value.as_string().and_then(|value| match value { + "inner" => Some(BorderAlignment::Inner), + "outer" => Some(BorderAlignment::Outer), + "center" => Some(BorderAlignment::Center), + _ => None, + }) }) } } @@ -62,25 +66,21 @@ impl fmt::Display for BorderStyle { } impl Parse for Border { - fn parse(value: &str) -> Result { - if value == "none" { + fn parse(parser: &mut Parser) -> Result { + if parser.try_consume(&Token::ident("none")) { return Ok(Self::default()); } - let mut border_values = value.split_ascii_whitespace(); - Ok(Border { - width: border_values - .next() - .ok_or(ParseError)? - .parse::() - .map_err(|_| ParseError)?, - style: match border_values.next().ok_or(ParseError)? { - "solid" => BorderStyle::Solid, - _ => BorderStyle::None, - }, - fill: Fill::parse(&border_values.collect::>().join(" ")) - .map_err(|_| ParseError)?, + width: parser.consume_map(Token::as_float)?, + style: parser.consume_map(|value| { + value.as_string().and_then(|value| match value { + "none" => Some(BorderStyle::None), + "solid" => Some(BorderStyle::Solid), + _ => None, + }) + })?, + fill: Fill::parse(parser)?, alignment: BorderAlignment::default(), }) } diff --git a/crates/state/src/values/color.rs b/crates/state/src/values/color.rs index a04fe748f..6135ed9f0 100644 --- a/crates/state/src/values/color.rs +++ b/crates/state/src/values/color.rs @@ -5,6 +5,8 @@ use freya_engine::prelude::*; use crate::{ Parse, ParseError, + Parser, + Token, }; pub trait DisplayColor { @@ -13,29 +15,40 @@ pub trait DisplayColor { } impl Parse for Color { - fn parse(value: &str) -> Result { - match value { - "red" => Ok(Color::RED), - "green" => Ok(Color::GREEN), - "blue" => Ok(Color::BLUE), - "yellow" => Ok(Color::YELLOW), - "black" => Ok(Color::BLACK), - "gray" => Ok(Color::GRAY), - "white" => Ok(Color::WHITE), - "orange" => Ok(Color::from_rgb(255, 165, 0)), - "transparent" => Ok(Color::TRANSPARENT), - _ => { - if value.starts_with("hsl(") { - parse_hsl(value) - } else if value.starts_with("rgb(") { - parse_rgb(value) - } else if value.starts_with('#') { - parse_hex_color(value) - } else { - Err(ParseError) - } - } - } + fn parse(parser: &mut Parser) -> Result { + parser + .consume_one_of(&[ + Token::Pound, + Token::ident("rgb"), + Token::ident("hsl"), + Token::ident("red"), + Token::ident("green"), + Token::ident("blue"), + Token::ident("yellow"), + Token::ident("black"), + Token::ident("gray"), + Token::ident("white"), + Token::ident("orange"), + Token::ident("transparent"), + ]) + .and_then(|token| match token { + Token::Pound => parse_hex_color(parser), + Token::Ident(ref value) => match value.as_str() { + "rgb" => parse_rgb(parser), + "hsl" => parse_hsl(parser), + "red" => Ok(Color::RED), + "green" => Ok(Color::GREEN), + "blue" => Ok(Color::BLUE), + "yellow" => Ok(Color::YELLOW), + "black" => Ok(Color::BLACK), + "gray" => Ok(Color::GRAY), + "white" => Ok(Color::WHITE), + "orange" => Ok(Color::from_rgb(255, 165, 0)), + "transparent" => Ok(Color::TRANSPARENT), + _ => unreachable!(), + }, + _ => unreachable!(), + }) } } @@ -72,127 +85,129 @@ impl DisplayColor for Color { } } -fn parse_rgb(color: &str) -> Result { - if !color.ends_with(')') { - return Err(ParseError); - } +fn parse_number_as>(token: &Token) -> Option { + token.as_number().and_then(|value| T::try_from(value).ok()) +} - let color = color.replacen("rgb(", "", 1).replacen(')', "", 1); - - let mut colors = color.split(','); - - let r = colors - .next() - .ok_or(ParseError)? - .trim() - .parse::() - .map_err(|_| ParseError)?; - let g = colors - .next() - .ok_or(ParseError)? - .trim() - .parse::() - .map_err(|_| ParseError)?; - let b = colors - .next() - .ok_or(ParseError)? - .trim() - .parse::() - .map_err(|_| ParseError)?; - let a: Option<&str> = colors.next(); - - // There should not be more than 4 components. - if colors.next().is_some() { - return Err(ParseError); - } +fn parse_rgb(parser: &mut Parser) -> Result { + parser.consume(&Token::ParenOpen)?; + + let red = parser.consume_map(parse_number_as)?; + + parser.consume(&Token::Comma)?; + + let green = parser.consume_map(parse_number_as)?; + + parser.consume(&Token::Comma)?; + + let blue = parser.consume_map(parse_number_as)?; + + let color = if parser.try_consume(&Token::Comma) { + let alpha = parser.consume_if(Token::is_integer).and_then(|token| { + if token.is_number() { + u8::try_from(token.into_number()).map_err(|_| ParseError) + } else { + Ok((token.into_float() * 255.0).round().clamp(0.0, 255.0) as u8) + } + })?; - if let Some(a) = a { - let alpha_trimmed = a.trim(); - if let Ok(u8_alpha) = alpha_trimmed.parse::() { - Ok(Color::from_argb(u8_alpha, r, g, b)) - } else if let Ok(f32_alpha) = alpha_trimmed.parse::() { - let a = (255.0 * f32_alpha).clamp(0.0, 255.0).round() as u8; - Ok(Color::from_argb(a, r, g, b)) + Color::from_argb(alpha, red, green, blue) + } else { + Color::from_rgb(red, green, blue) + }; + + parser.consume(&Token::ParenClose)?; + + Ok(color) +} + +fn parse_hsl(parser: &mut Parser) -> Result { + parser.consume(&Token::ParenOpen)?; + + let h = parser.consume_map(Token::as_number).and_then(|value| { + if (0..=360).contains(&value) { + Ok(value as f32) } else { Err(ParseError) } - } else { - Ok(Color::from_rgb(r, g, b)) - } -} + })?; -fn parse_hsl(color: &str) -> Result { - if !color.ends_with(')') { - return Err(ParseError); - } + parser.consume(&Token::ident("deg"))?; + parser.consume(&Token::Comma)?; - let color = color.replacen("hsl(", "", 1).replacen(')', "", 1); - let mut colors = color.split(','); - - // Get each color component as a string - let h_str = colors.next().ok_or(ParseError)?.trim(); - let s_str = colors.next().ok_or(ParseError)?.trim(); - let l_str = colors.next().ok_or(ParseError)?.trim(); - let a_str: Option<&str> = colors.next(); - - // Ensure correct units and lengths. - if colors.next().is_some() - || !h_str.ends_with("deg") - || !s_str.ends_with('%') - || !l_str.ends_with('%') - { - return Err(ParseError); - } + let mut s = parser.consume_map(Token::as_number).and_then(|value| { + if (0..=100).contains(&value) { + Ok((value as f32) / 100.0) + } else { + Err(ParseError) + } + })?; + + parser.consume(&Token::Percent)?; + parser.consume(&Token::Comma)?; + + let mut l = parser.consume_map(Token::as_number).and_then(|value| { + if (0..=100).contains(&value) { + Ok((value as f32) / 100.0) + } else { + Err(ParseError) + } + })?; + + parser.consume(&Token::Percent)?; + + let a = if parser.consume(&Token::Comma).is_ok() { + let value = parser.consume_map(Token::as_number).and_then(|value| { + if (0..=100).contains(&value) { + Ok((value as f32) / 100.0) + } else { + Err(ParseError) + } + })?; + + parser.consume(&Token::Percent)?; + + Some(value) + } else { + None + }; - // S, L and A can end in percentage, otherwise its 0.0 - 1.0 - let h = h_str - .replacen("deg", "", 1) - .parse::() - .map_err(|_| ParseError)?; - let mut s = s_str - .replacen('%', "", 1) - .parse::() - .map_err(|_| ParseError)? - / 100.0; - let mut l = l_str - .replacen('%', "", 1) - .parse::() - .map_err(|_| ParseError)? - / 100.0; + parser.consume(&Token::ParenClose)?; // HSL to HSV Conversion l *= 2.0; s *= if l <= 1.0 { l } else { 2.0 - l }; + let v = (l + s) / 2.0; + s = (2.0 * s) / (l + s); + let hsv = HSV::from((h, s, v)); // Handle alpha formatting and convert to ARGB - if let Some(a_str) = a_str { - if !s_str.ends_with('%') { - return Err(ParseError); - } + Ok(a.map(|a| hsv.to_color((a * 255.0).round() as u8)) + .unwrap_or_else(|| hsv.to_color(255))) +} - let a = a_str - .trim() - .replace('%', "") - .parse::() - .map_err(|_| ParseError)? - / 100.0; +fn parse_hex_color(parser: &mut Parser) -> Result { + let hex = parser.consume_if(Token::is_ident).map(Token::into_string)?; - Ok(hsv.to_color((a * 255.0).round() as u8)) - } else { - Ok(hsv.to_color(255)) + if ![6, 8].contains(&hex.len()) { + return Err(ParseError); } -} -fn parse_hex_color(color: &str) -> Result { - if color.len() == 7 { - let r = u8::from_str_radix(&color[1..3], 16).map_err(|_| ParseError)?; - let g = u8::from_str_radix(&color[3..5], 16).map_err(|_| ParseError)?; - let b = u8::from_str_radix(&color[5..7], 16).map_err(|_| ParseError)?; - Ok(Color::from_rgb(r, g, b)) + let value = i64::from_str_radix(&hex, 16).map_err(|_| ParseError)?; + + let a = if hex.len() == 8 { + Some(u8::try_from((value >> 24) & 0xFF).map_err(|_| ParseError)?) } else { - Err(ParseError) - } + None + }; + + let r = u8::try_from((value >> 16) & 0xFF).map_err(|_| ParseError)?; + let g = u8::try_from((value >> 8) & 0xFF).map_err(|_| ParseError)?; + let b = u8::try_from(value & 0xFF).map_err(|_| ParseError)?; + + Ok(a.map(|a| Color::from_argb(a, r, g, b)) + .unwrap_or_else(|| Color::from_rgb(r, g, b))) } diff --git a/crates/state/src/values/content.rs b/crates/state/src/values/content.rs index 7cd26f0a1..ab77434cd 100644 --- a/crates/state/src/values/content.rs +++ b/crates/state/src/values/content.rs @@ -3,13 +3,17 @@ use torin::content::Content; use crate::{ Parse, ParseError, + Parser, }; impl Parse for Content { - fn parse(value: &str) -> Result { - Ok(match value { - "fit" => Content::Fit, - _ => Content::Normal, + fn parse(parser: &mut Parser) -> Result { + parser.consume_map(|value| { + value.as_string().and_then(|value| match value { + "normal" => Some(Self::Normal), + "fit" => Some(Self::Fit), + _ => None, + }) }) } } diff --git a/crates/state/src/values/corner_radius.rs b/crates/state/src/values/corner_radius.rs index 004a6afab..7768fd566 100644 --- a/crates/state/src/values/corner_radius.rs +++ b/crates/state/src/values/corner_radius.rs @@ -9,6 +9,8 @@ use torin::scaled::Scaled; use crate::{ Parse, ParseError, + Parser, + Token, }; #[derive(PartialEq, Clone, Debug, Default, Copy)] @@ -199,64 +201,34 @@ impl CornerRadius { } impl Parse for CornerRadius { - fn parse(value: &str) -> Result { + fn parse(parser: &mut Parser) -> Result { let mut radius = CornerRadius::default(); - let mut values = value.split_ascii_whitespace(); - match values.clone().count() { + match ( + parser.consume_map(Token::as_float)?, + parser.consume_map(Token::as_float).ok(), + parser.consume_map(Token::as_float).ok(), + parser.consume_map(Token::as_float).ok(), + ) { // Same in all corners - 1 => { - radius.fill_all( - values - .next() - .ok_or(ParseError)? - .parse::() - .map_err(|_| ParseError)?, - ); + (value, None, None, None) => { + radius.fill_all(value); } // By Top and Bottom - 2 => { + (top, Some(bottom), None, None) => { // Top - radius.fill_top( - values - .next() - .ok_or(ParseError)? - .parse::() - .map_err(|_| ParseError)?, - ); + radius.fill_top(top); // Bottom - radius.fill_bottom( - values - .next() - .ok_or(ParseError)? - .parse::() - .map_err(|_| ParseError)?, - ) + radius.fill_bottom(bottom) } // Each corner - 4 => { + (top_left, Some(top_right), Some(bottom_left), Some(bottom_right)) => { radius = CornerRadius { - top_left: values - .next() - .ok_or(ParseError)? - .parse::() - .map_err(|_| ParseError)?, - top_right: values - .next() - .ok_or(ParseError)? - .parse::() - .map_err(|_| ParseError)?, - bottom_left: values - .next() - .ok_or(ParseError)? - .parse::() - .map_err(|_| ParseError)?, - bottom_right: values - .next() - .ok_or(ParseError)? - .parse::() - .map_err(|_| ParseError)?, + top_left, + top_right, + bottom_left, + bottom_right, ..Default::default() } } diff --git a/crates/state/src/values/cursor.rs b/crates/state/src/values/cursor.rs index f91a45ee7..c4ee4d83f 100644 --- a/crates/state/src/values/cursor.rs +++ b/crates/state/src/values/cursor.rs @@ -3,6 +3,7 @@ use std::fmt; use crate::{ Parse, ParseError, + Parser, }; #[derive(Clone, Debug, PartialEq, Eq)] @@ -12,10 +13,13 @@ pub enum CursorMode { } impl Parse for CursorMode { - fn parse(value: &str) -> Result { - Ok(match value { - "editable" => CursorMode::Editable, - _ => CursorMode::None, + fn parse(parser: &mut Parser) -> Result { + parser.consume_map(|value| { + value.as_string().and_then(|value| match value { + "none" => Some(Self::None), + "editable" => Some(Self::Editable), + _ => None, + }) }) } } diff --git a/crates/state/src/values/decoration.rs b/crates/state/src/values/decoration.rs index b049bdba3..dfba0c265 100644 --- a/crates/state/src/values/decoration.rs +++ b/crates/state/src/values/decoration.rs @@ -3,23 +3,23 @@ use freya_engine::prelude::*; use crate::{ Parse, ParseError, + Parser, }; impl Parse for TextDecoration { - fn parse(value: &str) -> Result { - let mut decoration = TextDecoration::default(); - let values = value.split_ascii_whitespace(); + fn parse(parser: &mut Parser) -> Result { + let mut decoration = Self::default(); - for val in values { - decoration.set( - match val { - "underline" => TextDecoration::UNDERLINE, - "overline" => TextDecoration::OVERLINE, - "line-through" => TextDecoration::LINE_THROUGH, - _ => TextDecoration::NO_DECORATION, - }, - true, - ); + while let Ok(value) = parser.consume_map(|token| { + token.as_string().and_then(|value| match value { + "none" => Some(Self::NO_DECORATION), + "underline" => Some(Self::UNDERLINE), + "overline" => Some(Self::OVERLINE), + "line-through" => Some(Self::LINE_THROUGH), + _ => None, + }) + }) { + decoration.set(value, true); } Ok(decoration) @@ -27,14 +27,16 @@ impl Parse for TextDecoration { } impl Parse for TextDecorationStyle { - fn parse(value: &str) -> Result { - Ok(match value { - "solid" => TextDecorationStyle::Solid, - "double" => TextDecorationStyle::Double, - "dotted" => TextDecorationStyle::Dotted, - "dashed" => TextDecorationStyle::Dashed, - "wavy" => TextDecorationStyle::Wavy, - _ => TextDecorationStyle::Solid, + fn parse(parser: &mut Parser) -> Result { + parser.consume_map(|token| { + token.as_string().and_then(|value| match value { + "solid" => Some(Self::Solid), + "double" => Some(Self::Double), + "dotted" => Some(Self::Dotted), + "dashed" => Some(Self::Dashed), + "wavy" => Some(Self::Wavy), + _ => None, + }) }) } } diff --git a/crates/state/src/values/fill.rs b/crates/state/src/values/fill.rs index db31befd9..2275ddd59 100644 --- a/crates/state/src/values/fill.rs +++ b/crates/state/src/values/fill.rs @@ -7,6 +7,7 @@ use crate::{ LinearGradient, Parse, ParseError, + Parser, }; #[derive(Clone, Debug, PartialEq)] @@ -30,12 +31,10 @@ impl From for Fill { } impl Parse for Fill { - fn parse(value: &str) -> Result { - Ok(if value.starts_with("linear-gradient(") { - Self::LinearGradient(LinearGradient::parse(value).map_err(|_| ParseError)?) - } else { - Self::Color(Color::parse(value).map_err(|_| ParseError)?) - }) + fn parse(parser: &mut Parser) -> Result { + LinearGradient::parse(parser) + .map(Self::LinearGradient) + .or(Color::parse(parser).map(Self::Color)) } } diff --git a/crates/state/src/values/font.rs b/crates/state/src/values/font.rs index 673e08732..8e5f77a3b 100644 --- a/crates/state/src/values/font.rs +++ b/crates/state/src/values/font.rs @@ -3,46 +3,53 @@ use freya_engine::prelude::*; use crate::{ Parse, ParseError, + Parser, }; impl Parse for TextAlign { - fn parse(value: &str) -> Result { - Ok(match value { - "center" => TextAlign::Center, - "justify" => TextAlign::Justify, - "start" => TextAlign::Start, - "end" => TextAlign::End, - "left" => TextAlign::Left, - "right" => TextAlign::Right, - _ => TextAlign::default(), + fn parse(parser: &mut Parser) -> Result { + parser.consume_map(|token| { + token.as_string().and_then(|value| match value { + "center" => Some(TextAlign::Center), + "justify" => Some(TextAlign::Justify), + "start" => Some(TextAlign::Start), + "end" => Some(TextAlign::End), + "left" => Some(TextAlign::Left), + "right" => Some(TextAlign::Right), + _ => None, + }) }) } } impl Parse for Slant { - fn parse(value: &str) -> Result { - Ok(match value { - "upright" => Slant::Upright, - "italic" => Slant::Italic, - "oblique" => Slant::Oblique, - _ => Slant::Upright, + fn parse(parser: &mut Parser) -> Result { + parser.consume_map(|token| { + token.as_string().and_then(|value| match value { + "upright" => Some(Slant::Upright), + "italic" => Some(Slant::Italic), + "oblique" => Some(Slant::Oblique), + _ => None, + }) }) } } impl Parse for Width { - fn parse(value: &str) -> Result { - Ok(match value { - "ultra-condensed" => Width::ULTRA_CONDENSED, - "extra-condensed" => Width::EXTRA_CONDENSED, - "condensed" => Width::CONDENSED, - "semi-condensed" => Width::SEMI_CONDENSED, - "normal" => Width::NORMAL, - "semi-expanded" => Width::SEMI_EXPANDED, - "expanded" => Width::EXPANDED, - "extra-expanded" => Width::EXTRA_EXPANDED, - "ultra-expanded" => Width::ULTRA_EXPANDED, - _ => Width::NORMAL, + fn parse(parser: &mut Parser) -> Result { + parser.consume_map(|token| { + token.as_string().and_then(|value| match value { + "ultra-condensed" => Some(Width::ULTRA_CONDENSED), + "extra-condensed" => Some(Width::EXTRA_CONDENSED), + "condensed" => Some(Width::CONDENSED), + "semi-condensed" => Some(Width::SEMI_CONDENSED), + "normal" => Some(Width::NORMAL), + "semi-expanded" => Some(Width::SEMI_EXPANDED), + "expanded" => Some(Width::EXPANDED), + "extra-expanded" => Some(Width::EXTRA_EXPANDED), + "ultra-expanded" => Some(Width::ULTRA_EXPANDED), + _ => None, + }) }) } } @@ -53,31 +60,39 @@ impl Parse for Weight { // CSS has one deviation from this spec, which uses the value "950" for extra_black. // skia_safe also has an "invisible" weight smaller than the thin weight, which could fall under CSS's interpretation of OpenType's // version. In this case it would be font_weight: "50". - fn parse(value: &str) -> Result { - Ok(match value { - "invisible" => Weight::INVISIBLE, - "thin" => Weight::THIN, - "extra-light" => Weight::EXTRA_LIGHT, - "light" => Weight::LIGHT, - "normal" => Weight::NORMAL, - "medium" => Weight::MEDIUM, - "semi-bold" => Weight::SEMI_BOLD, - "bold" => Weight::BOLD, - "extra-bold" => Weight::EXTRA_BOLD, - "black" => Weight::BLACK, - "extra-black" => Weight::EXTRA_BLACK, - "50" => Weight::INVISIBLE, - "100" => Weight::THIN, - "200" => Weight::EXTRA_LIGHT, - "300" => Weight::LIGHT, - "400" => Weight::NORMAL, - "500" => Weight::MEDIUM, - "600" => Weight::SEMI_BOLD, - "700" => Weight::BOLD, - "800" => Weight::EXTRA_BOLD, - "900" => Weight::BLACK, - "950" => Weight::EXTRA_BLACK, - _ => Weight::NORMAL, + fn parse(parser: &mut Parser) -> Result { + parser.consume_map(|token| { + if token.is_number() { + token.as_number().and_then(|value| match value { + 50 => Some(Weight::INVISIBLE), + 100 => Some(Weight::THIN), + 200 => Some(Weight::EXTRA_LIGHT), + 300 => Some(Weight::LIGHT), + 400 => Some(Weight::NORMAL), + 500 => Some(Weight::MEDIUM), + 600 => Some(Weight::SEMI_BOLD), + 700 => Some(Weight::BOLD), + 800 => Some(Weight::EXTRA_BOLD), + 900 => Some(Weight::BLACK), + 950 => Some(Weight::EXTRA_BLACK), + _ => None, + }) + } else { + token.as_string().and_then(|value| match value { + "invisible" => Some(Weight::INVISIBLE), + "thin" => Some(Weight::THIN), + "extra-light" => Some(Weight::EXTRA_LIGHT), + "light" => Some(Weight::LIGHT), + "normal" => Some(Weight::NORMAL), + "medium" => Some(Weight::MEDIUM), + "semi-bold" => Some(Weight::SEMI_BOLD), + "bold" => Some(Weight::BOLD), + "extra-bold" => Some(Weight::EXTRA_BOLD), + "black" => Some(Weight::BLACK), + "extra-black" => Some(Weight::EXTRA_BLACK), + _ => None, + }) + } }) } } @@ -109,11 +124,13 @@ impl TextOverflow { } impl Parse for TextOverflow { - fn parse(value: &str) -> Result { - Ok(match value { - "ellipsis" => TextOverflow::Ellipsis, - "clip" => TextOverflow::Clip, - value => TextOverflow::Custom(value.to_string()), + fn parse(parser: &mut Parser) -> Result { + parser.consume_map(|token| { + token.as_string().map(|value| match value { + "ellipsis" => TextOverflow::Ellipsis, + "clip" => TextOverflow::Clip, + value => TextOverflow::Custom(value.to_string()), + }) }) } } diff --git a/crates/state/src/values/gaps.rs b/crates/state/src/values/gaps.rs index 70a61be21..2a9991efd 100644 --- a/crates/state/src/values/gaps.rs +++ b/crates/state/src/values/gaps.rs @@ -3,92 +3,46 @@ use torin::gaps::Gaps; use crate::{ Parse, ParseError, + Parser, + Token, }; impl Parse for Gaps { - fn parse(value: &str) -> Result { + fn parse(parser: &mut Parser) -> Result { let mut paddings = Gaps::default(); - if value == "none" { + let value = + parser.consume_if(|token| token == &Token::ident("none") || token.is_integer())?; + + if value == Token::ident("none") { return Ok(paddings); } - let mut values = value.split_ascii_whitespace(); - - match values.clone().count() { + match ( + value.into_float(), + parser.consume_map(Token::as_float).ok(), + parser.consume_map(Token::as_float).ok(), + parser.consume_map(Token::as_float).ok(), + ) { // Same in each directions - 1 => { - paddings.fill_all( - values - .next() - .ok_or(ParseError)? - .parse::() - .map_err(|_| ParseError)?, - ); + (value, None, None, None) => { + paddings.fill_all(value); } // By vertical and horizontal - 2 => { + (vertical, Some(horizontal), None, None) => { // Vertical - paddings.fill_vertical( - values - .next() - .ok_or(ParseError)? - .parse::() - .map_err(|_| ParseError)?, - ); + paddings.fill_vertical(vertical); // Horizontal - paddings.fill_horizontal( - values - .next() - .ok_or(ParseError)? - .parse::() - .map_err(|_| ParseError)?, - ) + paddings.fill_horizontal(horizontal); } // Individual vertical but same horizontal - 3 => { - let top = values - .next() - .ok_or(ParseError)? - .parse::() - .map_err(|_| ParseError)?; - let left_and_right = values - .next() - .ok_or(ParseError)? - .parse::() - .map_err(|_| ParseError)?; - let bottom = values - .next() - .ok_or(ParseError)? - .parse::() - .map_err(|_| ParseError)?; + (top, Some(left_and_right), Some(bottom), None) => { paddings = Gaps::new(top, left_and_right, bottom, left_and_right); } // Each directions - 4 => { - paddings = Gaps::new( - values - .next() - .ok_or(ParseError)? - .parse::() - .map_err(|_| ParseError)?, - values - .next() - .ok_or(ParseError)? - .parse::() - .map_err(|_| ParseError)?, - values - .next() - .ok_or(ParseError)? - .parse::() - .map_err(|_| ParseError)?, - values - .next() - .ok_or(ParseError)? - .parse::() - .map_err(|_| ParseError)?, - ); + (top, Some(right), Some(bottom), Some(left)) => { + paddings = Gaps::new(top, right, bottom, left); } _ => {} } diff --git a/crates/state/src/values/gradient.rs b/crates/state/src/values/gradient.rs index e5011d7f4..ab66dfc47 100644 --- a/crates/state/src/values/gradient.rs +++ b/crates/state/src/values/gradient.rs @@ -8,9 +8,10 @@ use torin::{ use crate::{ DisplayColor, - ExtSplit, Parse, ParseError, + Parser, + Token, }; #[derive(Clone, Debug, Default, PartialEq)] @@ -20,25 +21,13 @@ pub struct GradientStop { } impl Parse for GradientStop { - fn parse(value: &str) -> Result { - let mut split = value.split_ascii_whitespace_excluding_group('(', ')'); - let color_str = split.next().ok_or(ParseError)?; + fn parse(parser: &mut Parser) -> Result { + let color = Color::parse(parser)?; + let offset = (parser.consume_map(Token::as_float)? / 100.0).clamp(0.0, 1.0); - let offset_str = split.next().ok_or(ParseError)?.trim(); - if !offset_str.ends_with('%') || split.next().is_some() { - return Err(ParseError); - } - - let offset = offset_str - .replacen('%', "", 1) - .parse::() - .map_err(|_| ParseError)? - / 100.0; + parser.consume(&Token::Percent)?; - Ok(GradientStop { - color: Color::parse(color_str).map_err(|_| ParseError)?, - offset, - }) + Ok(GradientStop { color, offset }) } } @@ -85,35 +74,32 @@ impl LinearGradient { } impl Parse for LinearGradient { - fn parse(value: &str) -> Result { - if !value.starts_with("linear-gradient(") || !value.ends_with(')') { - return Err(ParseError); - } - - let mut gradient = LinearGradient::default(); - let mut value = value.replacen("linear-gradient(", "", 1); - value.remove(value.rfind(')').ok_or(ParseError)?); - - let mut split = value.split_excluding_group(',', '(', ')'); - - let angle_or_first_stop = split.next().ok_or(ParseError)?.trim(); - - if angle_or_first_stop.ends_with("deg") { - if let Ok(angle) = angle_or_first_stop.replacen("deg", "", 1).parse::() { - gradient.angle = angle.to_radians(); + fn parse(parser: &mut Parser) -> Result { + parser.consume(&Token::ident("linear-gradient"))?; + parser.consume(&Token::ParenOpen)?; + + let mut gradient = LinearGradient { + angle: if let Some(angle) = parser.next_if(Token::is_number).map(Token::into_float) { + parser.consume(&Token::ident("deg"))?; + parser.consume(&Token::Comma)?; + + angle.to_radians() + } else { + 0.0 + }, + ..Default::default() + }; + + while !parser.check(&Token::ParenClose) { + if !gradient.stops.is_empty() { + parser.consume(&Token::Comma)?; } - } else { - gradient - .stops - .push(GradientStop::parse(angle_or_first_stop).map_err(|_| ParseError)?); - } - for stop in split { - gradient - .stops - .push(GradientStop::parse(stop).map_err(|_| ParseError)?); + gradient.stops.push(GradientStop::parse(parser)?); } + parser.consume(&Token::ParenClose)?; + Ok(gradient) } } diff --git a/crates/state/src/values/highlight.rs b/crates/state/src/values/highlight.rs index 4f1b96542..94bf235e4 100644 --- a/crates/state/src/values/highlight.rs +++ b/crates/state/src/values/highlight.rs @@ -1,6 +1,7 @@ use crate::{ Parse, ParseError, + Parser, }; #[derive(Default, Clone, Debug, PartialEq)] @@ -13,10 +14,13 @@ pub enum HighlightMode { } impl Parse for HighlightMode { - fn parse(value: &str) -> Result { - match value { - "expanded" => Ok(HighlightMode::Expanded), - _ => Ok(HighlightMode::Fit), - } + fn parse(parser: &mut Parser) -> Result { + parser.consume_map(|value| { + value.as_string().and_then(|value| match value { + "expanded" => Some(Self::Expanded), + "fit" => Some(Self::Fit), + _ => None, + }) + }) } } diff --git a/crates/state/src/values/overflow.rs b/crates/state/src/values/overflow.rs index e5a87f051..82f6e575b 100644 --- a/crates/state/src/values/overflow.rs +++ b/crates/state/src/values/overflow.rs @@ -3,6 +3,7 @@ use std::fmt; use crate::{ Parse, ParseError, + Parser, }; #[derive(Clone, Debug, PartialEq, Eq, Default)] @@ -13,10 +14,13 @@ pub enum OverflowMode { } impl Parse for OverflowMode { - fn parse(value: &str) -> Result { - Ok(match value { - "clip" => OverflowMode::Clip, - _ => OverflowMode::None, + fn parse(parser: &mut Parser) -> Result { + parser.consume_map(|value| { + value.as_string().and_then(|value| match value { + "clip" => Some(Self::Clip), + "none" => Some(Self::None), + _ => None, + }) }) } } @@ -24,8 +28,8 @@ impl Parse for OverflowMode { impl fmt::Display for OverflowMode { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str(match self { - OverflowMode::Clip => "clip", - OverflowMode::None => "none", + Self::Clip => "clip", + Self::None => "none", }) } } diff --git a/crates/state/src/values/position.rs b/crates/state/src/values/position.rs index 2d73ed5dd..a120fd02b 100644 --- a/crates/state/src/values/position.rs +++ b/crates/state/src/values/position.rs @@ -3,13 +3,17 @@ use torin::position::Position; use crate::{ Parse, ParseError, + Parser, }; impl Parse for Position { - fn parse(value: &str) -> Result { - Ok(match value { - "absolute" => Position::new_absolute(), - _ => Position::Stacked, + fn parse(parser: &mut Parser) -> Result { + parser.consume_map(|value| { + value.as_string().and_then(|value| match value { + "absolute" => Some(Self::new_absolute()), + "stacked" => Some(Self::Stacked), + _ => None, + }) }) } } diff --git a/crates/state/src/values/shadow.rs b/crates/state/src/values/shadow.rs index 907feb5df..ca9650984 100644 --- a/crates/state/src/values/shadow.rs +++ b/crates/state/src/values/shadow.rs @@ -1,10 +1,11 @@ use torin::scaled::Scaled; use crate::{ - ExtSplit, Fill, Parse, ParseError, + Parser, + Token, }; #[derive(Default, Clone, Debug, PartialEq)] @@ -25,51 +26,26 @@ pub struct Shadow { } impl Parse for Shadow { - fn parse(value: &str) -> Result { - let mut shadow_values = value.split_ascii_whitespace_excluding_group('(', ')'); + fn parse(parser: &mut Parser) -> Result { let mut shadow = Shadow::default(); - let first = shadow_values.next().ok_or(ParseError)?; + if parser.try_consume(&Token::ident("none")) { + return Ok(shadow); + } - if first == "inset" { + if parser.try_consume(&Token::ident("inset")) { shadow.position = ShadowPosition::Inset; - shadow.x = shadow_values - .next() - .ok_or(ParseError)? - .parse::() - .map_err(|_| ParseError)?; - } else { - shadow.x = first.parse::().map_err(|_| ParseError)?; } - shadow.y = shadow_values - .next() - .ok_or(ParseError)? - .parse::() - .map_err(|_| ParseError)?; - shadow.blur = shadow_values - .next() - .ok_or(ParseError)? - .parse::() - .map_err(|_| ParseError)?; - - let spread_or_fill = shadow_values.next().ok_or(ParseError)?; + shadow.x = parser.consume_map(Token::as_float)?; + shadow.y = parser.consume_map(Token::as_float)?; + shadow.blur = parser.consume_map(Token::as_float)?; - let mut already_filled = false; - if let Ok(spread) = spread_or_fill.parse::() { + if let Ok(spread) = parser.consume_map(Token::as_float) { shadow.spread = spread; - } else { - already_filled = true; - shadow.fill = Fill::parse(spread_or_fill).map_err(|_| ParseError)?; } - if let Some(fill) = shadow_values.next() { - if !already_filled { - shadow.fill = Fill::parse(fill).map_err(|_| ParseError)? - } else { - return Err(ParseError); - } - } + shadow.fill = Fill::parse(parser)?; Ok(shadow) } diff --git a/crates/state/src/values/size.rs b/crates/state/src/values/size.rs index 40604359d..108cff15f 100644 --- a/crates/state/src/values/size.rs +++ b/crates/state/src/values/size.rs @@ -9,70 +9,73 @@ use torin::{ use crate::{ Parse, ParseError, + Parser, + Token, }; impl Parse for Size { - fn parse(value: &str) -> Result { - if value == "auto" { - Ok(Size::Inner) - } else if value == "fill" { - Ok(Size::Fill) - } else if value == "fill-min" { - Ok(Size::FillMinimum) - } else if value.contains("calc") { - Ok(Size::DynamicCalculations(Box::new(parse_calc(value)?))) - } else if value.contains('%') { - Ok(Size::Percentage(Length::new( - value - .replace('%', "") - .parse::() - .map_err(|_| ParseError)?, - ))) - } else if value.contains('v') { - Ok(Size::RootPercentage(Length::new( - value - .replace('v', "") - .parse::() - .map_err(|_| ParseError)?, - ))) + fn parse(parser: &mut Parser) -> Result { + let value = parser.consume_if(|token| token.is_ident() || token.is_integer())?; + + if value.is_ident() { + value + .as_string() + .and_then(|value| match value { + "auto" => Some(Self::Inner), + "fill" => Some(Self::Fill), + "fill-min" => Some(Self::FillMinimum), + "calc" => parse_calc(parser) + .map(|value| Self::DynamicCalculations(Box::new(value))) + .ok(), + _ => None, + }) + .ok_or(ParseError) } else { - Ok(Size::Pixels(Length::new( - value.parse::().map_err(|_| ParseError)?, - ))) + let value = value.into_float(); + + Ok(if parser.try_consume(&Token::Percent) { + Size::Percentage(Length::new(value)) + } else if parser.try_consume(&Token::ident("v")) { + Size::RootPercentage(Length::new(value)) + } else { + Size::Pixels(Length::new(value)) + }) } } } -pub fn parse_calc(mut value: &str) -> Result, ParseError> { - let mut calcs = Vec::new(); +pub fn parse_calc(parser: &mut Parser) -> Result, ParseError> { + parser.consume(&Token::ParenOpen)?; - value = value - .strip_prefix("calc(") - .ok_or(ParseError)? - .strip_suffix(')') - .ok_or(ParseError)?; + let mut calcs = vec![]; - let values = value.split_whitespace(); + while let Ok(value) = parser.consume_if(|token| { + token.is_integer() + || matches!( + token, + Token::Plus | Token::Minus | Token::Slash | Token::Star + ) + }) { + if value.is_integer() { + let value = value.into_float(); - for val in values { - if val.contains('%') { - calcs.push(DynamicCalculation::Percentage( - val.replace('%', "").parse().map_err(|_| ParseError)?, - )); - } else if val == "+" { - calcs.push(DynamicCalculation::Add); - } else if val == "-" { - calcs.push(DynamicCalculation::Sub); - } else if val == "/" { - calcs.push(DynamicCalculation::Div); - } else if val == "*" { - calcs.push(DynamicCalculation::Mul); + calcs.push(if parser.try_consume(&Token::Percent) { + DynamicCalculation::Percentage(value) + } else { + DynamicCalculation::Pixels(value) + }); } else { - calcs.push(DynamicCalculation::Pixels( - val.parse::().map_err(|_| ParseError)?, - )); + match value { + Token::Plus => calcs.push(DynamicCalculation::Add), + Token::Minus => calcs.push(DynamicCalculation::Sub), + Token::Slash => calcs.push(DynamicCalculation::Div), + Token::Star => calcs.push(DynamicCalculation::Mul), + _ => {} + } } } + parser.consume(&Token::ParenClose)?; + Ok(calcs) } diff --git a/crates/state/src/values/text_shadow.rs b/crates/state/src/values/text_shadow.rs index 102e25ed3..233f4b45a 100644 --- a/crates/state/src/values/text_shadow.rs +++ b/crates/state/src/values/text_shadow.rs @@ -3,34 +3,21 @@ use freya_engine::prelude::*; use crate::{ Parse, ParseError, + Parser, + Token, }; // Same as shadow, but no inset or spread. impl Parse for TextShadow { - fn parse(value: &str) -> Result { - let mut shadow_values = value.split_ascii_whitespace(); + fn parse(parser: &mut Parser) -> Result { Ok(TextShadow { offset: ( - shadow_values - .next() - .ok_or(ParseError)? - .parse::() - .map_err(|_| ParseError)?, - shadow_values - .next() - .ok_or(ParseError)? - .parse::() - .map_err(|_| ParseError)?, + parser.consume_map(Token::as_float)?, + parser.consume_map(Token::as_float)?, ) .into(), - blur_sigma: shadow_values - .next() - .ok_or(ParseError)? - .parse::() - .map_err(|_| ParseError)? - / 2.0, - color: Color::parse(shadow_values.collect::>().join(" ").as_str()) - .map_err(|_| ParseError)?, + blur_sigma: parser.consume_map(Token::as_float)? as f64 / 2.0, + color: Color::parse(parser)?, }) } } diff --git a/crates/state/src/viewport.rs b/crates/state/src/viewport.rs index 4255d4420..fab1d0b34 100644 --- a/crates/state/src/viewport.rs +++ b/crates/state/src/viewport.rs @@ -15,10 +15,12 @@ use freya_native_core_macro::partial_derive_state; use crate::{ CustomAttributeValues, + Lexer, OverflowMode, Parse, ParseAttribute, ParseError, + Parser, }; #[derive(Default, PartialEq, Clone, Debug, Component)] @@ -32,12 +34,14 @@ impl ParseAttribute for ViewportState { fn parse_attribute( &mut self, attr: freya_native_core::prelude::OwnedAttributeView, - ) -> Result<(), crate::ParseError> { + ) -> Result<(), ParseError> { #[allow(clippy::single_match)] match attr.attribute { AttributeName::Overflow => { if let Some(value) = attr.value.as_text() { - self.overflow = OverflowMode::parse(value).map_err(|_| ParseError)?; + let mut parser = Parser::new(Lexer::parse(value)); + + self.overflow = OverflowMode::parse(&mut parser)?; } } _ => {} diff --git a/crates/state/tests/parse_align.rs b/crates/state/tests/parse_align.rs index 7153220ac..9d1ae2dec 100644 --- a/crates/state/tests/parse_align.rs +++ b/crates/state/tests/parse_align.rs @@ -3,36 +3,36 @@ use freya_node_state::Parse; #[test] fn parse_center_text_align() { - let center = TextAlign::parse("center"); + let center = TextAlign::parse_value("center"); assert_eq!(center, Ok(TextAlign::Center)); } #[test] fn parse_end_text_align() { - let end = TextAlign::parse("end"); + let end = TextAlign::parse_value("end"); assert_eq!(end, Ok(TextAlign::End)); } #[test] fn parse_justify_text_align() { - let justify = TextAlign::parse("justify"); + let justify = TextAlign::parse_value("justify"); assert_eq!(justify, Ok(TextAlign::Justify)); } #[test] fn parse_lefttext_align() { - let left = TextAlign::parse("left"); + let left = TextAlign::parse_value("left"); assert_eq!(left, Ok(TextAlign::Left)); } #[test] fn parse_right_text_align() { - let right = TextAlign::parse("right"); + let right = TextAlign::parse_value("right"); assert_eq!(right, Ok(TextAlign::Right)); } #[test] fn parse_start_text_align() { - let start = TextAlign::parse("start"); + let start = TextAlign::parse_value("start"); assert_eq!(start, Ok(TextAlign::Start)); } diff --git a/crates/state/tests/parse_border.rs b/crates/state/tests/parse_border.rs index 4250dd5be..a8b135445 100644 --- a/crates/state/tests/parse_border.rs +++ b/crates/state/tests/parse_border.rs @@ -11,7 +11,7 @@ use freya_node_state::{ #[test] fn parse_basic_border() { - let border = Border::parse("1 solid red"); + let border = Border::parse_value("1 solid red"); assert_eq!( border, @@ -26,7 +26,7 @@ fn parse_basic_border() { #[test] fn parse_gradient_border() { - let shadow = Border::parse("1 solid linear-gradient(red 0%, blue 100%)"); + let shadow = Border::parse_value("1 solid linear-gradient(red 0%, blue 100%)"); assert_eq!( shadow, Ok(Border { @@ -52,22 +52,22 @@ fn parse_gradient_border() { #[test] fn parse_border_alignments() { - let inner = BorderAlignment::parse("inner"); - let outer = BorderAlignment::parse("outer"); - let center = BorderAlignment::parse("center"); - let invalid = BorderAlignment::parse("invalid"); + let inner = BorderAlignment::parse_value("inner"); + let outer = BorderAlignment::parse_value("outer"); + let center = BorderAlignment::parse_value("center"); + let invalid = BorderAlignment::parse_value("invalid"); assert_eq!(inner, Ok(BorderAlignment::Inner)); assert_eq!(outer, Ok(BorderAlignment::Outer)); assert_eq!(center, Ok(BorderAlignment::Center)); - assert_eq!(invalid, Ok(BorderAlignment::Inner)); + assert!(invalid.is_err()); } #[test] fn parse_border_style() { - let solid = Border::parse("1 solid red"); - let none = Border::parse("1 rust red"); - let invalid = Border::parse("rust solid red"); + let solid = Border::parse_value("1 solid red"); + let invalid_style = Border::parse_value("1 rust red"); + let invalid_width = Border::parse_value("rust solid red"); assert_eq!( solid, @@ -78,14 +78,7 @@ fn parse_border_style() { alignment: BorderAlignment::default() }) ); - assert_eq!( - none, - Ok(Border { - width: 1.0, - fill: Fill::Color(Color::RED), - style: BorderStyle::None, - alignment: BorderAlignment::default() - }) - ); - assert!(invalid.is_err()); + + assert!(invalid_style.is_err()); + assert!(invalid_width.is_err()); } diff --git a/crates/state/tests/parse_color.rs b/crates/state/tests/parse_color.rs index de4ae324b..8a3479a54 100644 --- a/crates/state/tests/parse_color.rs +++ b/crates/state/tests/parse_color.rs @@ -3,53 +3,53 @@ use freya_node_state::Parse; #[test] fn parse_manual_color() { - let color = Color::parse("red"); + let color = Color::parse_value("red"); assert_eq!(color, Ok(Color::RED)); } #[test] fn parse_rgb_color() { - let color = Color::parse("rgb(91, 123, 57)"); + let color = Color::parse_value("rgb(91, 123, 57)"); assert_eq!(color, Ok(Color::from_rgb(91, 123, 57))); } #[test] fn parse_hsl_color() { - _ = Color::parse("hsl(28deg, 80%, 50%, 25%)").unwrap(); + _ = Color::parse_value("hsl(28deg, 80%, 50%, 25%)").unwrap(); } #[test] fn parse_argb_color_u8() { - let color = Color::parse("rgb(91, 123, 57, 127)"); + let color = Color::parse_value("rgb(91, 123, 57, 127)"); assert_eq!(color, Ok(Color::from_argb(127, 91, 123, 57))); } #[test] fn parse_argb_color_f32() { - let color = Color::parse("rgb(91, 123, 57, 0.5)"); + let color = Color::parse_value("rgb(91, 123, 57, 0.5)"); assert_eq!(color, Ok(Color::from_argb(128, 91, 123, 57))); } #[test] fn parse_hex_color() { - let color = Color::parse("#FFA500"); + let color = Color::parse_value("#FFA500"); assert_eq!(color, Ok(Color::from_rgb(255, 165, 0))); } #[test] fn invalid_colors() { - let incorrect_name = Color::parse("wow(0, 0, 0)"); - let extra_lparen = Color::parse("rgb((0, 0, 0)"); - let extra_rparen = Color::parse("rgb(0, 0, 0))"); - let missing_lparen = Color::parse("rgb0, 0, 0)"); - let missing_rparen = Color::parse("rgb(0, 0, 0"); - let missing_commas = Color::parse("rgb(0 0 0)"); - let extra_commas = Color::parse("rgb(0,, 0, 0)"); - let extra_component = Color::parse("rgb(0, 0, 0, 0, 0)"); - let extra_ending_commas = Color::parse("rgb(0, 0, 0, 0,)"); - let bad_unit = Color::parse("hsl(28in, 0.4, 0.25, 50%)"); - let missing_number_sign = Color::parse("FFA500"); - let incorrect_hex_length = Color::parse("#FFA0"); + let incorrect_name = Color::parse_value("wow(0, 0, 0)"); + let extra_lparen = Color::parse_value("rgb((0, 0, 0)"); + let extra_rparen = Color::parse_value("rgb(0, 0, 0))"); + let missing_lparen = Color::parse_value("rgb0, 0, 0)"); + let missing_rparen = Color::parse_value("rgb(0, 0, 0"); + let missing_commas = Color::parse_value("rgb(0 0 0)"); + let extra_commas = Color::parse_value("rgb(0,, 0, 0)"); + let extra_component = Color::parse_value("rgb(0, 0, 0, 0, 0)"); + let extra_ending_commas = Color::parse_value("rgb(0, 0, 0, 0,)"); + let bad_unit = Color::parse_value("hsl(28in, 0.4, 0.25, 50%)"); + let missing_number_sign = Color::parse_value("FFA500"); + let incorrect_hex_length = Color::parse_value("#FFA0"); assert!(incorrect_name.is_err()); assert!(extra_lparen.is_err()); diff --git a/crates/state/tests/parse_corner_radius.rs b/crates/state/tests/parse_corner_radius.rs index cd085ea78..96c6d5ae8 100644 --- a/crates/state/tests/parse_corner_radius.rs +++ b/crates/state/tests/parse_corner_radius.rs @@ -6,7 +6,7 @@ use freya_node_state::{ #[test] fn parse_basic_corner_radius() { assert_eq!( - CornerRadius::parse("3"), + CornerRadius::parse_value("3"), Ok(CornerRadius { top_left: 3.0, top_right: 3.0, @@ -19,7 +19,7 @@ fn parse_basic_corner_radius() { #[test] fn parse_two_value_radius() { assert_eq!( - CornerRadius::parse("2 4"), + CornerRadius::parse_value("2 4"), Ok(CornerRadius { top_left: 2.0, top_right: 2.0, @@ -33,7 +33,7 @@ fn parse_two_value_radius() { #[test] fn parse_four_value_radius() { assert_eq!( - CornerRadius::parse("2 4 3 1"), + CornerRadius::parse_value("2 4 3 1"), Ok(CornerRadius { top_left: 2.0, top_right: 4.0, @@ -46,10 +46,10 @@ fn parse_four_value_radius() { #[test] fn invalid_radius() { - let extra_value = CornerRadius::parse("1 2 4 3 1"); - let bad_value_count = CornerRadius::parse("4 3 1"); - let bad_unit = CornerRadius::parse("4deg 3"); - let incorrect_separator = CornerRadius::parse("4, 3, 2, 1"); + let extra_value = CornerRadius::parse_value("1 2 4 3 1"); + let bad_value_count = CornerRadius::parse_value("4 3 1"); + let bad_unit = CornerRadius::parse_value("4deg 3"); + let incorrect_separator = CornerRadius::parse_value("4, 3, 2, 1"); assert!(extra_value.is_err()); assert!(bad_value_count.is_err()); diff --git a/crates/state/tests/parse_decoration.rs b/crates/state/tests/parse_decoration.rs index 0d3e9ed77..9980bf5de 100644 --- a/crates/state/tests/parse_decoration.rs +++ b/crates/state/tests/parse_decoration.rs @@ -3,36 +3,38 @@ use freya_node_state::Parse; #[test] fn parse_text_decoration() { - let underline = TextDecoration::parse("underline"); + let underline = TextDecoration::parse_value("underline"); assert_eq!(underline, Ok(TextDecoration::UNDERLINE)); - let overline = TextDecoration::parse("overline"); + let overline = TextDecoration::parse_value("overline"); assert_eq!(overline, Ok(TextDecoration::OVERLINE)); - let line_through = TextDecoration::parse("line-through"); + let line_through = TextDecoration::parse_value("line-through"); assert_eq!(line_through, Ok(TextDecoration::LINE_THROUGH)); - let fallback = TextDecoration::parse("Rust"); - assert_eq!(fallback, Ok(TextDecoration::NO_DECORATION)); + let invalid_decoration_name = TextDecoration::parse_value("Rust"); + + assert!(invalid_decoration_name.is_err()); } #[test] fn parse_text_decoration_style() { - let solid = TextDecorationStyle::parse("solid"); + let solid = TextDecorationStyle::parse_value("solid"); assert_eq!(solid, Ok(TextDecorationStyle::Solid)); - let double = TextDecorationStyle::parse("double"); + let double = TextDecorationStyle::parse_value("double"); assert_eq!(double, Ok(TextDecorationStyle::Double)); - let dotted = TextDecorationStyle::parse("dotted"); + let dotted = TextDecorationStyle::parse_value("dotted"); assert_eq!(dotted, Ok(TextDecorationStyle::Dotted)); - let dashed = TextDecorationStyle::parse("dashed"); + let dashed = TextDecorationStyle::parse_value("dashed"); assert_eq!(dashed, Ok(TextDecorationStyle::Dashed)); - let wavy = TextDecorationStyle::parse("wavy"); + let wavy = TextDecorationStyle::parse_value("wavy"); assert_eq!(wavy, Ok(TextDecorationStyle::Wavy)); - let fallback = TextDecorationStyle::parse("Rust"); - assert_eq!(fallback, Ok(TextDecorationStyle::Solid)); + let invalid_decoration_style = TextDecorationStyle::parse_value("Rust"); + + assert!(invalid_decoration_style.is_err()); } diff --git a/crates/state/tests/parse_display.rs b/crates/state/tests/parse_display.rs index 9cd74e4c7..23f29e49b 100644 --- a/crates/state/tests/parse_display.rs +++ b/crates/state/tests/parse_display.rs @@ -3,42 +3,43 @@ use torin::alignment::Alignment; #[test] fn parse_normal_alignment() { - let alignment = Alignment::parse("start"); + let alignment = Alignment::parse_value("start"); assert_eq!(alignment, Ok(Alignment::Start)); } #[test] fn parse_center_alignment() { - let alignment = Alignment::parse("center"); + let alignment = Alignment::parse_value("center"); assert_eq!(alignment, Ok(Alignment::Center)); } #[test] fn parse_end_alignment() { - let alignment = Alignment::parse("end"); + let alignment = Alignment::parse_value("end"); assert_eq!(alignment, Ok(Alignment::End)); } #[test] fn parse_space_between_alignment() { - let alignment = Alignment::parse("space-between"); + let alignment = Alignment::parse_value("space-between"); assert_eq!(alignment, Ok(Alignment::SpaceBetween)); } #[test] fn parse_space_around_alignment() { - let alignment = Alignment::parse("space-around"); + let alignment = Alignment::parse_value("space-around"); assert_eq!(alignment, Ok(Alignment::SpaceAround)); } #[test] fn parse_space_evenly_alignment() { - let alignment = Alignment::parse("space-evenly"); + let alignment = Alignment::parse_value("space-evenly"); assert_eq!(alignment, Ok(Alignment::SpaceEvenly)); } #[test] fn parse_fallback_alignment() { - let alignment = Alignment::parse("Hello, World!"); - assert_eq!(alignment, Ok(Alignment::Start)); + let alignment = Alignment::parse_value("Hello, World!"); + + assert!(alignment.is_err()); } diff --git a/crates/state/tests/parse_gaps.rs b/crates/state/tests/parse_gaps.rs index 9150a9244..fbe96a8f9 100644 --- a/crates/state/tests/parse_gaps.rs +++ b/crates/state/tests/parse_gaps.rs @@ -3,24 +3,24 @@ use torin::gaps::Gaps; #[test] fn parse_all_gaps() { - let gaps = Gaps::parse("10"); + let gaps = Gaps::parse_value("10"); assert_eq!(gaps, Ok(Gaps::new(10.0, 10.0, 10.0, 10.0))); } #[test] fn parse_axis_gaps() { - let gaps = Gaps::parse("50 10"); + let gaps = Gaps::parse_value("50 10"); assert_eq!(gaps, Ok(Gaps::new(50.0, 10.0, 50.0, 10.0))); } #[test] fn parse_sides_gaps() { - let gaps = Gaps::parse("1 2 3 4"); + let gaps = Gaps::parse_value("1 2 3 4"); assert_eq!(gaps, Ok(Gaps::new(1.0, 2.0, 3.0, 4.0))); } #[test] fn parse_horizontal_axis_and_vertical_sides() { - let gaps = Gaps::parse("5 50 30"); + let gaps = Gaps::parse_value("5 50 30"); assert_eq!(gaps, Ok(Gaps::new(5.0, 50.0, 30.0, 50.0))); } diff --git a/crates/state/tests/parse_gradient.rs b/crates/state/tests/parse_gradient.rs index f85cdd2c1..a09be80cf 100644 --- a/crates/state/tests/parse_gradient.rs +++ b/crates/state/tests/parse_gradient.rs @@ -8,7 +8,7 @@ use freya_node_state::{ #[test] fn parse_basic_gradient() { assert_eq!( - LinearGradient::parse("linear-gradient(red 0%, blue 100%)"), + LinearGradient::parse_value("linear-gradient(red 0%, blue 100%)"), Ok(LinearGradient { angle: 0.0, stops: vec![ @@ -28,7 +28,7 @@ fn parse_basic_gradient() { #[test] fn parse_rgb_hsl_gradient() { assert_eq!( - LinearGradient::parse("linear-gradient(0deg, rgb(255, 0, 0) 0%, blue 100%)"), + LinearGradient::parse_value("linear-gradient(0deg, rgb(255, 0, 0) 0%, blue 100%)"), Ok(LinearGradient { angle: 0.0, stops: vec![ @@ -48,7 +48,7 @@ fn parse_rgb_hsl_gradient() { #[test] fn parse_gradient_angle() { assert_eq!( - LinearGradient::parse("linear-gradient(45deg, red 0%, blue 100%)"), + LinearGradient::parse_value("linear-gradient(45deg, red 0%, blue 100%)"), Ok(LinearGradient { angle: f32::to_radians(45.0), stops: vec![ @@ -67,18 +67,20 @@ fn parse_gradient_angle() { #[test] fn invalid_gradients() { - let incorrect_name = LinearGradient::parse("lkdsjfalkasdasdjaslkfjsdklfs(red 0%, blue 100%)"); - let extra_lparen = LinearGradient::parse("linear-gradient((red 0%, blue 100%)"); - let extra_rparen = LinearGradient::parse("linear-gradient(red 0%, blue 100%))"); - let missing_rparen = LinearGradient::parse("linear-gradient(red 0%, blue 100%"); - let missing_commas = LinearGradient::parse("linear-gradient(red 0% blue 100%)"); - let extra_commas = LinearGradient::parse("linear-gradient(red 0%, blue 100%,)"); + let incorrect_name = + LinearGradient::parse_value("lkdsjfalkasdasdjaslkfjsdklfs(red 0%, blue 100%)"); + let extra_lparen = LinearGradient::parse_value("linear-gradient((red 0%, blue 100%)"); + let extra_rparen = LinearGradient::parse_value("linear-gradient(red 0%, blue 100%))"); + let missing_rparen = LinearGradient::parse_value("linear-gradient(red 0%, blue 100%"); + let missing_commas = LinearGradient::parse_value("linear-gradient(red 0% blue 100%)"); + let extra_commas = LinearGradient::parse_value("linear-gradient(red 0%, blue 100%,)"); let extra_stop_component = - LinearGradient::parse("linear-gradient(red 0% something, blue 100%)"); - let bad_angle_unit = LinearGradient::parse("linear-gradient(45ft, red 0%, blue 100%,)"); - let bad_offset_unit = LinearGradient::parse("linear-gradient(45deg, red 0atm, blue 100kpa)"); - let missing_color = LinearGradient::parse("linear-gradient(45deg, 0%, blue 100%)"); - let missing_offset = LinearGradient::parse("linear-gradient(45deg, red, blue 100%)"); + LinearGradient::parse_value("linear-gradient(red 0% something, blue 100%)"); + let bad_angle_unit = LinearGradient::parse_value("linear-gradient(45ft, red 0%, blue 100%,)"); + let bad_offset_unit = + LinearGradient::parse_value("linear-gradient(45deg, red 0atm, blue 100kpa)"); + let missing_color = LinearGradient::parse_value("linear-gradient(45deg, 0%, blue 100%)"); + let missing_offset = LinearGradient::parse_value("linear-gradient(45deg, red, blue 100%)"); assert!(incorrect_name.is_err()); assert!(extra_lparen.is_err()); diff --git a/crates/state/tests/parse_highlight.rs b/crates/state/tests/parse_highlight.rs index 913ab3b67..bd189f899 100644 --- a/crates/state/tests/parse_highlight.rs +++ b/crates/state/tests/parse_highlight.rs @@ -5,18 +5,19 @@ use freya_node_state::{ #[test] fn parse_expanded_highlight_mode() { - let expanded = HighlightMode::parse("expanded"); + let expanded = HighlightMode::parse_value("expanded"); assert_eq!(expanded, Ok(HighlightMode::Expanded)); } #[test] fn parse_fit_highlight_mode() { - let fit = HighlightMode::parse("fit"); + let fit = HighlightMode::parse_value("fit"); assert_eq!(fit, Ok(HighlightMode::Fit)); } #[test] fn parse_fallback_highlight_mode() { - let fallback = HighlightMode::parse("Hello, World!"); - assert_eq!(fallback, Ok(HighlightMode::Fit)); + let fallback = HighlightMode::parse_value("Hello, World!"); + + assert!(fallback.is_err()); } diff --git a/crates/state/tests/parse_shadows.rs b/crates/state/tests/parse_shadows.rs index 081d44469..f100d83ad 100644 --- a/crates/state/tests/parse_shadows.rs +++ b/crates/state/tests/parse_shadows.rs @@ -10,7 +10,7 @@ use freya_node_state::{ #[test] fn parse_big_shadow() { - let shadow = Shadow::parse("1 2 50 25.0 red"); + let shadow = Shadow::parse_value("1 2 50 25.0 red"); assert_eq!( shadow, Ok(Shadow { @@ -26,7 +26,7 @@ fn parse_big_shadow() { #[test] fn parse_inset_shadow() { - let shadow = Shadow::parse("inset 1 2 50 25.0 red"); + let shadow = Shadow::parse_value("inset 1 2 50 25.0 red"); assert_eq!( shadow, Ok(Shadow { @@ -42,7 +42,7 @@ fn parse_inset_shadow() { #[test] fn parse_shadow_with_assumed_spread() { - let shadow = Shadow::parse("inset 1 2 50 red"); + let shadow = Shadow::parse_value("inset 1 2 50 red"); assert_eq!( shadow, Ok(Shadow { @@ -58,7 +58,7 @@ fn parse_shadow_with_assumed_spread() { #[test] fn parse_gradient_shadow() { - let shadow = Shadow::parse("inset 1 2 50 linear-gradient(red 0%, blue 100%)"); + let shadow = Shadow::parse_value("inset 1 2 50 linear-gradient(red 0%, blue 100%)"); assert_eq!( shadow, Ok(Shadow { diff --git a/crates/state/tests/parse_size.rs b/crates/state/tests/parse_size.rs index b2a3740ee..92c4158b5 100644 --- a/crates/state/tests/parse_size.rs +++ b/crates/state/tests/parse_size.rs @@ -9,25 +9,25 @@ use torin::{ #[test] fn parse_pixel_size() { - let size = Size::parse("123"); + let size = Size::parse_value("123"); assert_eq!(size, Ok(Size::Pixels(Length::new(123.0)))); } #[test] fn parse_relative_size() { - let size = Size::parse("78.123%"); + let size = Size::parse_value("78.123%"); assert_eq!(size, Ok(Size::Percentage(Length::new(78.123)))); } #[test] fn parse_auto_size() { - let size = Size::parse("auto"); + let size = Size::parse_value("auto"); assert_eq!(size, Ok(Size::Inner)); } #[test] fn parse_calc_size() { - let size = Size::parse("calc(90% - 5% * 123.6)"); + let size = Size::parse_value("calc(90% - 5% * 123.6)"); assert_eq!( size, Ok(Size::DynamicCalculations(Box::new(vec![ diff --git a/crates/state/tests/parse_text_shadow.rs b/crates/state/tests/parse_text_shadow.rs index 62b9131ec..3b3fdd39e 100644 --- a/crates/state/tests/parse_text_shadow.rs +++ b/crates/state/tests/parse_text_shadow.rs @@ -3,7 +3,7 @@ use freya_node_state::Parse; #[test] fn parse_text_shadow() { - let text_shadow = TextShadow::parse("1 5 12 rgb(255, 0, 0)"); + let text_shadow = TextShadow::parse_value("1 5 12 rgb(255, 0, 0)"); assert_eq!( text_shadow, Ok(TextShadow { diff --git a/examples/custom_tokio_rt.rs b/examples/custom_tokio_rt.rs index 3fe010705..e90fbfc71 100644 --- a/examples/custom_tokio_rt.rs +++ b/examples/custom_tokio_rt.rs @@ -3,8 +3,6 @@ windows_subsystem = "windows" )] -use freya::prelude::*; - #[cfg(not(feature = "custom-tokio-rt"))] fn main() { panic!("Run this example without the `custom-tokio-rt` feature."); @@ -12,6 +10,8 @@ fn main() { #[cfg(feature = "custom-tokio-rt")] fn main() { + use freya::prelude::*; + let rt = tokio::runtime::Builder::new_multi_thread() .enable_all() .build() diff --git a/examples/render_canvas.rs b/examples/render_canvas.rs index 56d0f257e..9af2333fe 100644 --- a/examples/render_canvas.rs +++ b/examples/render_canvas.rs @@ -27,7 +27,8 @@ fn app() -> Element { }); let canvas = use_canvas(move || { - let state = state.read().clone(); + let state = state(); + Box::new(move |canvas, font_collection, region, _| { canvas.translate((region.min_x(), region.min_y())); diff --git a/examples/shader_editor.rs b/examples/shader_editor.rs index 5d70cf599..9086311dd 100644 --- a/examples/shader_editor.rs +++ b/examples/shader_editor.rs @@ -207,7 +207,7 @@ fn ShaderView(editable: UseEditable) -> Element { UniformValue::Float(instant.elapsed().as_secs_f32()), ); - let uniforms = Data::new_copy(&builder.build(&runtime_effect)); + let uniforms = Data::new_copy(&builder.build(runtime_effect)); let shader = runtime_effect.make_shader(uniforms, &[], None).unwrap(); From 852d3fdbfa90d9f1e6a2f1a8811f02bbff7ed8e7 Mon Sep 17 00:00:00 2001 From: Savchenko Ivan <73419411+Aiving@users.noreply.github.com> Date: Mon, 15 Jul 2024 09:03:29 +0000 Subject: [PATCH 02/24] change parsing api replace all Parser::new(...) in node states with Parse::parse_value --- crates/state/src/cursor.rs | 18 ++------- crates/state/src/font_style.rs | 48 +++++------------------ crates/state/src/layout.rs | 50 ++++++------------------ crates/state/src/lexing.rs | 16 ++++---- crates/state/src/parsing.rs | 18 ++++++++- crates/state/src/style.rs | 32 ++++----------- crates/state/src/values/alignment.rs | 2 +- crates/state/src/values/border.rs | 6 +-- crates/state/src/values/color.rs | 18 ++++----- crates/state/src/values/content.rs | 2 +- crates/state/src/values/corner_radius.rs | 8 ++-- crates/state/src/values/cursor.rs | 2 +- crates/state/src/values/decoration.rs | 4 +- crates/state/src/values/font.rs | 14 +++---- crates/state/src/values/gaps.rs | 10 ++--- crates/state/src/values/gradient.rs | 4 +- crates/state/src/values/highlight.rs | 2 +- crates/state/src/values/overflow.rs | 2 +- crates/state/src/values/position.rs | 2 +- crates/state/src/values/shadow.rs | 8 ++-- crates/state/src/values/size.rs | 12 +++--- crates/state/src/values/text_shadow.rs | 6 +-- crates/state/src/viewport.rs | 6 +-- 23 files changed, 110 insertions(+), 180 deletions(-) diff --git a/crates/state/src/cursor.rs b/crates/state/src/cursor.rs index 93ff403f8..e9e1baf15 100644 --- a/crates/state/src/cursor.rs +++ b/crates/state/src/cursor.rs @@ -22,11 +22,9 @@ use crate::{ CursorReference, CustomAttributeValues, HighlightMode, - Lexer, Parse, ParseAttribute, ParseError, - Parser, }; #[derive(Clone, Debug, PartialEq, Component)] @@ -71,16 +69,12 @@ impl ParseAttribute for CursorState { } AttributeName::CursorColor => { if let Some(value) = attr.value.as_text() { - let mut parser = Parser::new(Lexer::parse(value)); - - self.color = Color::parse(&mut parser)?; + self.color = Color::parse_value(value)?; } } AttributeName::CursorMode => { if let Some(value) = attr.value.as_text() { - let mut parser = Parser::new(Lexer::parse(value)); - - self.mode = CursorMode::parse(&mut parser)?; + self.mode = CursorMode::parse_value(value)?; } } AttributeName::CursorId => { @@ -97,16 +91,12 @@ impl ParseAttribute for CursorState { } AttributeName::HighlightColor => { if let Some(value) = attr.value.as_text() { - let mut parser = Parser::new(Lexer::parse(value)); - - self.highlight_color = Color::parse(&mut parser)?; + self.highlight_color = Color::parse_value(value)? } } AttributeName::HighlightMode => { if let Some(value) = attr.value.as_text() { - let mut parser = Parser::new(Lexer::parse(value)); - - self.highlight_mode = HighlightMode::parse(&mut parser)?; + self.highlight_mode = HighlightMode::parse_value(value)? } } AttributeName::CursorReference => { diff --git a/crates/state/src/font_style.rs b/crates/state/src/font_style.rs index 5029ac92e..c526a4d46 100644 --- a/crates/state/src/font_style.rs +++ b/crates/state/src/font_style.rs @@ -22,11 +22,9 @@ use torin::torin::Torin; use crate::{ CustomAttributeValues, - Lexer, Parse, ParseAttribute, ParseError, - Parser, TextOverflow, Token, }; @@ -115,23 +113,13 @@ impl ParseAttribute for FontStyleState { // Make an exception for the "inherit" as in this case we don't want to pass // a color at all but use the inherited one. if value != "inherit" { - let mut parser = Parser::new(Lexer::parse(value)); - - self.color = Color::parse(&mut parser)?; + self.color = Color::parse_value(value)? } } } AttributeName::TextShadow => { if let Some(value) = attr.value.as_text() { - let mut parser = Parser::new(Lexer::parse(value)); - - let mut shadows = vec![TextShadow::parse(&mut parser)?]; - - while parser.try_consume(&Token::Comma) { - shadows.push(TextShadow::parse(&mut parser)?); - } - - self.text_shadows = shadows; + self.text_shadows = TextShadow::parse_values(value, &Token::Comma)?; } } AttributeName::FontFamily => { @@ -160,9 +148,7 @@ impl ParseAttribute for FontStyleState { } AttributeName::TextAlign => { if let Some(value) = attr.value.as_text() { - let mut parser = Parser::new(Lexer::parse(value)); - - self.text_align = TextAlign::parse(&mut parser)?; + self.text_align = TextAlign::parse_value(value)? } } AttributeName::MaxLines => { @@ -172,51 +158,37 @@ impl ParseAttribute for FontStyleState { } AttributeName::TextOverflow => { if let Some(value) = attr.value.as_text() { - let mut parser = Parser::new(Lexer::parse(value)); - - self.text_overflow = TextOverflow::parse(&mut parser)?; + self.text_overflow = TextOverflow::parse_value(value)? } } AttributeName::FontStyle => { if let Some(value) = attr.value.as_text() { - let mut parser = Parser::new(Lexer::parse(value)); - - self.font_slant = Slant::parse(&mut parser)?; + self.font_slant = Slant::parse_value(value)? } } AttributeName::FontWeight => { if let Some(value) = attr.value.as_text() { - let mut parser = Parser::new(Lexer::parse(value)); - - self.font_weight = Weight::parse(&mut parser)?; + self.font_weight = Weight::parse_value(value)? } } AttributeName::FontWidth => { if let Some(value) = attr.value.as_text() { - let mut parser = Parser::new(Lexer::parse(value)); - - self.font_width = Width::parse(&mut parser)?; + self.font_width = Width::parse_value(value)? } } AttributeName::Decoration => { if let Some(value) = attr.value.as_text() { - let mut parser = Parser::new(Lexer::parse(value)); - - self.decoration.ty = TextDecoration::parse(&mut parser)?; + self.decoration.ty = TextDecoration::parse_value(value)? } } AttributeName::DecorationStyle => { if let Some(value) = attr.value.as_text() { - let mut parser = Parser::new(Lexer::parse(value)); - - self.decoration.style = TextDecorationStyle::parse(&mut parser)?; + self.decoration.style = TextDecorationStyle::parse_value(value)? } } AttributeName::DecorationColor => { if let Some(value) = attr.value.as_text() { - let mut parser = Parser::new(Lexer::parse(value)); - - self.decoration.color = Color::parse(&mut parser)?; + self.decoration.color = Color::parse_value(value)? } else { self.decoration.color = self.color; } diff --git a/crates/state/src/layout.rs b/crates/state/src/layout.rs index b5f2c5728..b25d40aa4 100644 --- a/crates/state/src/layout.rs +++ b/crates/state/src/layout.rs @@ -23,12 +23,10 @@ use torin::prelude::*; use crate::{ CustomAttributeValues, - Lexer, NodeReference, Parse, ParseAttribute, ParseError, - Parser, }; #[derive(Default, Clone, Debug, Component, PartialEq)] @@ -60,58 +58,42 @@ impl ParseAttribute for LayoutState { match attr.attribute { AttributeName::Width => { if let Some(value) = attr.value.as_text() { - let mut parser = Parser::new(Lexer::parse(value)); - - self.width = Size::parse(&mut parser)?; + self.width = Size::parse_value(value)?; } } AttributeName::Height => { if let Some(value) = attr.value.as_text() { - let mut parser = Parser::new(Lexer::parse(value)); - - self.height = Size::parse(&mut parser)?; + self.height = Size::parse_value(value)?; } } AttributeName::MinHeight => { if let Some(value) = attr.value.as_text() { - let mut parser = Parser::new(Lexer::parse(value)); - - self.minimum_height = Size::parse(&mut parser)?; + self.minimum_height = Size::parse_value(value)?; } } AttributeName::MinWidth => { if let Some(value) = attr.value.as_text() { - let mut parser = Parser::new(Lexer::parse(value)); - - self.minimum_width = Size::parse(&mut parser)?; + self.minimum_width = Size::parse_value(value)?; } } AttributeName::MaxHeight => { if let Some(value) = attr.value.as_text() { - let mut parser = Parser::new(Lexer::parse(value)); - - self.maximum_height = Size::parse(&mut parser)?; + self.maximum_height = Size::parse_value(value)?; } } AttributeName::MaxWidth => { if let Some(value) = attr.value.as_text() { - let mut parser = Parser::new(Lexer::parse(value)); - - self.maximum_width = Size::parse(&mut parser)?; + self.maximum_width = Size::parse_value(value)?; } } AttributeName::Padding => { if let Some(value) = attr.value.as_text() { - let mut parser = Parser::new(Lexer::parse(value)); - - self.padding = Gaps::parse(&mut parser)?; + self.padding = Gaps::parse_value(value)?; } } AttributeName::Margin => { if let Some(value) = attr.value.as_text() { - let mut parser = Parser::new(Lexer::parse(value)); - - self.margin = Gaps::parse(&mut parser)?; + self.margin = Gaps::parse_value(value)?; } } AttributeName::Direction => { @@ -135,24 +117,18 @@ impl ParseAttribute for LayoutState { } AttributeName::MainAlign => { if let Some(value) = attr.value.as_text() { - let mut parser = Parser::new(Lexer::parse(value)); - - self.main_alignment = Alignment::parse(&mut parser)?; + self.main_alignment = Alignment::parse_value(value)?; } } AttributeName::CrossAlign => { if let Some(value) = attr.value.as_text() { - let mut parser = Parser::new(Lexer::parse(value)); - - self.cross_alignment = Alignment::parse(&mut parser)?; + self.cross_alignment = Alignment::parse_value(value)?; } } AttributeName::Position => { if let Some(value) = attr.value.as_text() { if self.position.is_empty() { - let mut parser = Parser::new(Lexer::parse(value)); - - self.position = Position::parse(&mut parser)?; + self.position = Position::parse_value(value)?; } } } @@ -182,9 +158,7 @@ impl ParseAttribute for LayoutState { } AttributeName::Content => { if let Some(value) = attr.value.as_text() { - let mut parser = Parser::new(Lexer::parse(value)); - - self.content = Content::parse(&mut parser)?; + self.content = Content::parse_value(value)?; } } AttributeName::Reference => { diff --git a/crates/state/src/lexing.rs b/crates/state/src/lexing.rs index e9b08bea3..511ef3d4b 100644 --- a/crates/state/src/lexing.rs +++ b/crates/state/src/lexing.rs @@ -26,15 +26,15 @@ impl Token { matches!(self, Token::Ident(_)) } - pub fn is_float(&self) -> bool { + pub fn is_f32(&self) -> bool { matches!(self, Token::Float(_)) } - pub fn is_number(&self) -> bool { + pub fn is_i64(&self) -> bool { matches!(self, Token::Number(_)) } - pub fn is_integer(&self) -> bool { + pub fn is_i64_or_f32(&self) -> bool { matches!(self, Token::Number(_) | Token::Float(_)) } @@ -46,7 +46,7 @@ impl Token { } } - pub fn into_float(self) -> f32 { + pub fn into_f32(self) -> f32 { if let Token::Float(value) = self { value } else if let Token::Number(value) = self { @@ -56,7 +56,7 @@ impl Token { } } - pub fn into_number(self) -> i64 { + pub fn into_i64(self) -> i64 { if let Token::Number(value) = self { value } else { @@ -72,7 +72,7 @@ impl Token { } } - pub fn as_string(&self) -> Option<&str> { + pub fn try_as_str(&self) -> Option<&str> { if let Token::Ident(value) = self { Some(value.as_str()) } else { @@ -80,7 +80,7 @@ impl Token { } } - pub fn as_float(&self) -> Option { + pub fn try_as_f32(&self) -> Option { if let Token::Float(value) = self { Some(*value) } else if let Token::Number(value) = self { @@ -90,7 +90,7 @@ impl Token { } } - pub fn as_number(&self) -> Option { + pub fn try_as_i64(&self) -> Option { if let Token::Number(value) = self { Some(*value) } else { diff --git a/crates/state/src/parsing.rs b/crates/state/src/parsing.rs index 2d1ab97c7..5a17b460e 100644 --- a/crates/state/src/parsing.rs +++ b/crates/state/src/parsing.rs @@ -16,7 +16,7 @@ use crate::{ pub struct ParseError; pub struct Parser { - tokens: Peekable>, + pub(crate) tokens: Peekable>, } impl Parser { @@ -121,6 +121,22 @@ pub trait Parse: Sized { value } } + + fn parse_values(value: &str, separator: &Token) -> Result, ParseError> { + let mut parser = Parser::new(Lexer::parse(value)); + + let mut values = vec![Self::parse(&mut parser)?]; + + while parser.try_consume(separator) { + values.push(Self::parse(&mut parser)?); + } + + if parser.tokens.len() > 0 { + Err(ParseError) + } else { + Ok(values) + } + } } pub trait ParseAttribute: Sized { diff --git a/crates/state/src/style.rs b/crates/state/src/style.rs index d44988940..d6d7bcdf9 100644 --- a/crates/state/src/style.rs +++ b/crates/state/src/style.rs @@ -54,16 +54,12 @@ impl ParseAttribute for StyleState { return Ok(()); } - let mut parser = Parser::new(Lexer::parse(value)); - - self.background = Fill::parse(&mut parser)?; + self.background = Fill::parse_value(value)?; } } AttributeName::Border => { if let Some(value) = attr.value.as_text() { - let mut parser = Parser::new(Lexer::parse(value)); - - let mut border = Border::parse(&mut parser)?; + let mut border = Border::parse_value(value)?; border.alignment = self.border.alignment; @@ -72,29 +68,17 @@ impl ParseAttribute for StyleState { } AttributeName::BorderAlign => { if let Some(value) = attr.value.as_text() { - let mut parser = Parser::new(Lexer::parse(value)); - - self.border.alignment = BorderAlignment::parse(&mut parser)?; + self.border.alignment = BorderAlignment::parse_value(value)?; } } AttributeName::Shadow => { if let Some(value) = attr.value.as_text() { - let mut parser = Parser::new(Lexer::parse(value)); - - let mut shadows = vec![Shadow::parse(&mut parser)?]; - - while parser.try_consume(&Token::Comma) { - shadows.push(Shadow::parse(&mut parser)?); - } - - self.shadows = shadows; + self.shadows = Shadow::parse_values(value, &Token::Comma)?; } } AttributeName::CornerRadius => { if let Some(value) = attr.value.as_text() { - let mut parser = Parser::new(Lexer::parse(value)); - - let mut radius = CornerRadius::parse(&mut parser)?; + let mut radius = CornerRadius::parse_value(value)?; radius.smoothing = self.corner_radius.smoothing; @@ -105,7 +89,7 @@ impl ParseAttribute for StyleState { if let Some(value) = attr.value.as_text() { let mut parser = Parser::new(Lexer::parse(value)); - let smoothing = parser.consume_map(Token::as_float)?; + let smoothing = parser.consume_map(Token::try_as_f32)?; parser.consume(&Token::Percent)?; @@ -131,9 +115,7 @@ impl ParseAttribute for StyleState { } AttributeName::Overflow => { if let Some(value) = attr.value.as_text() { - let mut parser = Parser::new(Lexer::parse(value)); - - self.overflow = OverflowMode::parse(&mut parser)?; + self.overflow = OverflowMode::parse_value(value)?; } } AttributeName::Opacity => { diff --git a/crates/state/src/values/alignment.rs b/crates/state/src/values/alignment.rs index 135c91673..67d7b9c9c 100644 --- a/crates/state/src/values/alignment.rs +++ b/crates/state/src/values/alignment.rs @@ -9,7 +9,7 @@ use crate::{ impl Parse for Alignment { fn parse(parser: &mut Parser) -> Result { parser.consume_map(|value| { - value.as_string().and_then(|value| match value { + value.try_as_str().and_then(|value| match value { "start" => Some(Self::Start), "center" => Some(Self::Center), "end" => Some(Self::End), diff --git a/crates/state/src/values/border.rs b/crates/state/src/values/border.rs index dd8609186..167a5cee8 100644 --- a/crates/state/src/values/border.rs +++ b/crates/state/src/values/border.rs @@ -36,7 +36,7 @@ pub enum BorderAlignment { impl Parse for BorderAlignment { fn parse(parser: &mut Parser) -> Result { parser.consume_map(|value| { - value.as_string().and_then(|value| match value { + value.try_as_str().and_then(|value| match value { "inner" => Some(BorderAlignment::Inner), "outer" => Some(BorderAlignment::Outer), "center" => Some(BorderAlignment::Center), @@ -72,9 +72,9 @@ impl Parse for Border { } Ok(Border { - width: parser.consume_map(Token::as_float)?, + width: parser.consume_map(Token::try_as_f32)?, style: parser.consume_map(|value| { - value.as_string().and_then(|value| match value { + value.try_as_str().and_then(|value| match value { "none" => Some(BorderStyle::None), "solid" => Some(BorderStyle::Solid), _ => None, diff --git a/crates/state/src/values/color.rs b/crates/state/src/values/color.rs index 6135ed9f0..c3593f6ea 100644 --- a/crates/state/src/values/color.rs +++ b/crates/state/src/values/color.rs @@ -86,7 +86,7 @@ impl DisplayColor for Color { } fn parse_number_as>(token: &Token) -> Option { - token.as_number().and_then(|value| T::try_from(value).ok()) + token.try_as_i64().and_then(|value| T::try_from(value).ok()) } fn parse_rgb(parser: &mut Parser) -> Result { @@ -103,11 +103,11 @@ fn parse_rgb(parser: &mut Parser) -> Result { let blue = parser.consume_map(parse_number_as)?; let color = if parser.try_consume(&Token::Comma) { - let alpha = parser.consume_if(Token::is_integer).and_then(|token| { - if token.is_number() { - u8::try_from(token.into_number()).map_err(|_| ParseError) + let alpha = parser.consume_if(Token::is_i64_or_f32).and_then(|token| { + if token.is_i64() { + u8::try_from(token.into_i64()).map_err(|_| ParseError) } else { - Ok((token.into_float() * 255.0).round().clamp(0.0, 255.0) as u8) + Ok((token.into_f32() * 255.0).round().clamp(0.0, 255.0) as u8) } })?; @@ -124,7 +124,7 @@ fn parse_rgb(parser: &mut Parser) -> Result { fn parse_hsl(parser: &mut Parser) -> Result { parser.consume(&Token::ParenOpen)?; - let h = parser.consume_map(Token::as_number).and_then(|value| { + let h = parser.consume_map(Token::try_as_i64).and_then(|value| { if (0..=360).contains(&value) { Ok(value as f32) } else { @@ -135,7 +135,7 @@ fn parse_hsl(parser: &mut Parser) -> Result { parser.consume(&Token::ident("deg"))?; parser.consume(&Token::Comma)?; - let mut s = parser.consume_map(Token::as_number).and_then(|value| { + let mut s = parser.consume_map(Token::try_as_i64).and_then(|value| { if (0..=100).contains(&value) { Ok((value as f32) / 100.0) } else { @@ -146,7 +146,7 @@ fn parse_hsl(parser: &mut Parser) -> Result { parser.consume(&Token::Percent)?; parser.consume(&Token::Comma)?; - let mut l = parser.consume_map(Token::as_number).and_then(|value| { + let mut l = parser.consume_map(Token::try_as_i64).and_then(|value| { if (0..=100).contains(&value) { Ok((value as f32) / 100.0) } else { @@ -157,7 +157,7 @@ fn parse_hsl(parser: &mut Parser) -> Result { parser.consume(&Token::Percent)?; let a = if parser.consume(&Token::Comma).is_ok() { - let value = parser.consume_map(Token::as_number).and_then(|value| { + let value = parser.consume_map(Token::try_as_i64).and_then(|value| { if (0..=100).contains(&value) { Ok((value as f32) / 100.0) } else { diff --git a/crates/state/src/values/content.rs b/crates/state/src/values/content.rs index ab77434cd..fb9daa7d2 100644 --- a/crates/state/src/values/content.rs +++ b/crates/state/src/values/content.rs @@ -9,7 +9,7 @@ use crate::{ impl Parse for Content { fn parse(parser: &mut Parser) -> Result { parser.consume_map(|value| { - value.as_string().and_then(|value| match value { + value.try_as_str().and_then(|value| match value { "normal" => Some(Self::Normal), "fit" => Some(Self::Fit), _ => None, diff --git a/crates/state/src/values/corner_radius.rs b/crates/state/src/values/corner_radius.rs index 7768fd566..e614d3f97 100644 --- a/crates/state/src/values/corner_radius.rs +++ b/crates/state/src/values/corner_radius.rs @@ -205,10 +205,10 @@ impl Parse for CornerRadius { let mut radius = CornerRadius::default(); match ( - parser.consume_map(Token::as_float)?, - parser.consume_map(Token::as_float).ok(), - parser.consume_map(Token::as_float).ok(), - parser.consume_map(Token::as_float).ok(), + parser.consume_map(Token::try_as_f32)?, + parser.consume_map(Token::try_as_f32).ok(), + parser.consume_map(Token::try_as_f32).ok(), + parser.consume_map(Token::try_as_f32).ok(), ) { // Same in all corners (value, None, None, None) => { diff --git a/crates/state/src/values/cursor.rs b/crates/state/src/values/cursor.rs index c4ee4d83f..c4dd507d2 100644 --- a/crates/state/src/values/cursor.rs +++ b/crates/state/src/values/cursor.rs @@ -15,7 +15,7 @@ pub enum CursorMode { impl Parse for CursorMode { fn parse(parser: &mut Parser) -> Result { parser.consume_map(|value| { - value.as_string().and_then(|value| match value { + value.try_as_str().and_then(|value| match value { "none" => Some(Self::None), "editable" => Some(Self::Editable), _ => None, diff --git a/crates/state/src/values/decoration.rs b/crates/state/src/values/decoration.rs index dfba0c265..b3f0fba56 100644 --- a/crates/state/src/values/decoration.rs +++ b/crates/state/src/values/decoration.rs @@ -11,7 +11,7 @@ impl Parse for TextDecoration { let mut decoration = Self::default(); while let Ok(value) = parser.consume_map(|token| { - token.as_string().and_then(|value| match value { + token.try_as_str().and_then(|value| match value { "none" => Some(Self::NO_DECORATION), "underline" => Some(Self::UNDERLINE), "overline" => Some(Self::OVERLINE), @@ -29,7 +29,7 @@ impl Parse for TextDecoration { impl Parse for TextDecorationStyle { fn parse(parser: &mut Parser) -> Result { parser.consume_map(|token| { - token.as_string().and_then(|value| match value { + token.try_as_str().and_then(|value| match value { "solid" => Some(Self::Solid), "double" => Some(Self::Double), "dotted" => Some(Self::Dotted), diff --git a/crates/state/src/values/font.rs b/crates/state/src/values/font.rs index 8e5f77a3b..0005d8c98 100644 --- a/crates/state/src/values/font.rs +++ b/crates/state/src/values/font.rs @@ -9,7 +9,7 @@ use crate::{ impl Parse for TextAlign { fn parse(parser: &mut Parser) -> Result { parser.consume_map(|token| { - token.as_string().and_then(|value| match value { + token.try_as_str().and_then(|value| match value { "center" => Some(TextAlign::Center), "justify" => Some(TextAlign::Justify), "start" => Some(TextAlign::Start), @@ -25,7 +25,7 @@ impl Parse for TextAlign { impl Parse for Slant { fn parse(parser: &mut Parser) -> Result { parser.consume_map(|token| { - token.as_string().and_then(|value| match value { + token.try_as_str().and_then(|value| match value { "upright" => Some(Slant::Upright), "italic" => Some(Slant::Italic), "oblique" => Some(Slant::Oblique), @@ -38,7 +38,7 @@ impl Parse for Slant { impl Parse for Width { fn parse(parser: &mut Parser) -> Result { parser.consume_map(|token| { - token.as_string().and_then(|value| match value { + token.try_as_str().and_then(|value| match value { "ultra-condensed" => Some(Width::ULTRA_CONDENSED), "extra-condensed" => Some(Width::EXTRA_CONDENSED), "condensed" => Some(Width::CONDENSED), @@ -62,8 +62,8 @@ impl Parse for Weight { // version. In this case it would be font_weight: "50". fn parse(parser: &mut Parser) -> Result { parser.consume_map(|token| { - if token.is_number() { - token.as_number().and_then(|value| match value { + if token.is_i64() { + token.try_as_i64().and_then(|value| match value { 50 => Some(Weight::INVISIBLE), 100 => Some(Weight::THIN), 200 => Some(Weight::EXTRA_LIGHT), @@ -78,7 +78,7 @@ impl Parse for Weight { _ => None, }) } else { - token.as_string().and_then(|value| match value { + token.try_as_str().and_then(|value| match value { "invisible" => Some(Weight::INVISIBLE), "thin" => Some(Weight::THIN), "extra-light" => Some(Weight::EXTRA_LIGHT), @@ -126,7 +126,7 @@ impl TextOverflow { impl Parse for TextOverflow { fn parse(parser: &mut Parser) -> Result { parser.consume_map(|token| { - token.as_string().map(|value| match value { + token.try_as_str().map(|value| match value { "ellipsis" => TextOverflow::Ellipsis, "clip" => TextOverflow::Clip, value => TextOverflow::Custom(value.to_string()), diff --git a/crates/state/src/values/gaps.rs b/crates/state/src/values/gaps.rs index 2a9991efd..1e33ba05e 100644 --- a/crates/state/src/values/gaps.rs +++ b/crates/state/src/values/gaps.rs @@ -12,17 +12,17 @@ impl Parse for Gaps { let mut paddings = Gaps::default(); let value = - parser.consume_if(|token| token == &Token::ident("none") || token.is_integer())?; + parser.consume_if(|token| token == &Token::ident("none") || token.is_i64_or_f32())?; if value == Token::ident("none") { return Ok(paddings); } match ( - value.into_float(), - parser.consume_map(Token::as_float).ok(), - parser.consume_map(Token::as_float).ok(), - parser.consume_map(Token::as_float).ok(), + value.into_f32(), + parser.consume_map(Token::try_as_f32).ok(), + parser.consume_map(Token::try_as_f32).ok(), + parser.consume_map(Token::try_as_f32).ok(), ) { // Same in each directions (value, None, None, None) => { diff --git a/crates/state/src/values/gradient.rs b/crates/state/src/values/gradient.rs index ab66dfc47..710d69da7 100644 --- a/crates/state/src/values/gradient.rs +++ b/crates/state/src/values/gradient.rs @@ -23,7 +23,7 @@ pub struct GradientStop { impl Parse for GradientStop { fn parse(parser: &mut Parser) -> Result { let color = Color::parse(parser)?; - let offset = (parser.consume_map(Token::as_float)? / 100.0).clamp(0.0, 1.0); + let offset = (parser.consume_map(Token::try_as_f32)? / 100.0).clamp(0.0, 1.0); parser.consume(&Token::Percent)?; @@ -79,7 +79,7 @@ impl Parse for LinearGradient { parser.consume(&Token::ParenOpen)?; let mut gradient = LinearGradient { - angle: if let Some(angle) = parser.next_if(Token::is_number).map(Token::into_float) { + angle: if let Some(angle) = parser.next_if(Token::is_i64).map(Token::into_f32) { parser.consume(&Token::ident("deg"))?; parser.consume(&Token::Comma)?; diff --git a/crates/state/src/values/highlight.rs b/crates/state/src/values/highlight.rs index 94bf235e4..2d758a036 100644 --- a/crates/state/src/values/highlight.rs +++ b/crates/state/src/values/highlight.rs @@ -16,7 +16,7 @@ pub enum HighlightMode { impl Parse for HighlightMode { fn parse(parser: &mut Parser) -> Result { parser.consume_map(|value| { - value.as_string().and_then(|value| match value { + value.try_as_str().and_then(|value| match value { "expanded" => Some(Self::Expanded), "fit" => Some(Self::Fit), _ => None, diff --git a/crates/state/src/values/overflow.rs b/crates/state/src/values/overflow.rs index 82f6e575b..108e951b2 100644 --- a/crates/state/src/values/overflow.rs +++ b/crates/state/src/values/overflow.rs @@ -16,7 +16,7 @@ pub enum OverflowMode { impl Parse for OverflowMode { fn parse(parser: &mut Parser) -> Result { parser.consume_map(|value| { - value.as_string().and_then(|value| match value { + value.try_as_str().and_then(|value| match value { "clip" => Some(Self::Clip), "none" => Some(Self::None), _ => None, diff --git a/crates/state/src/values/position.rs b/crates/state/src/values/position.rs index a120fd02b..9f1041bf9 100644 --- a/crates/state/src/values/position.rs +++ b/crates/state/src/values/position.rs @@ -9,7 +9,7 @@ use crate::{ impl Parse for Position { fn parse(parser: &mut Parser) -> Result { parser.consume_map(|value| { - value.as_string().and_then(|value| match value { + value.try_as_str().and_then(|value| match value { "absolute" => Some(Self::new_absolute()), "stacked" => Some(Self::Stacked), _ => None, diff --git a/crates/state/src/values/shadow.rs b/crates/state/src/values/shadow.rs index ca9650984..e8e93d386 100644 --- a/crates/state/src/values/shadow.rs +++ b/crates/state/src/values/shadow.rs @@ -37,11 +37,11 @@ impl Parse for Shadow { shadow.position = ShadowPosition::Inset; } - shadow.x = parser.consume_map(Token::as_float)?; - shadow.y = parser.consume_map(Token::as_float)?; - shadow.blur = parser.consume_map(Token::as_float)?; + shadow.x = parser.consume_map(Token::try_as_f32)?; + shadow.y = parser.consume_map(Token::try_as_f32)?; + shadow.blur = parser.consume_map(Token::try_as_f32)?; - if let Ok(spread) = parser.consume_map(Token::as_float) { + if let Ok(spread) = parser.consume_map(Token::try_as_f32) { shadow.spread = spread; } diff --git a/crates/state/src/values/size.rs b/crates/state/src/values/size.rs index 108cff15f..7a56c65a1 100644 --- a/crates/state/src/values/size.rs +++ b/crates/state/src/values/size.rs @@ -15,11 +15,11 @@ use crate::{ impl Parse for Size { fn parse(parser: &mut Parser) -> Result { - let value = parser.consume_if(|token| token.is_ident() || token.is_integer())?; + let value = parser.consume_if(|token| token.is_ident() || token.is_i64_or_f32())?; if value.is_ident() { value - .as_string() + .try_as_str() .and_then(|value| match value { "auto" => Some(Self::Inner), "fill" => Some(Self::Fill), @@ -31,7 +31,7 @@ impl Parse for Size { }) .ok_or(ParseError) } else { - let value = value.into_float(); + let value = value.into_f32(); Ok(if parser.try_consume(&Token::Percent) { Size::Percentage(Length::new(value)) @@ -50,14 +50,14 @@ pub fn parse_calc(parser: &mut Parser) -> Result, ParseE let mut calcs = vec![]; while let Ok(value) = parser.consume_if(|token| { - token.is_integer() + token.is_i64_or_f32() || matches!( token, Token::Plus | Token::Minus | Token::Slash | Token::Star ) }) { - if value.is_integer() { - let value = value.into_float(); + if value.is_i64_or_f32() { + let value = value.into_f32(); calcs.push(if parser.try_consume(&Token::Percent) { DynamicCalculation::Percentage(value) diff --git a/crates/state/src/values/text_shadow.rs b/crates/state/src/values/text_shadow.rs index 233f4b45a..0e4762340 100644 --- a/crates/state/src/values/text_shadow.rs +++ b/crates/state/src/values/text_shadow.rs @@ -12,11 +12,11 @@ impl Parse for TextShadow { fn parse(parser: &mut Parser) -> Result { Ok(TextShadow { offset: ( - parser.consume_map(Token::as_float)?, - parser.consume_map(Token::as_float)?, + parser.consume_map(Token::try_as_f32)?, + parser.consume_map(Token::try_as_f32)?, ) .into(), - blur_sigma: parser.consume_map(Token::as_float)? as f64 / 2.0, + blur_sigma: parser.consume_map(Token::try_as_f32)? as f64 / 2.0, color: Color::parse(parser)?, }) } diff --git a/crates/state/src/viewport.rs b/crates/state/src/viewport.rs index fab1d0b34..ed885b3a7 100644 --- a/crates/state/src/viewport.rs +++ b/crates/state/src/viewport.rs @@ -15,12 +15,10 @@ use freya_native_core_macro::partial_derive_state; use crate::{ CustomAttributeValues, - Lexer, OverflowMode, Parse, ParseAttribute, ParseError, - Parser, }; #[derive(Default, PartialEq, Clone, Debug, Component)] @@ -39,9 +37,7 @@ impl ParseAttribute for ViewportState { match attr.attribute { AttributeName::Overflow => { if let Some(value) = attr.value.as_text() { - let mut parser = Parser::new(Lexer::parse(value)); - - self.overflow = OverflowMode::parse(&mut parser)?; + self.overflow = OverflowMode::parse_value(value)?; } } _ => {} From bf5cd55cbb5dee3bfcb686753afe3a0c24848fe1 Mon Sep 17 00:00:00 2001 From: Savchenko Ivan <73419411+Aiving@users.noreply.github.com> Date: Mon, 15 Jul 2024 12:32:00 +0000 Subject: [PATCH 03/24] feat: add `try_as_u8` function for `Token` chore: change parsing api chore: change color parsing --- crates/components/src/graph.rs | 2 +- crates/hooks/src/use_animation.rs | 6 +- crates/hooks/tests/use_animation.rs | 2 +- crates/renderer/src/config.rs | 2 +- crates/state/src/cursor.rs | 8 +-- crates/state/src/font_style.rs | 20 +++---- crates/state/src/layout.rs | 24 ++++---- crates/state/src/lexing.rs | 8 +++ crates/state/src/parsing.rs | 12 ++-- crates/state/src/style.rs | 12 ++-- crates/state/src/values/alignment.rs | 2 +- crates/state/src/values/border.rs | 6 +- crates/state/src/values/color.rs | 73 +++++++++-------------- crates/state/src/values/content.rs | 2 +- crates/state/src/values/corner_radius.rs | 2 +- crates/state/src/values/cursor.rs | 2 +- crates/state/src/values/decoration.rs | 4 +- crates/state/src/values/fill.rs | 6 +- crates/state/src/values/font.rs | 10 ++-- crates/state/src/values/gaps.rs | 2 +- crates/state/src/values/gradient.rs | 8 +-- crates/state/src/values/highlight.rs | 2 +- crates/state/src/values/overflow.rs | 2 +- crates/state/src/values/position.rs | 2 +- crates/state/src/values/shadow.rs | 4 +- crates/state/src/values/size.rs | 2 +- crates/state/src/values/text_shadow.rs | 4 +- crates/state/src/viewport.rs | 2 +- crates/state/tests/parse_align.rs | 12 ++-- crates/state/tests/parse_border.rs | 18 +++--- crates/state/tests/parse_color.rs | 36 +++++------ crates/state/tests/parse_corner_radius.rs | 14 ++--- crates/state/tests/parse_decoration.rs | 20 +++---- crates/state/tests/parse_display.rs | 14 ++--- crates/state/tests/parse_gaps.rs | 8 +-- crates/state/tests/parse_gradient.rs | 30 +++++----- crates/state/tests/parse_highlight.rs | 6 +- crates/state/tests/parse_shadows.rs | 8 +-- crates/state/tests/parse_size.rs | 8 +-- crates/state/tests/parse_text_shadow.rs | 2 +- 40 files changed, 199 insertions(+), 208 deletions(-) diff --git a/crates/components/src/graph.rs b/crates/components/src/graph.rs index 02accb0f7..e73f4c2ac 100644 --- a/crates/components/src/graph.rs +++ b/crates/components/src/graph.rs @@ -106,7 +106,7 @@ pub fn Graph(props: GraphProps) -> Element { paint.set_anti_alias(true); paint.set_style(PaintStyle::Fill); - paint.set_color(Color::parse_value(&line.color).unwrap()); + paint.set_color(Color::parse(&line.color).unwrap()); paint.set_stroke_width(3.0); let mut previous_x = None; diff --git a/crates/hooks/src/use_animation.rs b/crates/hooks/src/use_animation.rs index b296d4761..35371d945 100644 --- a/crates/hooks/src/use_animation.rs +++ b/crates/hooks/src/use_animation.rs @@ -133,13 +133,13 @@ pub struct AnimColor { impl AnimColor { pub fn new(origin: &str, destination: &str) -> Self { Self { - origin: Color::parse_value(origin).unwrap(), - destination: Color::parse_value(destination).unwrap(), + origin: Color::parse(origin).unwrap(), + destination: Color::parse(destination).unwrap(), time: Duration::default(), ease: Ease::default(), function: Function::default(), - value: Color::parse_value(origin).unwrap(), + value: Color::parse(origin).unwrap(), } } diff --git a/crates/hooks/tests/use_animation.rs b/crates/hooks/tests/use_animation.rs index 2b559aa0e..25be6d45d 100644 --- a/crates/hooks/tests/use_animation.rs +++ b/crates/hooks/tests/use_animation.rs @@ -168,7 +168,7 @@ pub async fn animate_color() { assert_eq!( utils.root().get(0).style().background, - Fill::Color(Color::parse_value("rgb(50, 100, 200)").unwrap()) + Fill::Color(Color::parse("rgb(50, 100, 200)").unwrap()) ); } diff --git a/crates/renderer/src/config.rs b/crates/renderer/src/config.rs index bfccd095c..05dc9d50f 100644 --- a/crates/renderer/src/config.rs +++ b/crates/renderer/src/config.rs @@ -155,7 +155,7 @@ impl<'a, T: Clone> LaunchConfig<'a, T> { /// Specify the Window background color. pub fn with_background(mut self, background: &str) -> Self { - self.window_config.background = Color::parse_value(background).unwrap_or(Color::WHITE); + self.window_config.background = Color::parse(background).unwrap_or(Color::WHITE); self } diff --git a/crates/state/src/cursor.rs b/crates/state/src/cursor.rs index e9e1baf15..c1c58062f 100644 --- a/crates/state/src/cursor.rs +++ b/crates/state/src/cursor.rs @@ -69,12 +69,12 @@ impl ParseAttribute for CursorState { } AttributeName::CursorColor => { if let Some(value) = attr.value.as_text() { - self.color = Color::parse_value(value)?; + self.color = Color::parse(value)?; } } AttributeName::CursorMode => { if let Some(value) = attr.value.as_text() { - self.mode = CursorMode::parse_value(value)?; + self.mode = CursorMode::parse(value)?; } } AttributeName::CursorId => { @@ -91,12 +91,12 @@ impl ParseAttribute for CursorState { } AttributeName::HighlightColor => { if let Some(value) = attr.value.as_text() { - self.highlight_color = Color::parse_value(value)? + self.highlight_color = Color::parse(value)? } } AttributeName::HighlightMode => { if let Some(value) = attr.value.as_text() { - self.highlight_mode = HighlightMode::parse_value(value)? + self.highlight_mode = HighlightMode::parse(value)? } } AttributeName::CursorReference => { diff --git a/crates/state/src/font_style.rs b/crates/state/src/font_style.rs index c526a4d46..65948fe18 100644 --- a/crates/state/src/font_style.rs +++ b/crates/state/src/font_style.rs @@ -113,13 +113,13 @@ impl ParseAttribute for FontStyleState { // Make an exception for the "inherit" as in this case we don't want to pass // a color at all but use the inherited one. if value != "inherit" { - self.color = Color::parse_value(value)? + self.color = Color::parse(value)? } } } AttributeName::TextShadow => { if let Some(value) = attr.value.as_text() { - self.text_shadows = TextShadow::parse_values(value, &Token::Comma)?; + self.text_shadows = TextShadow::parse_with_separator(value, &Token::Comma)?; } } AttributeName::FontFamily => { @@ -148,7 +148,7 @@ impl ParseAttribute for FontStyleState { } AttributeName::TextAlign => { if let Some(value) = attr.value.as_text() { - self.text_align = TextAlign::parse_value(value)? + self.text_align = TextAlign::parse(value)? } } AttributeName::MaxLines => { @@ -158,37 +158,37 @@ impl ParseAttribute for FontStyleState { } AttributeName::TextOverflow => { if let Some(value) = attr.value.as_text() { - self.text_overflow = TextOverflow::parse_value(value)? + self.text_overflow = TextOverflow::parse(value)? } } AttributeName::FontStyle => { if let Some(value) = attr.value.as_text() { - self.font_slant = Slant::parse_value(value)? + self.font_slant = Slant::parse(value)? } } AttributeName::FontWeight => { if let Some(value) = attr.value.as_text() { - self.font_weight = Weight::parse_value(value)? + self.font_weight = Weight::parse(value)? } } AttributeName::FontWidth => { if let Some(value) = attr.value.as_text() { - self.font_width = Width::parse_value(value)? + self.font_width = Width::parse(value)? } } AttributeName::Decoration => { if let Some(value) = attr.value.as_text() { - self.decoration.ty = TextDecoration::parse_value(value)? + self.decoration.ty = TextDecoration::parse(value)? } } AttributeName::DecorationStyle => { if let Some(value) = attr.value.as_text() { - self.decoration.style = TextDecorationStyle::parse_value(value)? + self.decoration.style = TextDecorationStyle::parse(value)? } } AttributeName::DecorationColor => { if let Some(value) = attr.value.as_text() { - self.decoration.color = Color::parse_value(value)? + self.decoration.color = Color::parse(value)? } else { self.decoration.color = self.color; } diff --git a/crates/state/src/layout.rs b/crates/state/src/layout.rs index b25d40aa4..cd422f166 100644 --- a/crates/state/src/layout.rs +++ b/crates/state/src/layout.rs @@ -58,42 +58,42 @@ impl ParseAttribute for LayoutState { match attr.attribute { AttributeName::Width => { if let Some(value) = attr.value.as_text() { - self.width = Size::parse_value(value)?; + self.width = Size::parse(value)?; } } AttributeName::Height => { if let Some(value) = attr.value.as_text() { - self.height = Size::parse_value(value)?; + self.height = Size::parse(value)?; } } AttributeName::MinHeight => { if let Some(value) = attr.value.as_text() { - self.minimum_height = Size::parse_value(value)?; + self.minimum_height = Size::parse(value)?; } } AttributeName::MinWidth => { if let Some(value) = attr.value.as_text() { - self.minimum_width = Size::parse_value(value)?; + self.minimum_width = Size::parse(value)?; } } AttributeName::MaxHeight => { if let Some(value) = attr.value.as_text() { - self.maximum_height = Size::parse_value(value)?; + self.maximum_height = Size::parse(value)?; } } AttributeName::MaxWidth => { if let Some(value) = attr.value.as_text() { - self.maximum_width = Size::parse_value(value)?; + self.maximum_width = Size::parse(value)?; } } AttributeName::Padding => { if let Some(value) = attr.value.as_text() { - self.padding = Gaps::parse_value(value)?; + self.padding = Gaps::parse(value)?; } } AttributeName::Margin => { if let Some(value) = attr.value.as_text() { - self.margin = Gaps::parse_value(value)?; + self.margin = Gaps::parse(value)?; } } AttributeName::Direction => { @@ -117,18 +117,18 @@ impl ParseAttribute for LayoutState { } AttributeName::MainAlign => { if let Some(value) = attr.value.as_text() { - self.main_alignment = Alignment::parse_value(value)?; + self.main_alignment = Alignment::parse(value)?; } } AttributeName::CrossAlign => { if let Some(value) = attr.value.as_text() { - self.cross_alignment = Alignment::parse_value(value)?; + self.cross_alignment = Alignment::parse(value)?; } } AttributeName::Position => { if let Some(value) = attr.value.as_text() { if self.position.is_empty() { - self.position = Position::parse_value(value)?; + self.position = Position::parse(value)?; } } } @@ -158,7 +158,7 @@ impl ParseAttribute for LayoutState { } AttributeName::Content => { if let Some(value) = attr.value.as_text() { - self.content = Content::parse_value(value)?; + self.content = Content::parse(value)?; } } AttributeName::Reference => { diff --git a/crates/state/src/lexing.rs b/crates/state/src/lexing.rs index 511ef3d4b..df24dd934 100644 --- a/crates/state/src/lexing.rs +++ b/crates/state/src/lexing.rs @@ -97,6 +97,14 @@ impl Token { None } } + + pub fn try_as_u8(&self) -> Option { + if let Token::Number(value) = self { + u8::try_from(*value).ok() + } else { + None + } + } } pub struct Lexer; diff --git a/crates/state/src/parsing.rs b/crates/state/src/parsing.rs index 5a17b460e..3d5011769 100644 --- a/crates/state/src/parsing.rs +++ b/crates/state/src/parsing.rs @@ -108,12 +108,12 @@ impl Parser { // FromStr but we own it so we can impl it on torin and skia_safe types. pub trait Parse: Sized { - fn parse(parser: &mut Parser) -> Result; + fn from_parser(parser: &mut Parser) -> Result; - fn parse_value(value: &str) -> Result { + fn parse(value: &str) -> Result { let mut parser = Parser::new(Lexer::parse(value)); - let value = Self::parse(&mut parser); + let value = Self::from_parser(&mut parser); if parser.tokens.len() > 0 { Err(ParseError) @@ -122,13 +122,13 @@ pub trait Parse: Sized { } } - fn parse_values(value: &str, separator: &Token) -> Result, ParseError> { + fn parse_with_separator(value: &str, separator: &Token) -> Result, ParseError> { let mut parser = Parser::new(Lexer::parse(value)); - let mut values = vec![Self::parse(&mut parser)?]; + let mut values = vec![Self::from_parser(&mut parser)?]; while parser.try_consume(separator) { - values.push(Self::parse(&mut parser)?); + values.push(Self::from_parser(&mut parser)?); } if parser.tokens.len() > 0 { diff --git a/crates/state/src/style.rs b/crates/state/src/style.rs index d6d7bcdf9..506ab24b9 100644 --- a/crates/state/src/style.rs +++ b/crates/state/src/style.rs @@ -54,12 +54,12 @@ impl ParseAttribute for StyleState { return Ok(()); } - self.background = Fill::parse_value(value)?; + self.background = Fill::parse(value)?; } } AttributeName::Border => { if let Some(value) = attr.value.as_text() { - let mut border = Border::parse_value(value)?; + let mut border = Border::parse(value)?; border.alignment = self.border.alignment; @@ -68,17 +68,17 @@ impl ParseAttribute for StyleState { } AttributeName::BorderAlign => { if let Some(value) = attr.value.as_text() { - self.border.alignment = BorderAlignment::parse_value(value)?; + self.border.alignment = BorderAlignment::parse(value)?; } } AttributeName::Shadow => { if let Some(value) = attr.value.as_text() { - self.shadows = Shadow::parse_values(value, &Token::Comma)?; + self.shadows = Shadow::parse_with_separator(value, &Token::Comma)?; } } AttributeName::CornerRadius => { if let Some(value) = attr.value.as_text() { - let mut radius = CornerRadius::parse_value(value)?; + let mut radius = CornerRadius::parse(value)?; radius.smoothing = self.corner_radius.smoothing; @@ -115,7 +115,7 @@ impl ParseAttribute for StyleState { } AttributeName::Overflow => { if let Some(value) = attr.value.as_text() { - self.overflow = OverflowMode::parse_value(value)?; + self.overflow = OverflowMode::parse(value)?; } } AttributeName::Opacity => { diff --git a/crates/state/src/values/alignment.rs b/crates/state/src/values/alignment.rs index 67d7b9c9c..066d28727 100644 --- a/crates/state/src/values/alignment.rs +++ b/crates/state/src/values/alignment.rs @@ -7,7 +7,7 @@ use crate::{ }; impl Parse for Alignment { - fn parse(parser: &mut Parser) -> Result { + fn from_parser(parser: &mut Parser) -> Result { parser.consume_map(|value| { value.try_as_str().and_then(|value| match value { "start" => Some(Self::Start), diff --git a/crates/state/src/values/border.rs b/crates/state/src/values/border.rs index 167a5cee8..7b5359692 100644 --- a/crates/state/src/values/border.rs +++ b/crates/state/src/values/border.rs @@ -34,7 +34,7 @@ pub enum BorderAlignment { } impl Parse for BorderAlignment { - fn parse(parser: &mut Parser) -> Result { + fn from_parser(parser: &mut Parser) -> Result { parser.consume_map(|value| { value.try_as_str().and_then(|value| match value { "inner" => Some(BorderAlignment::Inner), @@ -66,7 +66,7 @@ impl fmt::Display for BorderStyle { } impl Parse for Border { - fn parse(parser: &mut Parser) -> Result { + fn from_parser(parser: &mut Parser) -> Result { if parser.try_consume(&Token::ident("none")) { return Ok(Self::default()); } @@ -80,7 +80,7 @@ impl Parse for Border { _ => None, }) })?, - fill: Fill::parse(parser)?, + fill: Fill::from_parser(parser)?, alignment: BorderAlignment::default(), }) } diff --git a/crates/state/src/values/color.rs b/crates/state/src/values/color.rs index c3593f6ea..f9922f4e3 100644 --- a/crates/state/src/values/color.rs +++ b/crates/state/src/values/color.rs @@ -15,40 +15,29 @@ pub trait DisplayColor { } impl Parse for Color { - fn parse(parser: &mut Parser) -> Result { - parser - .consume_one_of(&[ - Token::Pound, - Token::ident("rgb"), - Token::ident("hsl"), - Token::ident("red"), - Token::ident("green"), - Token::ident("blue"), - Token::ident("yellow"), - Token::ident("black"), - Token::ident("gray"), - Token::ident("white"), - Token::ident("orange"), - Token::ident("transparent"), - ]) - .and_then(|token| match token { - Token::Pound => parse_hex_color(parser), - Token::Ident(ref value) => match value.as_str() { - "rgb" => parse_rgb(parser), - "hsl" => parse_hsl(parser), - "red" => Ok(Color::RED), - "green" => Ok(Color::GREEN), - "blue" => Ok(Color::BLUE), - "yellow" => Ok(Color::YELLOW), - "black" => Ok(Color::BLACK), - "gray" => Ok(Color::GRAY), - "white" => Ok(Color::WHITE), - "orange" => Ok(Color::from_rgb(255, 165, 0)), - "transparent" => Ok(Color::TRANSPARENT), - _ => unreachable!(), - }, - _ => unreachable!(), + fn from_parser(parser: &mut Parser) -> Result { + if parser.try_consume(&Token::Pound) { + parse_hex_color(parser) + } else if parser.try_consume(&Token::ident("rgb")) { + parse_rgb(parser) + } else if parser.try_consume(&Token::ident("hsl")) { + parse_hsl(parser) + } else { + parser.consume_map(|token| { + token.try_as_str().and_then(|value| match value { + "red" => Some(Color::RED), + "green" => Some(Color::GREEN), + "blue" => Some(Color::BLUE), + "yellow" => Some(Color::YELLOW), + "black" => Some(Color::BLACK), + "gray" => Some(Color::GRAY), + "white" => Some(Color::WHITE), + "orange" => Some(Color::from_rgb(255, 165, 0)), + "transparent" => Some(Color::TRANSPARENT), + _ => None, + }) }) + } } } @@ -85,29 +74,25 @@ impl DisplayColor for Color { } } -fn parse_number_as>(token: &Token) -> Option { - token.try_as_i64().and_then(|value| T::try_from(value).ok()) -} - fn parse_rgb(parser: &mut Parser) -> Result { parser.consume(&Token::ParenOpen)?; - let red = parser.consume_map(parse_number_as)?; + let red = parser.consume_map(Token::try_as_u8)?; parser.consume(&Token::Comma)?; - let green = parser.consume_map(parse_number_as)?; + let green = parser.consume_map(Token::try_as_u8)?; parser.consume(&Token::Comma)?; - let blue = parser.consume_map(parse_number_as)?; + let blue = parser.consume_map(Token::try_as_u8)?; let color = if parser.try_consume(&Token::Comma) { - let alpha = parser.consume_if(Token::is_i64_or_f32).and_then(|token| { - if token.is_i64() { - u8::try_from(token.into_i64()).map_err(|_| ParseError) + let alpha = parser.consume_map(|token| { + if let Some(value) = token.try_as_f32() { + Some((value * 255.0).round().clamp(0.0, 255.0) as u8) } else { - Ok((token.into_f32() * 255.0).round().clamp(0.0, 255.0) as u8) + token.try_as_u8() } })?; diff --git a/crates/state/src/values/content.rs b/crates/state/src/values/content.rs index fb9daa7d2..50bc6c4af 100644 --- a/crates/state/src/values/content.rs +++ b/crates/state/src/values/content.rs @@ -7,7 +7,7 @@ use crate::{ }; impl Parse for Content { - fn parse(parser: &mut Parser) -> Result { + fn from_parser(parser: &mut Parser) -> Result { parser.consume_map(|value| { value.try_as_str().and_then(|value| match value { "normal" => Some(Self::Normal), diff --git a/crates/state/src/values/corner_radius.rs b/crates/state/src/values/corner_radius.rs index e614d3f97..e21a9346b 100644 --- a/crates/state/src/values/corner_radius.rs +++ b/crates/state/src/values/corner_radius.rs @@ -201,7 +201,7 @@ impl CornerRadius { } impl Parse for CornerRadius { - fn parse(parser: &mut Parser) -> Result { + fn from_parser(parser: &mut Parser) -> Result { let mut radius = CornerRadius::default(); match ( diff --git a/crates/state/src/values/cursor.rs b/crates/state/src/values/cursor.rs index c4dd507d2..63901add3 100644 --- a/crates/state/src/values/cursor.rs +++ b/crates/state/src/values/cursor.rs @@ -13,7 +13,7 @@ pub enum CursorMode { } impl Parse for CursorMode { - fn parse(parser: &mut Parser) -> Result { + fn from_parser(parser: &mut Parser) -> Result { parser.consume_map(|value| { value.try_as_str().and_then(|value| match value { "none" => Some(Self::None), diff --git a/crates/state/src/values/decoration.rs b/crates/state/src/values/decoration.rs index b3f0fba56..5a8395beb 100644 --- a/crates/state/src/values/decoration.rs +++ b/crates/state/src/values/decoration.rs @@ -7,7 +7,7 @@ use crate::{ }; impl Parse for TextDecoration { - fn parse(parser: &mut Parser) -> Result { + fn from_parser(parser: &mut Parser) -> Result { let mut decoration = Self::default(); while let Ok(value) = parser.consume_map(|token| { @@ -27,7 +27,7 @@ impl Parse for TextDecoration { } impl Parse for TextDecorationStyle { - fn parse(parser: &mut Parser) -> Result { + fn from_parser(parser: &mut Parser) -> Result { parser.consume_map(|token| { token.try_as_str().and_then(|value| match value { "solid" => Some(Self::Solid), diff --git a/crates/state/src/values/fill.rs b/crates/state/src/values/fill.rs index 2275ddd59..9f09d6d02 100644 --- a/crates/state/src/values/fill.rs +++ b/crates/state/src/values/fill.rs @@ -31,10 +31,10 @@ impl From for Fill { } impl Parse for Fill { - fn parse(parser: &mut Parser) -> Result { - LinearGradient::parse(parser) + fn from_parser(parser: &mut Parser) -> Result { + LinearGradient::from_parser(parser) .map(Self::LinearGradient) - .or(Color::parse(parser).map(Self::Color)) + .or(Color::from_parser(parser).map(Self::Color)) } } diff --git a/crates/state/src/values/font.rs b/crates/state/src/values/font.rs index 0005d8c98..f34501ba1 100644 --- a/crates/state/src/values/font.rs +++ b/crates/state/src/values/font.rs @@ -7,7 +7,7 @@ use crate::{ }; impl Parse for TextAlign { - fn parse(parser: &mut Parser) -> Result { + fn from_parser(parser: &mut Parser) -> Result { parser.consume_map(|token| { token.try_as_str().and_then(|value| match value { "center" => Some(TextAlign::Center), @@ -23,7 +23,7 @@ impl Parse for TextAlign { } impl Parse for Slant { - fn parse(parser: &mut Parser) -> Result { + fn from_parser(parser: &mut Parser) -> Result { parser.consume_map(|token| { token.try_as_str().and_then(|value| match value { "upright" => Some(Slant::Upright), @@ -36,7 +36,7 @@ impl Parse for Slant { } impl Parse for Width { - fn parse(parser: &mut Parser) -> Result { + fn from_parser(parser: &mut Parser) -> Result { parser.consume_map(|token| { token.try_as_str().and_then(|value| match value { "ultra-condensed" => Some(Width::ULTRA_CONDENSED), @@ -60,7 +60,7 @@ impl Parse for Weight { // CSS has one deviation from this spec, which uses the value "950" for extra_black. // skia_safe also has an "invisible" weight smaller than the thin weight, which could fall under CSS's interpretation of OpenType's // version. In this case it would be font_weight: "50". - fn parse(parser: &mut Parser) -> Result { + fn from_parser(parser: &mut Parser) -> Result { parser.consume_map(|token| { if token.is_i64() { token.try_as_i64().and_then(|value| match value { @@ -124,7 +124,7 @@ impl TextOverflow { } impl Parse for TextOverflow { - fn parse(parser: &mut Parser) -> Result { + fn from_parser(parser: &mut Parser) -> Result { parser.consume_map(|token| { token.try_as_str().map(|value| match value { "ellipsis" => TextOverflow::Ellipsis, diff --git a/crates/state/src/values/gaps.rs b/crates/state/src/values/gaps.rs index 1e33ba05e..626784a8a 100644 --- a/crates/state/src/values/gaps.rs +++ b/crates/state/src/values/gaps.rs @@ -8,7 +8,7 @@ use crate::{ }; impl Parse for Gaps { - fn parse(parser: &mut Parser) -> Result { + fn from_parser(parser: &mut Parser) -> Result { let mut paddings = Gaps::default(); let value = diff --git a/crates/state/src/values/gradient.rs b/crates/state/src/values/gradient.rs index 710d69da7..5d9a2424c 100644 --- a/crates/state/src/values/gradient.rs +++ b/crates/state/src/values/gradient.rs @@ -21,8 +21,8 @@ pub struct GradientStop { } impl Parse for GradientStop { - fn parse(parser: &mut Parser) -> Result { - let color = Color::parse(parser)?; + fn from_parser(parser: &mut Parser) -> Result { + let color = Color::from_parser(parser)?; let offset = (parser.consume_map(Token::try_as_f32)? / 100.0).clamp(0.0, 1.0); parser.consume(&Token::Percent)?; @@ -74,7 +74,7 @@ impl LinearGradient { } impl Parse for LinearGradient { - fn parse(parser: &mut Parser) -> Result { + fn from_parser(parser: &mut Parser) -> Result { parser.consume(&Token::ident("linear-gradient"))?; parser.consume(&Token::ParenOpen)?; @@ -95,7 +95,7 @@ impl Parse for LinearGradient { parser.consume(&Token::Comma)?; } - gradient.stops.push(GradientStop::parse(parser)?); + gradient.stops.push(GradientStop::from_parser(parser)?); } parser.consume(&Token::ParenClose)?; diff --git a/crates/state/src/values/highlight.rs b/crates/state/src/values/highlight.rs index 2d758a036..38e289a86 100644 --- a/crates/state/src/values/highlight.rs +++ b/crates/state/src/values/highlight.rs @@ -14,7 +14,7 @@ pub enum HighlightMode { } impl Parse for HighlightMode { - fn parse(parser: &mut Parser) -> Result { + fn from_parser(parser: &mut Parser) -> Result { parser.consume_map(|value| { value.try_as_str().and_then(|value| match value { "expanded" => Some(Self::Expanded), diff --git a/crates/state/src/values/overflow.rs b/crates/state/src/values/overflow.rs index 108e951b2..c241a621b 100644 --- a/crates/state/src/values/overflow.rs +++ b/crates/state/src/values/overflow.rs @@ -14,7 +14,7 @@ pub enum OverflowMode { } impl Parse for OverflowMode { - fn parse(parser: &mut Parser) -> Result { + fn from_parser(parser: &mut Parser) -> Result { parser.consume_map(|value| { value.try_as_str().and_then(|value| match value { "clip" => Some(Self::Clip), diff --git a/crates/state/src/values/position.rs b/crates/state/src/values/position.rs index 9f1041bf9..236e7ed95 100644 --- a/crates/state/src/values/position.rs +++ b/crates/state/src/values/position.rs @@ -7,7 +7,7 @@ use crate::{ }; impl Parse for Position { - fn parse(parser: &mut Parser) -> Result { + fn from_parser(parser: &mut Parser) -> Result { parser.consume_map(|value| { value.try_as_str().and_then(|value| match value { "absolute" => Some(Self::new_absolute()), diff --git a/crates/state/src/values/shadow.rs b/crates/state/src/values/shadow.rs index e8e93d386..adda40d53 100644 --- a/crates/state/src/values/shadow.rs +++ b/crates/state/src/values/shadow.rs @@ -26,7 +26,7 @@ pub struct Shadow { } impl Parse for Shadow { - fn parse(parser: &mut Parser) -> Result { + fn from_parser(parser: &mut Parser) -> Result { let mut shadow = Shadow::default(); if parser.try_consume(&Token::ident("none")) { @@ -45,7 +45,7 @@ impl Parse for Shadow { shadow.spread = spread; } - shadow.fill = Fill::parse(parser)?; + shadow.fill = Fill::from_parser(parser)?; Ok(shadow) } diff --git a/crates/state/src/values/size.rs b/crates/state/src/values/size.rs index 7a56c65a1..cbaf1f235 100644 --- a/crates/state/src/values/size.rs +++ b/crates/state/src/values/size.rs @@ -14,7 +14,7 @@ use crate::{ }; impl Parse for Size { - fn parse(parser: &mut Parser) -> Result { + fn from_parser(parser: &mut Parser) -> Result { let value = parser.consume_if(|token| token.is_ident() || token.is_i64_or_f32())?; if value.is_ident() { diff --git a/crates/state/src/values/text_shadow.rs b/crates/state/src/values/text_shadow.rs index 0e4762340..196ce1539 100644 --- a/crates/state/src/values/text_shadow.rs +++ b/crates/state/src/values/text_shadow.rs @@ -9,7 +9,7 @@ use crate::{ // Same as shadow, but no inset or spread. impl Parse for TextShadow { - fn parse(parser: &mut Parser) -> Result { + fn from_parser(parser: &mut Parser) -> Result { Ok(TextShadow { offset: ( parser.consume_map(Token::try_as_f32)?, @@ -17,7 +17,7 @@ impl Parse for TextShadow { ) .into(), blur_sigma: parser.consume_map(Token::try_as_f32)? as f64 / 2.0, - color: Color::parse(parser)?, + color: Color::from_parser(parser)?, }) } } diff --git a/crates/state/src/viewport.rs b/crates/state/src/viewport.rs index ed885b3a7..8a06cd05e 100644 --- a/crates/state/src/viewport.rs +++ b/crates/state/src/viewport.rs @@ -37,7 +37,7 @@ impl ParseAttribute for ViewportState { match attr.attribute { AttributeName::Overflow => { if let Some(value) = attr.value.as_text() { - self.overflow = OverflowMode::parse_value(value)?; + self.overflow = OverflowMode::parse(value)?; } } _ => {} diff --git a/crates/state/tests/parse_align.rs b/crates/state/tests/parse_align.rs index 9d1ae2dec..7153220ac 100644 --- a/crates/state/tests/parse_align.rs +++ b/crates/state/tests/parse_align.rs @@ -3,36 +3,36 @@ use freya_node_state::Parse; #[test] fn parse_center_text_align() { - let center = TextAlign::parse_value("center"); + let center = TextAlign::parse("center"); assert_eq!(center, Ok(TextAlign::Center)); } #[test] fn parse_end_text_align() { - let end = TextAlign::parse_value("end"); + let end = TextAlign::parse("end"); assert_eq!(end, Ok(TextAlign::End)); } #[test] fn parse_justify_text_align() { - let justify = TextAlign::parse_value("justify"); + let justify = TextAlign::parse("justify"); assert_eq!(justify, Ok(TextAlign::Justify)); } #[test] fn parse_lefttext_align() { - let left = TextAlign::parse_value("left"); + let left = TextAlign::parse("left"); assert_eq!(left, Ok(TextAlign::Left)); } #[test] fn parse_right_text_align() { - let right = TextAlign::parse_value("right"); + let right = TextAlign::parse("right"); assert_eq!(right, Ok(TextAlign::Right)); } #[test] fn parse_start_text_align() { - let start = TextAlign::parse_value("start"); + let start = TextAlign::parse("start"); assert_eq!(start, Ok(TextAlign::Start)); } diff --git a/crates/state/tests/parse_border.rs b/crates/state/tests/parse_border.rs index a8b135445..865f1e562 100644 --- a/crates/state/tests/parse_border.rs +++ b/crates/state/tests/parse_border.rs @@ -11,7 +11,7 @@ use freya_node_state::{ #[test] fn parse_basic_border() { - let border = Border::parse_value("1 solid red"); + let border = Border::parse("1 solid red"); assert_eq!( border, @@ -26,7 +26,7 @@ fn parse_basic_border() { #[test] fn parse_gradient_border() { - let shadow = Border::parse_value("1 solid linear-gradient(red 0%, blue 100%)"); + let shadow = Border::parse("1 solid linear-gradient(red 0%, blue 100%)"); assert_eq!( shadow, Ok(Border { @@ -52,10 +52,10 @@ fn parse_gradient_border() { #[test] fn parse_border_alignments() { - let inner = BorderAlignment::parse_value("inner"); - let outer = BorderAlignment::parse_value("outer"); - let center = BorderAlignment::parse_value("center"); - let invalid = BorderAlignment::parse_value("invalid"); + let inner = BorderAlignment::parse("inner"); + let outer = BorderAlignment::parse("outer"); + let center = BorderAlignment::parse("center"); + let invalid = BorderAlignment::parse("invalid"); assert_eq!(inner, Ok(BorderAlignment::Inner)); assert_eq!(outer, Ok(BorderAlignment::Outer)); @@ -65,9 +65,9 @@ fn parse_border_alignments() { #[test] fn parse_border_style() { - let solid = Border::parse_value("1 solid red"); - let invalid_style = Border::parse_value("1 rust red"); - let invalid_width = Border::parse_value("rust solid red"); + let solid = Border::parse("1 solid red"); + let invalid_style = Border::parse("1 rust red"); + let invalid_width = Border::parse("rust solid red"); assert_eq!( solid, diff --git a/crates/state/tests/parse_color.rs b/crates/state/tests/parse_color.rs index 8a3479a54..de4ae324b 100644 --- a/crates/state/tests/parse_color.rs +++ b/crates/state/tests/parse_color.rs @@ -3,53 +3,53 @@ use freya_node_state::Parse; #[test] fn parse_manual_color() { - let color = Color::parse_value("red"); + let color = Color::parse("red"); assert_eq!(color, Ok(Color::RED)); } #[test] fn parse_rgb_color() { - let color = Color::parse_value("rgb(91, 123, 57)"); + let color = Color::parse("rgb(91, 123, 57)"); assert_eq!(color, Ok(Color::from_rgb(91, 123, 57))); } #[test] fn parse_hsl_color() { - _ = Color::parse_value("hsl(28deg, 80%, 50%, 25%)").unwrap(); + _ = Color::parse("hsl(28deg, 80%, 50%, 25%)").unwrap(); } #[test] fn parse_argb_color_u8() { - let color = Color::parse_value("rgb(91, 123, 57, 127)"); + let color = Color::parse("rgb(91, 123, 57, 127)"); assert_eq!(color, Ok(Color::from_argb(127, 91, 123, 57))); } #[test] fn parse_argb_color_f32() { - let color = Color::parse_value("rgb(91, 123, 57, 0.5)"); + let color = Color::parse("rgb(91, 123, 57, 0.5)"); assert_eq!(color, Ok(Color::from_argb(128, 91, 123, 57))); } #[test] fn parse_hex_color() { - let color = Color::parse_value("#FFA500"); + let color = Color::parse("#FFA500"); assert_eq!(color, Ok(Color::from_rgb(255, 165, 0))); } #[test] fn invalid_colors() { - let incorrect_name = Color::parse_value("wow(0, 0, 0)"); - let extra_lparen = Color::parse_value("rgb((0, 0, 0)"); - let extra_rparen = Color::parse_value("rgb(0, 0, 0))"); - let missing_lparen = Color::parse_value("rgb0, 0, 0)"); - let missing_rparen = Color::parse_value("rgb(0, 0, 0"); - let missing_commas = Color::parse_value("rgb(0 0 0)"); - let extra_commas = Color::parse_value("rgb(0,, 0, 0)"); - let extra_component = Color::parse_value("rgb(0, 0, 0, 0, 0)"); - let extra_ending_commas = Color::parse_value("rgb(0, 0, 0, 0,)"); - let bad_unit = Color::parse_value("hsl(28in, 0.4, 0.25, 50%)"); - let missing_number_sign = Color::parse_value("FFA500"); - let incorrect_hex_length = Color::parse_value("#FFA0"); + let incorrect_name = Color::parse("wow(0, 0, 0)"); + let extra_lparen = Color::parse("rgb((0, 0, 0)"); + let extra_rparen = Color::parse("rgb(0, 0, 0))"); + let missing_lparen = Color::parse("rgb0, 0, 0)"); + let missing_rparen = Color::parse("rgb(0, 0, 0"); + let missing_commas = Color::parse("rgb(0 0 0)"); + let extra_commas = Color::parse("rgb(0,, 0, 0)"); + let extra_component = Color::parse("rgb(0, 0, 0, 0, 0)"); + let extra_ending_commas = Color::parse("rgb(0, 0, 0, 0,)"); + let bad_unit = Color::parse("hsl(28in, 0.4, 0.25, 50%)"); + let missing_number_sign = Color::parse("FFA500"); + let incorrect_hex_length = Color::parse("#FFA0"); assert!(incorrect_name.is_err()); assert!(extra_lparen.is_err()); diff --git a/crates/state/tests/parse_corner_radius.rs b/crates/state/tests/parse_corner_radius.rs index 96c6d5ae8..cd085ea78 100644 --- a/crates/state/tests/parse_corner_radius.rs +++ b/crates/state/tests/parse_corner_radius.rs @@ -6,7 +6,7 @@ use freya_node_state::{ #[test] fn parse_basic_corner_radius() { assert_eq!( - CornerRadius::parse_value("3"), + CornerRadius::parse("3"), Ok(CornerRadius { top_left: 3.0, top_right: 3.0, @@ -19,7 +19,7 @@ fn parse_basic_corner_radius() { #[test] fn parse_two_value_radius() { assert_eq!( - CornerRadius::parse_value("2 4"), + CornerRadius::parse("2 4"), Ok(CornerRadius { top_left: 2.0, top_right: 2.0, @@ -33,7 +33,7 @@ fn parse_two_value_radius() { #[test] fn parse_four_value_radius() { assert_eq!( - CornerRadius::parse_value("2 4 3 1"), + CornerRadius::parse("2 4 3 1"), Ok(CornerRadius { top_left: 2.0, top_right: 4.0, @@ -46,10 +46,10 @@ fn parse_four_value_radius() { #[test] fn invalid_radius() { - let extra_value = CornerRadius::parse_value("1 2 4 3 1"); - let bad_value_count = CornerRadius::parse_value("4 3 1"); - let bad_unit = CornerRadius::parse_value("4deg 3"); - let incorrect_separator = CornerRadius::parse_value("4, 3, 2, 1"); + let extra_value = CornerRadius::parse("1 2 4 3 1"); + let bad_value_count = CornerRadius::parse("4 3 1"); + let bad_unit = CornerRadius::parse("4deg 3"); + let incorrect_separator = CornerRadius::parse("4, 3, 2, 1"); assert!(extra_value.is_err()); assert!(bad_value_count.is_err()); diff --git a/crates/state/tests/parse_decoration.rs b/crates/state/tests/parse_decoration.rs index 9980bf5de..62230ddd2 100644 --- a/crates/state/tests/parse_decoration.rs +++ b/crates/state/tests/parse_decoration.rs @@ -3,38 +3,38 @@ use freya_node_state::Parse; #[test] fn parse_text_decoration() { - let underline = TextDecoration::parse_value("underline"); + let underline = TextDecoration::parse("underline"); assert_eq!(underline, Ok(TextDecoration::UNDERLINE)); - let overline = TextDecoration::parse_value("overline"); + let overline = TextDecoration::parse("overline"); assert_eq!(overline, Ok(TextDecoration::OVERLINE)); - let line_through = TextDecoration::parse_value("line-through"); + let line_through = TextDecoration::parse("line-through"); assert_eq!(line_through, Ok(TextDecoration::LINE_THROUGH)); - let invalid_decoration_name = TextDecoration::parse_value("Rust"); + let invalid_decoration_name = TextDecoration::parse("Rust"); assert!(invalid_decoration_name.is_err()); } #[test] fn parse_text_decoration_style() { - let solid = TextDecorationStyle::parse_value("solid"); + let solid = TextDecorationStyle::parse("solid"); assert_eq!(solid, Ok(TextDecorationStyle::Solid)); - let double = TextDecorationStyle::parse_value("double"); + let double = TextDecorationStyle::parse("double"); assert_eq!(double, Ok(TextDecorationStyle::Double)); - let dotted = TextDecorationStyle::parse_value("dotted"); + let dotted = TextDecorationStyle::parse("dotted"); assert_eq!(dotted, Ok(TextDecorationStyle::Dotted)); - let dashed = TextDecorationStyle::parse_value("dashed"); + let dashed = TextDecorationStyle::parse("dashed"); assert_eq!(dashed, Ok(TextDecorationStyle::Dashed)); - let wavy = TextDecorationStyle::parse_value("wavy"); + let wavy = TextDecorationStyle::parse("wavy"); assert_eq!(wavy, Ok(TextDecorationStyle::Wavy)); - let invalid_decoration_style = TextDecorationStyle::parse_value("Rust"); + let invalid_decoration_style = TextDecorationStyle::parse("Rust"); assert!(invalid_decoration_style.is_err()); } diff --git a/crates/state/tests/parse_display.rs b/crates/state/tests/parse_display.rs index 23f29e49b..4af555913 100644 --- a/crates/state/tests/parse_display.rs +++ b/crates/state/tests/parse_display.rs @@ -3,43 +3,43 @@ use torin::alignment::Alignment; #[test] fn parse_normal_alignment() { - let alignment = Alignment::parse_value("start"); + let alignment = Alignment::parse("start"); assert_eq!(alignment, Ok(Alignment::Start)); } #[test] fn parse_center_alignment() { - let alignment = Alignment::parse_value("center"); + let alignment = Alignment::parse("center"); assert_eq!(alignment, Ok(Alignment::Center)); } #[test] fn parse_end_alignment() { - let alignment = Alignment::parse_value("end"); + let alignment = Alignment::parse("end"); assert_eq!(alignment, Ok(Alignment::End)); } #[test] fn parse_space_between_alignment() { - let alignment = Alignment::parse_value("space-between"); + let alignment = Alignment::parse("space-between"); assert_eq!(alignment, Ok(Alignment::SpaceBetween)); } #[test] fn parse_space_around_alignment() { - let alignment = Alignment::parse_value("space-around"); + let alignment = Alignment::parse("space-around"); assert_eq!(alignment, Ok(Alignment::SpaceAround)); } #[test] fn parse_space_evenly_alignment() { - let alignment = Alignment::parse_value("space-evenly"); + let alignment = Alignment::parse("space-evenly"); assert_eq!(alignment, Ok(Alignment::SpaceEvenly)); } #[test] fn parse_fallback_alignment() { - let alignment = Alignment::parse_value("Hello, World!"); + let alignment = Alignment::parse("Hello, World!"); assert!(alignment.is_err()); } diff --git a/crates/state/tests/parse_gaps.rs b/crates/state/tests/parse_gaps.rs index fbe96a8f9..9150a9244 100644 --- a/crates/state/tests/parse_gaps.rs +++ b/crates/state/tests/parse_gaps.rs @@ -3,24 +3,24 @@ use torin::gaps::Gaps; #[test] fn parse_all_gaps() { - let gaps = Gaps::parse_value("10"); + let gaps = Gaps::parse("10"); assert_eq!(gaps, Ok(Gaps::new(10.0, 10.0, 10.0, 10.0))); } #[test] fn parse_axis_gaps() { - let gaps = Gaps::parse_value("50 10"); + let gaps = Gaps::parse("50 10"); assert_eq!(gaps, Ok(Gaps::new(50.0, 10.0, 50.0, 10.0))); } #[test] fn parse_sides_gaps() { - let gaps = Gaps::parse_value("1 2 3 4"); + let gaps = Gaps::parse("1 2 3 4"); assert_eq!(gaps, Ok(Gaps::new(1.0, 2.0, 3.0, 4.0))); } #[test] fn parse_horizontal_axis_and_vertical_sides() { - let gaps = Gaps::parse_value("5 50 30"); + let gaps = Gaps::parse("5 50 30"); assert_eq!(gaps, Ok(Gaps::new(5.0, 50.0, 30.0, 50.0))); } diff --git a/crates/state/tests/parse_gradient.rs b/crates/state/tests/parse_gradient.rs index a09be80cf..f85cdd2c1 100644 --- a/crates/state/tests/parse_gradient.rs +++ b/crates/state/tests/parse_gradient.rs @@ -8,7 +8,7 @@ use freya_node_state::{ #[test] fn parse_basic_gradient() { assert_eq!( - LinearGradient::parse_value("linear-gradient(red 0%, blue 100%)"), + LinearGradient::parse("linear-gradient(red 0%, blue 100%)"), Ok(LinearGradient { angle: 0.0, stops: vec![ @@ -28,7 +28,7 @@ fn parse_basic_gradient() { #[test] fn parse_rgb_hsl_gradient() { assert_eq!( - LinearGradient::parse_value("linear-gradient(0deg, rgb(255, 0, 0) 0%, blue 100%)"), + LinearGradient::parse("linear-gradient(0deg, rgb(255, 0, 0) 0%, blue 100%)"), Ok(LinearGradient { angle: 0.0, stops: vec![ @@ -48,7 +48,7 @@ fn parse_rgb_hsl_gradient() { #[test] fn parse_gradient_angle() { assert_eq!( - LinearGradient::parse_value("linear-gradient(45deg, red 0%, blue 100%)"), + LinearGradient::parse("linear-gradient(45deg, red 0%, blue 100%)"), Ok(LinearGradient { angle: f32::to_radians(45.0), stops: vec![ @@ -67,20 +67,18 @@ fn parse_gradient_angle() { #[test] fn invalid_gradients() { - let incorrect_name = - LinearGradient::parse_value("lkdsjfalkasdasdjaslkfjsdklfs(red 0%, blue 100%)"); - let extra_lparen = LinearGradient::parse_value("linear-gradient((red 0%, blue 100%)"); - let extra_rparen = LinearGradient::parse_value("linear-gradient(red 0%, blue 100%))"); - let missing_rparen = LinearGradient::parse_value("linear-gradient(red 0%, blue 100%"); - let missing_commas = LinearGradient::parse_value("linear-gradient(red 0% blue 100%)"); - let extra_commas = LinearGradient::parse_value("linear-gradient(red 0%, blue 100%,)"); + let incorrect_name = LinearGradient::parse("lkdsjfalkasdasdjaslkfjsdklfs(red 0%, blue 100%)"); + let extra_lparen = LinearGradient::parse("linear-gradient((red 0%, blue 100%)"); + let extra_rparen = LinearGradient::parse("linear-gradient(red 0%, blue 100%))"); + let missing_rparen = LinearGradient::parse("linear-gradient(red 0%, blue 100%"); + let missing_commas = LinearGradient::parse("linear-gradient(red 0% blue 100%)"); + let extra_commas = LinearGradient::parse("linear-gradient(red 0%, blue 100%,)"); let extra_stop_component = - LinearGradient::parse_value("linear-gradient(red 0% something, blue 100%)"); - let bad_angle_unit = LinearGradient::parse_value("linear-gradient(45ft, red 0%, blue 100%,)"); - let bad_offset_unit = - LinearGradient::parse_value("linear-gradient(45deg, red 0atm, blue 100kpa)"); - let missing_color = LinearGradient::parse_value("linear-gradient(45deg, 0%, blue 100%)"); - let missing_offset = LinearGradient::parse_value("linear-gradient(45deg, red, blue 100%)"); + LinearGradient::parse("linear-gradient(red 0% something, blue 100%)"); + let bad_angle_unit = LinearGradient::parse("linear-gradient(45ft, red 0%, blue 100%,)"); + let bad_offset_unit = LinearGradient::parse("linear-gradient(45deg, red 0atm, blue 100kpa)"); + let missing_color = LinearGradient::parse("linear-gradient(45deg, 0%, blue 100%)"); + let missing_offset = LinearGradient::parse("linear-gradient(45deg, red, blue 100%)"); assert!(incorrect_name.is_err()); assert!(extra_lparen.is_err()); diff --git a/crates/state/tests/parse_highlight.rs b/crates/state/tests/parse_highlight.rs index bd189f899..dc3af1daa 100644 --- a/crates/state/tests/parse_highlight.rs +++ b/crates/state/tests/parse_highlight.rs @@ -5,19 +5,19 @@ use freya_node_state::{ #[test] fn parse_expanded_highlight_mode() { - let expanded = HighlightMode::parse_value("expanded"); + let expanded = HighlightMode::parse("expanded"); assert_eq!(expanded, Ok(HighlightMode::Expanded)); } #[test] fn parse_fit_highlight_mode() { - let fit = HighlightMode::parse_value("fit"); + let fit = HighlightMode::parse("fit"); assert_eq!(fit, Ok(HighlightMode::Fit)); } #[test] fn parse_fallback_highlight_mode() { - let fallback = HighlightMode::parse_value("Hello, World!"); + let fallback = HighlightMode::parse("Hello, World!"); assert!(fallback.is_err()); } diff --git a/crates/state/tests/parse_shadows.rs b/crates/state/tests/parse_shadows.rs index f100d83ad..081d44469 100644 --- a/crates/state/tests/parse_shadows.rs +++ b/crates/state/tests/parse_shadows.rs @@ -10,7 +10,7 @@ use freya_node_state::{ #[test] fn parse_big_shadow() { - let shadow = Shadow::parse_value("1 2 50 25.0 red"); + let shadow = Shadow::parse("1 2 50 25.0 red"); assert_eq!( shadow, Ok(Shadow { @@ -26,7 +26,7 @@ fn parse_big_shadow() { #[test] fn parse_inset_shadow() { - let shadow = Shadow::parse_value("inset 1 2 50 25.0 red"); + let shadow = Shadow::parse("inset 1 2 50 25.0 red"); assert_eq!( shadow, Ok(Shadow { @@ -42,7 +42,7 @@ fn parse_inset_shadow() { #[test] fn parse_shadow_with_assumed_spread() { - let shadow = Shadow::parse_value("inset 1 2 50 red"); + let shadow = Shadow::parse("inset 1 2 50 red"); assert_eq!( shadow, Ok(Shadow { @@ -58,7 +58,7 @@ fn parse_shadow_with_assumed_spread() { #[test] fn parse_gradient_shadow() { - let shadow = Shadow::parse_value("inset 1 2 50 linear-gradient(red 0%, blue 100%)"); + let shadow = Shadow::parse("inset 1 2 50 linear-gradient(red 0%, blue 100%)"); assert_eq!( shadow, Ok(Shadow { diff --git a/crates/state/tests/parse_size.rs b/crates/state/tests/parse_size.rs index 92c4158b5..b2a3740ee 100644 --- a/crates/state/tests/parse_size.rs +++ b/crates/state/tests/parse_size.rs @@ -9,25 +9,25 @@ use torin::{ #[test] fn parse_pixel_size() { - let size = Size::parse_value("123"); + let size = Size::parse("123"); assert_eq!(size, Ok(Size::Pixels(Length::new(123.0)))); } #[test] fn parse_relative_size() { - let size = Size::parse_value("78.123%"); + let size = Size::parse("78.123%"); assert_eq!(size, Ok(Size::Percentage(Length::new(78.123)))); } #[test] fn parse_auto_size() { - let size = Size::parse_value("auto"); + let size = Size::parse("auto"); assert_eq!(size, Ok(Size::Inner)); } #[test] fn parse_calc_size() { - let size = Size::parse_value("calc(90% - 5% * 123.6)"); + let size = Size::parse("calc(90% - 5% * 123.6)"); assert_eq!( size, Ok(Size::DynamicCalculations(Box::new(vec![ diff --git a/crates/state/tests/parse_text_shadow.rs b/crates/state/tests/parse_text_shadow.rs index 3b3fdd39e..62b9131ec 100644 --- a/crates/state/tests/parse_text_shadow.rs +++ b/crates/state/tests/parse_text_shadow.rs @@ -3,7 +3,7 @@ use freya_node_state::Parse; #[test] fn parse_text_shadow() { - let text_shadow = TextShadow::parse_value("1 5 12 rgb(255, 0, 0)"); + let text_shadow = TextShadow::parse("1 5 12 rgb(255, 0, 0)"); assert_eq!( text_shadow, Ok(TextShadow { From c953a5b2b753e1b765b5fd5241fd203a2152e1d7 Mon Sep 17 00:00:00 2001 From: Savchenko Ivan <73419411+Aiving@users.noreply.github.com> Date: Mon, 15 Jul 2024 12:37:54 +0000 Subject: [PATCH 04/24] reduced number of modifications for PR files diff --- crates/devtools/src/property.rs | 2 +- crates/state/src/cursor.rs | 4 ++-- crates/state/src/font_style.rs | 18 +++++++++--------- crates/state/src/parsing.rs | 2 +- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/crates/devtools/src/property.rs b/crates/devtools/src/property.rs index 98cfd6dab..a58f7b753 100644 --- a/crates/devtools/src/property.rs +++ b/crates/devtools/src/property.rs @@ -195,7 +195,7 @@ pub fn BorderProperty(name: String, border: Border) -> Element { text { font_size: "15", color: "rgb(252,181,172)", - "{border.width:?} {border.style:?} {border.alignment:?}" + "{border.width} {border.style:?} {border.alignment:?}" } } rect { diff --git a/crates/state/src/cursor.rs b/crates/state/src/cursor.rs index c1c58062f..0ad05b87a 100644 --- a/crates/state/src/cursor.rs +++ b/crates/state/src/cursor.rs @@ -91,12 +91,12 @@ impl ParseAttribute for CursorState { } AttributeName::HighlightColor => { if let Some(value) = attr.value.as_text() { - self.highlight_color = Color::parse(value)? + self.highlight_color = Color::parse(value)?; } } AttributeName::HighlightMode => { if let Some(value) = attr.value.as_text() { - self.highlight_mode = HighlightMode::parse(value)? + self.highlight_mode = HighlightMode::parse(value)?; } } AttributeName::CursorReference => { diff --git a/crates/state/src/font_style.rs b/crates/state/src/font_style.rs index 65948fe18..966a1b3ec 100644 --- a/crates/state/src/font_style.rs +++ b/crates/state/src/font_style.rs @@ -113,7 +113,7 @@ impl ParseAttribute for FontStyleState { // Make an exception for the "inherit" as in this case we don't want to pass // a color at all but use the inherited one. if value != "inherit" { - self.color = Color::parse(value)? + self.color = Color::parse(value)?; } } } @@ -148,7 +148,7 @@ impl ParseAttribute for FontStyleState { } AttributeName::TextAlign => { if let Some(value) = attr.value.as_text() { - self.text_align = TextAlign::parse(value)? + self.text_align = TextAlign::parse(value)?; } } AttributeName::MaxLines => { @@ -158,37 +158,37 @@ impl ParseAttribute for FontStyleState { } AttributeName::TextOverflow => { if let Some(value) = attr.value.as_text() { - self.text_overflow = TextOverflow::parse(value)? + self.text_overflow = TextOverflow::parse(value)?; } } AttributeName::FontStyle => { if let Some(value) = attr.value.as_text() { - self.font_slant = Slant::parse(value)? + self.font_slant = Slant::parse(value)?; } } AttributeName::FontWeight => { if let Some(value) = attr.value.as_text() { - self.font_weight = Weight::parse(value)? + self.font_weight = Weight::parse(value)?; } } AttributeName::FontWidth => { if let Some(value) = attr.value.as_text() { - self.font_width = Width::parse(value)? + self.font_width = Width::parse(value)?; } } AttributeName::Decoration => { if let Some(value) = attr.value.as_text() { - self.decoration.ty = TextDecoration::parse(value)? + self.decoration.ty = TextDecoration::parse(value)?; } } AttributeName::DecorationStyle => { if let Some(value) = attr.value.as_text() { - self.decoration.style = TextDecorationStyle::parse(value)? + self.decoration.style = TextDecorationStyle::parse(value)?; } } AttributeName::DecorationColor => { if let Some(value) = attr.value.as_text() { - self.decoration.color = Color::parse(value)? + self.decoration.color = Color::parse(value)?; } else { self.decoration.color = self.color; } diff --git a/crates/state/src/parsing.rs b/crates/state/src/parsing.rs index 3d5011769..1a5cfa39b 100644 --- a/crates/state/src/parsing.rs +++ b/crates/state/src/parsing.rs @@ -153,7 +153,7 @@ pub trait ParseAttribute: Sized { if self.parse_attribute(attr).is_err() { panic!( "Failed to parse attribute '{:?}' with value '{:?}'", - error_attr.attribute, error_attr.value, + error_attr.attribute, error_attr.value ); } } From 17bee71625fdd8328d2d5a502214ae17d967c078 Mon Sep 17 00:00:00 2001 From: Savchenko Ivan <73419411+Aiving@users.noreply.github.com> Date: Tue, 16 Jul 2024 16:19:32 +0000 Subject: [PATCH 05/24] fix rgba color parsing --- crates/state/src/lexing.rs | 20 ++++++++++---------- crates/state/src/values/color.rs | 10 +++++----- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/crates/state/src/lexing.rs b/crates/state/src/lexing.rs index df24dd934..3f1007025 100644 --- a/crates/state/src/lexing.rs +++ b/crates/state/src/lexing.rs @@ -4,7 +4,7 @@ use std::iter; pub enum Token { Ident(String), Float(f32), - Number(i64), + Integer(i64), ParenOpen, ParenClose, Minus, @@ -31,11 +31,11 @@ impl Token { } pub fn is_i64(&self) -> bool { - matches!(self, Token::Number(_)) + matches!(self, Token::Integer(_)) } pub fn is_i64_or_f32(&self) -> bool { - matches!(self, Token::Number(_) | Token::Float(_)) + matches!(self, Token::Integer(_) | Token::Float(_)) } pub fn into_string(self) -> String { @@ -49,7 +49,7 @@ impl Token { pub fn into_f32(self) -> f32 { if let Token::Float(value) = self { value - } else if let Token::Number(value) = self { + } else if let Token::Integer(value) = self { value as f32 } else { unreachable!() @@ -57,7 +57,7 @@ impl Token { } pub fn into_i64(self) -> i64 { - if let Token::Number(value) = self { + if let Token::Integer(value) = self { value } else { unreachable!() @@ -83,7 +83,7 @@ impl Token { pub fn try_as_f32(&self) -> Option { if let Token::Float(value) = self { Some(*value) - } else if let Token::Number(value) = self { + } else if let Token::Integer(value) = self { Some(*value as f32) } else { None @@ -91,7 +91,7 @@ impl Token { } pub fn try_as_i64(&self) -> Option { - if let Token::Number(value) = self { + if let Token::Integer(value) = self { Some(*value) } else { None @@ -99,7 +99,7 @@ impl Token { } pub fn try_as_u8(&self) -> Option { - if let Token::Number(value) = self { + if let Token::Integer(value) = self { u8::try_from(*value).ok() } else { None @@ -140,7 +140,7 @@ impl Lexer { if value.contains('.') { tokens.push(Token::Float(value.parse().unwrap())); } else { - tokens.push(Token::Number(value.parse().unwrap())); + tokens.push(Token::Integer(value.parse().unwrap())); } } '(' => tokens.push(Token::ParenOpen), @@ -157,7 +157,7 @@ impl Lexer { if value.contains('.') { tokens.push(Token::Float(value.parse().unwrap())); } else { - tokens.push(Token::Number(value.parse().unwrap())); + tokens.push(Token::Integer(value.parse().unwrap())); } } else { tokens.push(Token::Minus); diff --git a/crates/state/src/values/color.rs b/crates/state/src/values/color.rs index f9922f4e3..b72eb68d8 100644 --- a/crates/state/src/values/color.rs +++ b/crates/state/src/values/color.rs @@ -89,11 +89,11 @@ fn parse_rgb(parser: &mut Parser) -> Result { let color = if parser.try_consume(&Token::Comma) { let alpha = parser.consume_map(|token| { - if let Some(value) = token.try_as_f32() { - Some((value * 255.0).round().clamp(0.0, 255.0) as u8) - } else { - token.try_as_u8() - } + token.try_as_u8().or_else(|| { + token + .try_as_f32() + .map(|value| (value * 255.0).round().clamp(0.0, 255.0) as u8) + }) })?; Color::from_argb(alpha, red, green, blue) From fd372824496d14309bd266f256d012eda135dbae Mon Sep 17 00:00:00 2001 From: Savchenko Ivan <73419411+Aiving@users.noreply.github.com> Date: Tue, 16 Jul 2024 22:25:10 +0500 Subject: [PATCH 06/24] Update crates/state/src/values/font.rs Co-authored-by: Marc Espin --- crates/state/src/values/font.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/state/src/values/font.rs b/crates/state/src/values/font.rs index f34501ba1..36df88f92 100644 --- a/crates/state/src/values/font.rs +++ b/crates/state/src/values/font.rs @@ -26,7 +26,7 @@ impl Parse for Slant { fn from_parser(parser: &mut Parser) -> Result { parser.consume_map(|token| { token.try_as_str().and_then(|value| match value { - "upright" => Some(Slant::Upright), + "upright" | "normal" => Some(Slant::Upright), "italic" => Some(Slant::Italic), "oblique" => Some(Slant::Oblique), _ => None, From 08818aa5066a9cffa22805a94c5dfca56a87b3bf Mon Sep 17 00:00:00 2001 From: Aiving Date: Wed, 17 Jul 2024 12:53:58 +0500 Subject: [PATCH 07/24] fix unicode character parsing --- crates/state/src/lexing.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/crates/state/src/lexing.rs b/crates/state/src/lexing.rs index 3f1007025..6bce4ef1a 100644 --- a/crates/state/src/lexing.rs +++ b/crates/state/src/lexing.rs @@ -14,7 +14,6 @@ pub enum Token { Pound, Percent, Comma, - Unknown(char), } impl Token { @@ -177,7 +176,13 @@ impl Lexer { } '%' => tokens.push(Token::Percent), ',' => tokens.push(Token::Comma), - character => tokens.push(Token::Unknown(character)), + character => { + if let Some(Token::Ident(data)) = tokens.last_mut() { + data.push(character); + } else { + tokens.push(Token::Ident(character.to_string())); + } + } } } From 0ee85baa028f8613df82b25acd318fdfcec2f029 Mon Sep 17 00:00:00 2001 From: Aiving Date: Wed, 17 Jul 2024 13:06:26 +0500 Subject: [PATCH 08/24] change text-overflow parsing --- crates/state/src/values/font.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/crates/state/src/values/font.rs b/crates/state/src/values/font.rs index 36df88f92..887246e8c 100644 --- a/crates/state/src/values/font.rs +++ b/crates/state/src/values/font.rs @@ -124,13 +124,15 @@ impl TextOverflow { } impl Parse for TextOverflow { - fn from_parser(parser: &mut Parser) -> Result { - parser.consume_map(|token| { - token.try_as_str().map(|value| match value { - "ellipsis" => TextOverflow::Ellipsis, - "clip" => TextOverflow::Clip, - value => TextOverflow::Custom(value.to_string()), - }) + fn from_parser(_: &mut Parser) -> Result { + unimplemented!() + } + + fn parse(value: &str) -> Result { + Ok(match value { + "ellipsis" => TextOverflow::Ellipsis, + "clip" => TextOverflow::Clip, + value => TextOverflow::Custom(value.to_string()), }) } } From fc751ee390af3db6d04ca54b37f94a89e4feda00 Mon Sep 17 00:00:00 2001 From: Aiving Date: Sun, 4 Aug 2024 08:15:11 +0500 Subject: [PATCH 09/24] fixes --- crates/state/src/values/fill.rs | 6 +++--- crates/state/src/values/gradient.rs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/state/src/values/fill.rs b/crates/state/src/values/fill.rs index d1ca63f3c..961fbe7c9 100644 --- a/crates/state/src/values/fill.rs +++ b/crates/state/src/values/fill.rs @@ -38,11 +38,11 @@ impl Parse for Fill { if parser.check(&Token::ident("linear-gradient")) { LinearGradient::from_parser(parser).map(Self::LinearGradient) } else if parser.check(&Token::ident("radial-gradient")) { - RadialGradient::from_parser(parser).map(Self::RadialGradient) + RadialGradient::from_parser(parser).map(Self::RadialGradient) } else if parser.check(&Token::ident("gradient-gradient")) { - ConicGradient::from_parser(parser).map(Self::ConicGradient) + ConicGradient::from_parser(parser).map(Self::ConicGradient) } else { - Color::from_parser(parser).map(Self::Color) + Color::from_parser(parser).map(Self::Color) } } } diff --git a/crates/state/src/values/gradient.rs b/crates/state/src/values/gradient.rs index abb1e5a7a..4369c10bc 100644 --- a/crates/state/src/values/gradient.rs +++ b/crates/state/src/values/gradient.rs @@ -144,7 +144,7 @@ impl RadialGradient { } impl Parse for RadialGradient { - fn parse(value: &str) -> Result { + fn from_parser(parser: &mut Parser) -> Result { parser.consume(&Token::ident("radial-gradient"))?; parser.consume(&Token::ParenOpen)?; @@ -208,7 +208,7 @@ impl ConicGradient { } impl Parse for ConicGradient { - fn parse(value: &str) -> Result { + fn from_parser(parser: &mut Parser) -> Result { parser.consume(&Token::ident("conic-gradient"))?; parser.consume(&Token::ParenOpen)?; @@ -237,7 +237,7 @@ impl Parse for ConicGradient { })?; parser.consume(&Token::ident("deg"))?; - + let end = if parser.try_consume(&Token::ident("to")) { let result = parser.consume_map(Token::try_as_i64).and_then(|value| { if (0..=360).contains(&value) { From 3dafc96033c296804cdd43b09f02c6649b1c2464 Mon Sep 17 00:00:00 2001 From: Aiving Date: Sun, 4 Aug 2024 08:21:00 +0500 Subject: [PATCH 10/24] more fixes --- crates/state/src/values/gradient.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/state/src/values/gradient.rs b/crates/state/src/values/gradient.rs index 4369c10bc..59b47e1f9 100644 --- a/crates/state/src/values/gradient.rs +++ b/crates/state/src/values/gradient.rs @@ -223,9 +223,9 @@ impl Parse for ConicGradient { parser.consume(&Token::ident("deg"))?; parser.consume(&Token::Comma)?; - angle + Some(angle) } else { - 0.0 + None }, angles: if parser.try_consume(&Token::ident("from")) { let start = parser.consume_map(Token::try_as_i64).and_then(|value| { From b50b78dc7a2b19771a68e56b8e8e4080f9d322a5 Mon Sep 17 00:00:00 2001 From: Aiving Date: Sun, 29 Sep 2024 15:51:25 +0500 Subject: [PATCH 11/24] yoo --- crates/state/src/parsing.rs | 52 ++++++-- crates/state/src/values/border.rs | 44 ++++++- crates/state/src/values/color.rs | 156 ++++++++++++----------- crates/state/src/values/corner_radius.rs | 2 +- crates/state/src/values/focusable.rs | 5 + crates/state/src/values/gaps.rs | 7 +- crates/state/src/values/gradient.rs | 118 ++++------------- crates/state/src/values/size.rs | 77 +++++------ 8 files changed, 231 insertions(+), 230 deletions(-) diff --git a/crates/state/src/parsing.rs b/crates/state/src/parsing.rs index aa755a402..cd851e8ab 100644 --- a/crates/state/src/parsing.rs +++ b/crates/state/src/parsing.rs @@ -109,36 +109,68 @@ impl Parser { // FromStr but we own it so we can impl it on torin and skia_safe types. pub trait Parse: Sized { fn from_parser(parser: &mut Parser) -> Result; + fn from_parser_multiple( + parser: &mut Parser, + separator: &Token, + ) -> Result, ParseError> { + let mut values = vec![Self::from_parser(parser)?]; - fn parse(value: &str) -> Result { + while parser.try_consume(separator) { + values.push(Self::from_parser(parser)?); + } + + Ok(values) + } + + fn parse_with_separator(value: &str, separator: &Token) -> Result, ParseError> { let mut parser = Parser::new(Lexer::parse(value)); - let value = Self::from_parser(&mut parser); + let values = Self::from_parser_multiple(&mut parser, separator)?; if parser.tokens.len() > 0 { Err(ParseError) } else { - value + Ok(values) } } - fn parse_with_separator(value: &str, separator: &Token) -> Result, ParseError> { + fn parse(value: &str) -> Result { let mut parser = Parser::new(Lexer::parse(value)); - let mut values = vec![Self::from_parser(&mut parser)?]; - - while parser.try_consume(separator) { - values.push(Self::from_parser(&mut parser)?); - } + let value = Self::from_parser(&mut parser); if parser.tokens.len() > 0 { Err(ParseError) } else { - Ok(values) + value } } } +pub fn parse_angle(parser: &mut Parser) -> Result { + let value = parser.consume_if(Token::is_i64).map(Token::into_f32)?; + + parser.consume(&Token::ident("deg"))?; + + Ok(value) +} + +pub fn parse_func, F: FnOnce(&mut Parser) -> Result, O>( + parser: &mut Parser, + name: T, + body: F, +) -> Result { + parser.consume(&Token::ident(name.as_ref()))?; + + parser.consume(&Token::ParenOpen)?; + + let value = body(parser)?; + + parser.consume(&Token::ParenClose)?; + + Ok(value) +} + pub trait ParseAttribute: Sized { fn parse_attribute( &mut self, diff --git a/crates/state/src/values/border.rs b/crates/state/src/values/border.rs index 684ac9022..37ee4fd1c 100644 --- a/crates/state/src/values/border.rs +++ b/crates/state/src/values/border.rs @@ -4,7 +4,6 @@ use freya_engine::prelude::Color; use torin::scaled::Scaled; use crate::{ - ExtSplit, Fill, Parse, ParseError, @@ -38,6 +37,45 @@ pub struct BorderWidth { pub left: f32, } +impl Parse for BorderWidth { + fn from_parser(parser: &mut Parser) -> Result { + Ok( + match ( + parser.consume_map(Token::try_as_f32)?, + parser.consume_map(Token::try_as_f32).ok(), + parser.consume_map(Token::try_as_f32).ok(), + parser.consume_map(Token::try_as_f32).ok(), + ) { + (top, Some(right), Some(bottom), Some(left)) => Self { + top, + right, + bottom, + left, + }, + (top, Some(horizontal), Some(bottom), None) => Self { + top, + right: horizontal, + bottom, + left: horizontal, + }, + (vertical, Some(horizontal), None, None) => Self { + top: vertical, + right: horizontal, + bottom: vertical, + left: horizontal, + }, + (all, None, None, None) => Self { + top: all, + right: all, + bottom: all, + left: all, + }, + _ => return Err(ParseError), + }, + ) + } +} + impl Scaled for BorderWidth { fn scale(&mut self, scale_factor: f32) { self.top *= scale_factor; @@ -95,7 +133,7 @@ impl Parse for Border { } Ok(Border { - width: parser.consume_map(Token::try_as_f32)?, + width: BorderWidth::from_parser(parser)?, fill: Fill::from_parser(parser)?, alignment: BorderAlignment::default(), }) @@ -104,7 +142,7 @@ impl Parse for Border { impl fmt::Display for Border { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{} {} {}", self.width, self.alignment, self.fill,) + write!(f, "{} {} {}", self.width, self.alignment, self.fill) } } diff --git a/crates/state/src/values/color.rs b/crates/state/src/values/color.rs index d7fbaaedb..f0f281604 100644 --- a/crates/state/src/values/color.rs +++ b/crates/state/src/values/color.rs @@ -3,6 +3,8 @@ use std::fmt; use freya_engine::prelude::*; use crate::{ + parse_angle, + parse_func, Parse, ParseError, Parser, @@ -16,27 +18,29 @@ pub trait DisplayColor { impl Parse for Color { fn from_parser(parser: &mut Parser) -> Result { - if parser.try_consume(&Token::Pound) { + if parser.check(&Token::Pound) { parse_hex_color(parser) - } else if parser.try_consume(&Token::ident("rgb")) { + } else if parser.check(&Token::ident("rgb")) { parse_rgb(parser) - } else if parser.try_consume(&Token::ident("hsl")) { + } else if parser.check(&Token::ident("hsl")) { parse_hsl(parser) } else { - parser.consume_map(|token| { - token.try_as_str().and_then(|value| match value { - "red" => Some(Color::RED), - "green" => Some(Color::GREEN), - "blue" => Some(Color::BLUE), - "yellow" => Some(Color::YELLOW), - "black" => Some(Color::BLACK), - "gray" => Some(Color::GRAY), - "white" => Some(Color::WHITE), - "orange" => Some(Color::from_rgb(255, 165, 0)), - "transparent" | "none" => Some(Color::TRANSPARENT), - _ => None, - }) - }) + let value = parser.consume_if(Token::is_ident).map(Token::into_string)?; + + match value.as_str() { + "rgb" => parse_rgb(parser), + "hsl" => parse_hsl(parser), + "red" => Ok(Color::RED), + "green" => Ok(Color::GREEN), + "blue" => Ok(Color::BLUE), + "yellow" => Ok(Color::YELLOW), + "black" => Ok(Color::BLACK), + "gray" => Ok(Color::GRAY), + "white" => Ok(Color::WHITE), + "orange" => Ok(Color::from_rgb(255, 165, 0)), + "transparent" | "none" => Ok(Color::TRANSPARENT), + _ => Err(ParseError), + } } } } @@ -88,13 +92,7 @@ fn parse_rgb(parser: &mut Parser) -> Result { let blue = parser.consume_map(Token::try_as_u8)?; let color = if parser.try_consume(&Token::Comma) { - let alpha = parser.consume_map(|token| { - token.try_as_u8().or_else(|| { - token - .try_as_f32() - .map(|value| (value * 255.0).round().clamp(0.0, 255.0) as u8) - }) - })?; + let alpha = parser.consume_map(Token::try_as_u8)?; Color::from_argb(alpha, red, green, blue) } else { @@ -107,74 +105,82 @@ fn parse_rgb(parser: &mut Parser) -> Result { } fn parse_hsl(parser: &mut Parser) -> Result { - parser.consume(&Token::ParenOpen)?; + parse_func(parser, "hsl", |parser| { + let h = parse_angle(parser)?; - let h = parser.consume_map(Token::try_as_i64).and_then(|value| { - if (0..=360).contains(&value) { - Ok(value as f32) - } else { - Err(ParseError) + if !(0.0..=360.0).contains(&h) { + return Err(ParseError); } - })?; - parser.consume(&Token::ident("deg"))?; - parser.consume(&Token::Comma)?; + parser.consume(&Token::Comma)?; - let mut s = parser.consume_map(Token::try_as_i64).and_then(|value| { - if (0..=100).contains(&value) { - Ok((value as f32) / 100.0) - } else { - Err(ParseError) - } - })?; - - parser.consume(&Token::Percent)?; - parser.consume(&Token::Comma)?; - - let mut l = parser.consume_map(Token::try_as_i64).and_then(|value| { - if (0..=100).contains(&value) { - Ok((value as f32) / 100.0) - } else { - Err(ParseError) - } - })?; + let mut s = parser + .consume_if(Token::is_i64) + .map(Token::into_f32) + .and_then(|value| { + if (0.0..=100.0).contains(&value) { + Ok(value / 100.0) + } else { + Err(ParseError) + } + })?; - parser.consume(&Token::Percent)?; - - let a = if parser.consume(&Token::Comma).is_ok() { - let value = parser.consume_map(Token::try_as_i64).and_then(|value| { - if (0..=100).contains(&value) { - Ok((value as f32) / 100.0) - } else { - Err(ParseError) - } - })?; + parser.consume(&Token::Percent)?; + parser.consume(&Token::Comma)?; + + let mut l = parser + .consume_if(Token::is_i64) + .map(Token::into_f32) + .and_then(|value| { + if (0.0..=100.0).contains(&value) { + Ok(value / 100.0) + } else { + Err(ParseError) + } + })?; parser.consume(&Token::Percent)?; - Some(value) - } else { - None - }; + let a = if parser.consume(&Token::Comma).is_ok() { + let value = parser + .consume_if(Token::is_i64) + .map(Token::into_f32) + .and_then(|value| { + if (0.0..=100.0).contains(&value) { + Ok(value / 100.0) + } else { + Err(ParseError) + } + })?; + + parser.consume(&Token::Percent)?; + + Some(value) + } else { + None + }; - parser.consume(&Token::ParenClose)?; + parser.consume(&Token::ParenClose)?; - // HSL to HSV Conversion - l *= 2.0; - s *= if l <= 1.0 { l } else { 2.0 - l }; + // HSL to HSV Conversion + l *= 2.0; + s *= if l <= 1.0 { l } else { 2.0 - l }; - let v = (l + s) / 2.0; + let v = (l + s) / 2.0; - s = (2.0 * s) / (l + s); + s = (2.0 * s) / (l + s); - let hsv = HSV::from((h, s, v)); + let hsv = HSV::from((h, s, v)); - // Handle alpha formatting and convert to ARGB - Ok(a.map(|a| hsv.to_color((a * 255.0).round() as u8)) - .unwrap_or_else(|| hsv.to_color(255))) + // Handle alpha formatting and convert to ARGB + Ok(a.map(|a| hsv.to_color((a * 255.0).round() as u8)) + .unwrap_or_else(|| hsv.to_color(255))) + }) } fn parse_hex_color(parser: &mut Parser) -> Result { + parser.consume(&Token::Pound)?; + let hex = parser.consume_if(Token::is_ident).map(Token::into_string)?; if ![6, 8].contains(&hex.len()) { diff --git a/crates/state/src/values/corner_radius.rs b/crates/state/src/values/corner_radius.rs index e21a9346b..e614d3f97 100644 --- a/crates/state/src/values/corner_radius.rs +++ b/crates/state/src/values/corner_radius.rs @@ -201,7 +201,7 @@ impl CornerRadius { } impl Parse for CornerRadius { - fn from_parser(parser: &mut Parser) -> Result { + fn parse(parser: &mut Parser) -> Result { let mut radius = CornerRadius::default(); match ( diff --git a/crates/state/src/values/focusable.rs b/crates/state/src/values/focusable.rs index 8f4e8d9c2..d32f3b00a 100644 --- a/crates/state/src/values/focusable.rs +++ b/crates/state/src/values/focusable.rs @@ -1,6 +1,7 @@ use crate::{ Parse, ParseError, + Parser, }; #[derive(Clone, Debug, PartialEq, Default, Eq)] @@ -22,6 +23,10 @@ impl Focusable { } impl Parse for Focusable { + fn from_parser(_: &mut Parser) -> Result { + unimplemented!() + } + fn parse(value: &str) -> Result { Ok(match value { "true" => Self::Enabled, diff --git a/crates/state/src/values/gaps.rs b/crates/state/src/values/gaps.rs index 626784a8a..ef2884af0 100644 --- a/crates/state/src/values/gaps.rs +++ b/crates/state/src/values/gaps.rs @@ -11,15 +11,12 @@ impl Parse for Gaps { fn from_parser(parser: &mut Parser) -> Result { let mut paddings = Gaps::default(); - let value = - parser.consume_if(|token| token == &Token::ident("none") || token.is_i64_or_f32())?; - - if value == Token::ident("none") { + if parser.try_consume(&Token::ident("none")) { return Ok(paddings); } match ( - value.into_f32(), + parser.consume_map(Token::try_as_f32)?, parser.consume_map(Token::try_as_f32).ok(), parser.consume_map(Token::try_as_f32).ok(), parser.consume_map(Token::try_as_f32).ok(), diff --git a/crates/state/src/values/gradient.rs b/crates/state/src/values/gradient.rs index e7111e8ad..5ffe90029 100644 --- a/crates/state/src/values/gradient.rs +++ b/crates/state/src/values/gradient.rs @@ -10,6 +10,8 @@ use torin::{ }; use crate::{ + parse_angle, + parse_func, DisplayColor, Parse, ParseError, @@ -78,38 +80,19 @@ impl LinearGradient { impl Parse for LinearGradient { fn from_parser(parser: &mut Parser) -> Result { - parser.consume(&Token::ident("linear-gradient"))?; - parser.consume(&Token::ParenOpen)?; + parse_func(parser, "linear-gradient", |parser| { + let mut gradient = Self::default(); - let mut gradient = LinearGradient { - angle: if let Ok(angle) = parser.consume_map(Token::try_as_i64).and_then(|value| { - if (0..=360).contains(&value) { - Ok(value as f32) - } else { - Err(ParseError) - } - }) { - parser.consume(&Token::ident("deg"))?; + if let Ok(angle) = parse_angle(parser) { parser.consume(&Token::Comma)?; - angle - } else { - 0.0 - }, - ..Default::default() - }; - - while !parser.check(&Token::ParenClose) { - if !gradient.stops.is_empty() { - parser.consume(&Token::Comma)?; + gradient.angle = angle.to_radians(); } - gradient.stops.push(GradientStop::from_parser(parser)?); - } - - parser.consume(&Token::ParenClose)?; + gradient.stops = GradientStop::from_parser_multiple(parser, &Token::Comma)?; - Ok(gradient) + Ok(gradient) + }) } } @@ -154,22 +137,9 @@ impl RadialGradient { impl Parse for RadialGradient { fn from_parser(parser: &mut Parser) -> Result { - parser.consume(&Token::ident("radial-gradient"))?; - parser.consume(&Token::ParenOpen)?; - - let mut gradient = RadialGradient::default(); - - while !parser.check(&Token::ParenClose) { - if !gradient.stops.is_empty() { - parser.consume(&Token::Comma)?; - } - - gradient.stops.push(GradientStop::from_parser(parser)?); - } - - parser.consume(&Token::ParenClose)?; - - Ok(gradient) + parse_func(parser, "radial-gradient", |parser| { + GradientStop::from_parser_multiple(parser, &Token::Comma).map(|stops| Self { stops }) + }) } } @@ -218,71 +188,33 @@ impl ConicGradient { impl Parse for ConicGradient { fn from_parser(parser: &mut Parser) -> Result { - parser.consume(&Token::ident("conic-gradient"))?; - parser.consume(&Token::ParenOpen)?; + parse_func(parser, "conic-gradient", |parser| { + let mut gradient = Self::default(); + + if let Ok(angle) = parse_angle(parser) { + gradient.angle = Some(angle.to_radians()); - let mut gradient = ConicGradient { - angle: if let Ok(angle) = parser.consume_map(Token::try_as_i64).and_then(|value| { - if (0..=360).contains(&value) { - Ok(value as f32) - } else { - Err(ParseError) - } - }) { - parser.consume(&Token::ident("deg"))?; parser.consume(&Token::Comma)?; + } - Some(angle) - } else { - None - }, - angles: if parser.try_consume(&Token::ident("from")) { - let start = parser.consume_map(Token::try_as_i64).and_then(|value| { - if (0..=360).contains(&value) { - Ok(value as f32) - } else { - Err(ParseError) - } - })?; - - parser.consume(&Token::ident("deg"))?; + if parser.try_consume(&Token::ident("from")) { + let start = parse_angle(parser)?; let end = if parser.try_consume(&Token::ident("to")) { - let result = parser.consume_map(Token::try_as_i64).and_then(|value| { - if (0..=360).contains(&value) { - Ok(value as f32) - } else { - Err(ParseError) - } - })?; - - parser.consume(&Token::ident("deg"))?; - - result + parse_angle(parser)? } else { 360.0 }; - parser.consume(&Token::Comma)?; - - Some((start, end)) - } else { - None - }, - ..Default::default() - }; + gradient.angles = Some((start, end)); - while !parser.check(&Token::ParenClose) { - if !gradient.stops.is_empty() { parser.consume(&Token::Comma)?; } - gradient.stops.push(GradientStop::from_parser(parser)?); - } - - parser.consume(&Token::ParenClose)?; + gradient.stops = GradientStop::from_parser_multiple(parser, &Token::Comma)?; - Ok(gradient) + Ok(gradient) + }) } } diff --git a/crates/state/src/values/size.rs b/crates/state/src/values/size.rs index 39c352564..d17bdec24 100644 --- a/crates/state/src/values/size.rs +++ b/crates/state/src/values/size.rs @@ -7,6 +7,7 @@ use torin::{ }; use crate::{ + parse_func, Parse, ParseError, Parser, @@ -15,23 +16,17 @@ use crate::{ impl Parse for Size { fn from_parser(parser: &mut Parser) -> Result { - let value = parser.consume_if(|token| token.is_ident() || token.is_i64_or_f32())?; - - if value.is_ident() { - value - .try_as_str() - .and_then(|value| match value { - "auto" => Some(Self::Inner), - "fill" => Some(Self::Fill), - "fill-min" => Some(Self::FillMinimum), - "calc" => parse_calc(parser) - .map(|value| Self::DynamicCalculations(Box::new(value))) - .ok(), - _ => None, - }) - .ok_or(ParseError) + if parser.check(&Token::ident("calc")) { + parse_calc(parser).map(|value| Self::DynamicCalculations(Box::new(value))) + } else if let Ok(value) = parser.consume_if(Token::is_ident).map(Token::into_string) { + match value.as_str() { + "auto" => Ok(Self::Inner), + "fill" => Ok(Self::Fill), + "fill-min" => Ok(Self::FillMinimum), + _ => Err(ParseError), + } } else { - let value = value.into_f32(); + let value = parser.consume_map(Token::try_as_f32)?; Ok(if parser.try_consume(&Token::Percent) { Size::Percentage(Length::new(value)) @@ -46,40 +41,36 @@ impl Parse for Size { } } -pub fn parse_calc(parser: &mut Parser) -> Result, ParseError> { - parser.consume(&Token::ParenOpen)?; - - let mut calcs = vec![]; - - while let Ok(value) = parser.consume_if(|token| { - token.is_i64_or_f32() - || matches!( - token, - Token::Plus | Token::Minus | Token::Slash | Token::Star - ) - }) { - if value.is_i64_or_f32() { - let value = value.into_f32(); - - calcs.push(if parser.try_consume(&Token::Percent) { +impl Parse for DynamicCalculation { + fn from_parser(parser: &mut Parser) -> Result { + if let Ok(value) = parser.consume_map(Token::try_as_f32) { + Ok(if parser.try_consume(&Token::Percent) { DynamicCalculation::Percentage(value) } else if parser.try_consume(&Token::ident("v")) { - DynamicCalculation::Pixels(value) + DynamicCalculation::RootPercentage(value) } else { DynamicCalculation::Pixels(value) - }); + }) } else { - match value { - Token::Plus => calcs.push(DynamicCalculation::Add), - Token::Minus => calcs.push(DynamicCalculation::Sub), - Token::Slash => calcs.push(DynamicCalculation::Div), - Token::Star => calcs.push(DynamicCalculation::Mul), - _ => {} - } + parser.consume_map(|token| match token { + Token::Plus => Some(Self::Add), + Token::Minus => Some(Self::Sub), + Token::Slash => Some(Self::Div), + Token::Star => Some(Self::Mul), + _ => None, + }) } } +} + +pub fn parse_calc(parser: &mut Parser) -> Result, ParseError> { + parse_func(parser, "calc", |parser| { + let mut calcs = Vec::new(); - parser.consume(&Token::ParenClose)?; + while let Ok(value) = DynamicCalculation::from_parser(parser) { + calcs.push(value); + } - Ok(calcs) + Ok(calcs) + }) } From b88b19b5042ac77739e57b58b8337ccf27acda52 Mon Sep 17 00:00:00 2001 From: Savchenko Ivan <73419411+Aiving@users.noreply.github.com> Date: Sun, 29 Sep 2024 16:05:59 +0500 Subject: [PATCH 12/24] fix corner_radius.rs --- crates/state/src/values/corner_radius.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/state/src/values/corner_radius.rs b/crates/state/src/values/corner_radius.rs index e614d3f97..e21a9346b 100644 --- a/crates/state/src/values/corner_radius.rs +++ b/crates/state/src/values/corner_radius.rs @@ -201,7 +201,7 @@ impl CornerRadius { } impl Parse for CornerRadius { - fn parse(parser: &mut Parser) -> Result { + fn from_parser(parser: &mut Parser) -> Result { let mut radius = CornerRadius::default(); match ( From 3babf8c20133c25238b3476e690c31757457401f Mon Sep 17 00:00:00 2001 From: Savchenko Ivan <73419411+Aiving@users.noreply.github.com> Date: Sun, 29 Sep 2024 16:06:19 +0500 Subject: [PATCH 13/24] Update style.rs --- crates/state/src/style.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/state/src/style.rs b/crates/state/src/style.rs index 496b7a2e9..0dd24cb3f 100644 --- a/crates/state/src/style.rs +++ b/crates/state/src/style.rs @@ -29,7 +29,6 @@ use crate::{ OverflowMode, Parse, ParseAttribute, - ParseError, Parser, Shadow, Token, From c3a630c02ffd38eb49a88ffeeb596ddb2f47012e Mon Sep 17 00:00:00 2001 From: Savchenko Ivan <73419411+Aiving@users.noreply.github.com> Date: Sun, 29 Sep 2024 16:11:15 +0500 Subject: [PATCH 14/24] Update border.rs --- crates/state/src/values/border.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/state/src/values/border.rs b/crates/state/src/values/border.rs index 37ee4fd1c..696641443 100644 --- a/crates/state/src/values/border.rs +++ b/crates/state/src/values/border.rs @@ -134,8 +134,8 @@ impl Parse for Border { Ok(Border { width: BorderWidth::from_parser(parser)?, + alignment: BorderAlignment::from_parser(parser)?, fill: Fill::from_parser(parser)?, - alignment: BorderAlignment::default(), }) } } From 32ec6929ca82c729ed4f9626906150273d3ab5db Mon Sep 17 00:00:00 2001 From: Savchenko Ivan <73419411+Aiving@users.noreply.github.com> Date: Sun, 29 Sep 2024 16:21:33 +0500 Subject: [PATCH 15/24] Update color.rs --- crates/state/src/values/color.rs | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/crates/state/src/values/color.rs b/crates/state/src/values/color.rs index f0f281604..9cfd321bb 100644 --- a/crates/state/src/values/color.rs +++ b/crates/state/src/values/color.rs @@ -79,29 +79,25 @@ impl DisplayColor for Color { } fn parse_rgb(parser: &mut Parser) -> Result { - parser.consume(&Token::ParenOpen)?; + parse_func(parser, "rgb", |parser| { + let red = parser.consume_map(Token::try_as_u8)?; - let red = parser.consume_map(Token::try_as_u8)?; - - parser.consume(&Token::Comma)?; + parser.consume(&Token::Comma)?; - let green = parser.consume_map(Token::try_as_u8)?; + let green = parser.consume_map(Token::try_as_u8)?; - parser.consume(&Token::Comma)?; + parser.consume(&Token::Comma)?; - let blue = parser.consume_map(Token::try_as_u8)?; + let blue = parser.consume_map(Token::try_as_u8)?; - let color = if parser.try_consume(&Token::Comma) { - let alpha = parser.consume_map(Token::try_as_u8)?; + Ok(if parser.try_consume(&Token::Comma) { + let alpha = parser.consume_map(Token::try_as_u8)?; - Color::from_argb(alpha, red, green, blue) - } else { - Color::from_rgb(red, green, blue) - }; - - parser.consume(&Token::ParenClose)?; - - Ok(color) + Color::from_argb(alpha, red, green, blue) + } else { + Color::from_rgb(red, green, blue) + }) + }) } fn parse_hsl(parser: &mut Parser) -> Result { From a1dd2cb79350eec824253b1f33e7bbcacb18d02a Mon Sep 17 00:00:00 2001 From: Aiving Date: Sun, 29 Sep 2024 18:40:43 +0500 Subject: [PATCH 16/24] add parse error messages --- crates/state/src/accessibility.rs | 2 +- crates/state/src/cursor.rs | 21 +- crates/state/src/font_style.rs | 26 ++- crates/state/src/layer.rs | 4 +- crates/state/src/layout.rs | 53 +++-- crates/state/src/parsing.rs | 255 +++++++---------------- crates/state/src/transform.rs | 15 +- crates/state/src/values/border.rs | 7 +- crates/state/src/values/color.rs | 64 ++---- crates/state/src/values/corner_radius.rs | 25 ++- crates/state/src/values/size.rs | 5 +- crates/state/tests/parse_focusable.rs | 1 - 12 files changed, 208 insertions(+), 270 deletions(-) diff --git a/crates/state/src/accessibility.rs b/crates/state/src/accessibility.rs index 1fb90b874..31e7d44c5 100644 --- a/crates/state/src/accessibility.rs +++ b/crates/state/src/accessibility.rs @@ -70,7 +70,7 @@ impl ParseAttribute for AccessibilityNodeState { if let OwnedAttributeValue::Text(attr) = attr.value { self.a11y_role = Some( serde_json::from_str::(&format!("\"{attr}\"")) - .map_err(|_| ParseError)?, + .map_err(|err| ParseError(err.to_string()))?, ) } } diff --git a/crates/state/src/cursor.rs b/crates/state/src/cursor.rs index 6dcb5bbc2..44db5c7dc 100644 --- a/crates/state/src/cursor.rs +++ b/crates/state/src/cursor.rs @@ -1,6 +1,9 @@ -use std::sync::{ - Arc, - Mutex, +use std::{ + num::ParseIntError, + sync::{ + Arc, + Mutex, + }, }; use freya_common::{ @@ -71,7 +74,11 @@ impl ParseAttribute for CursorState { AttributeName::CursorIndex => { if let Some(value) = attr.value.as_text() { if value != "none" { - self.position = Some(value.parse().map_err(|_| ParseError)?); + self.position = Some( + value + .parse() + .map_err(|err: ParseIntError| ParseError(err.to_string()))?, + ); } } } @@ -87,7 +94,11 @@ impl ParseAttribute for CursorState { } AttributeName::CursorId => { if let Some(value) = attr.value.as_text() { - self.cursor_id = Some(value.parse().map_err(|_| ParseError)?); + self.cursor_id = Some( + value + .parse() + .map_err(|err: ParseIntError| ParseError(err.to_string()))?, + ); } } AttributeName::Highlights => { diff --git a/crates/state/src/font_style.rs b/crates/state/src/font_style.rs index 556e46371..fd6dd6a15 100644 --- a/crates/state/src/font_style.rs +++ b/crates/state/src/font_style.rs @@ -1,6 +1,12 @@ -use std::sync::{ - Arc, - Mutex, +use std::{ + num::{ + ParseFloatError, + ParseIntError, + }, + sync::{ + Arc, + Mutex, + }, }; use freya_common::CompositorDirtyNodes; @@ -158,7 +164,11 @@ impl ParseAttribute for FontStyleState { } AttributeName::MaxLines => { if let Some(value) = attr.value.as_text() { - self.max_lines = Some(value.parse().map_err(|_| ParseError)?); + self.max_lines = Some( + value + .parse() + .map_err(|err: ParseIntError| ParseError(err.to_string()))?, + ); } } AttributeName::TextOverflow => { @@ -200,12 +210,16 @@ impl ParseAttribute for FontStyleState { } AttributeName::WordSpacing => { if let Some(value) = attr.value.as_text() { - self.word_spacing = value.parse().map_err(|_| ParseError)?; + self.word_spacing = value + .parse() + .map_err(|err: ParseFloatError| ParseError(err.to_string()))?; } } AttributeName::LetterSpacing => { if let Some(value) = attr.value.as_text() { - self.letter_spacing = value.parse().map_err(|_| ParseError)?; + self.letter_spacing = value + .parse() + .map_err(|err: ParseFloatError| ParseError(err.to_string()))?; } } _ => {} diff --git a/crates/state/src/layer.rs b/crates/state/src/layer.rs index fc5d7c616..f7aef02a7 100644 --- a/crates/state/src/layer.rs +++ b/crates/state/src/layer.rs @@ -39,7 +39,9 @@ impl ParseAttribute for LayerState { match attr.attribute { AttributeName::Layer => { if let Some(value) = attr.value.as_text() { - let layer = value.parse::().map_err(|_| ParseError)?; + let layer = value + .parse::() + .map_err(|err| ParseError(err.to_string()))?; self.layer -= layer; self.layer_for_children += layer; } diff --git a/crates/state/src/layout.rs b/crates/state/src/layout.rs index 125fde15b..31201b1f6 100644 --- a/crates/state/src/layout.rs +++ b/crates/state/src/layout.rs @@ -103,18 +103,31 @@ impl ParseAttribute for LayoutState { self.direction = match value { "horizontal" => DirectionMode::Horizontal, "vertical" => DirectionMode::Vertical, - _ => return Err(ParseError), + value => { + return Err(ParseError::invalid_ident( + value, + &["horizontal", "vertical"], + )) + } } } } AttributeName::OffsetY => { if let Some(value) = attr.value.as_text() { - self.offset_y = Length::new(value.parse::().map_err(|_| ParseError)?); + self.offset_y = Length::new( + value + .parse::() + .map_err(|err| ParseError(err.to_string()))?, + ); } } AttributeName::OffsetX => { if let Some(value) = attr.value.as_text() { - self.offset_x = Length::new(value.parse::().map_err(|_| ParseError)?); + self.offset_x = Length::new( + value + .parse::() + .map_err(|err| ParseError(err.to_string()))?, + ); } } AttributeName::MainAlign => { @@ -136,26 +149,38 @@ impl ParseAttribute for LayoutState { } AttributeName::PositionTop => { if let Some(value) = attr.value.as_text() { - self.position - .set_top(value.parse::().map_err(|_| ParseError)?); + self.position.set_top( + value + .parse::() + .map_err(|err| ParseError(err.to_string()))?, + ); } } AttributeName::PositionRight => { if let Some(value) = attr.value.as_text() { - self.position - .set_right(value.parse::().map_err(|_| ParseError)?); + self.position.set_right( + value + .parse::() + .map_err(|err| ParseError(err.to_string()))?, + ); } } AttributeName::PositionBottom => { if let Some(value) = attr.value.as_text() { - self.position - .set_bottom(value.parse::().map_err(|_| ParseError)?); + self.position.set_bottom( + value + .parse::() + .map_err(|err| ParseError(err.to_string()))?, + ); } } AttributeName::PositionLeft => { if let Some(value) = attr.value.as_text() { - self.position - .set_left(value.parse::().map_err(|_| ParseError)?); + self.position.set_left( + value + .parse::() + .map_err(|err| ParseError(err.to_string()))?, + ); } } AttributeName::Content => { @@ -172,7 +197,11 @@ impl ParseAttribute for LayoutState { } AttributeName::Spacing => { if let Some(value) = attr.value.as_text() { - self.spacing = Length::new(value.parse::().map_err(|_| ParseError)?); + self.spacing = Length::new( + value + .parse::() + .map_err(|err| ParseError(err.to_string()))?, + ); } } _ => {} diff --git a/crates/state/src/parsing.rs b/crates/state/src/parsing.rs index cd851e8ab..ecd3f06ec 100644 --- a/crates/state/src/parsing.rs +++ b/crates/state/src/parsing.rs @@ -1,6 +1,9 @@ use std::{ iter::Peekable, - str::CharIndices, + ops::{ + Bound, + RangeBounds, + }, vec::IntoIter, }; @@ -13,7 +16,46 @@ use crate::{ }; #[derive(Clone, Debug, PartialEq)] -pub struct ParseError; +pub struct ParseError(pub String); + +impl ParseError { + pub fn expected_token(expected: &Token, found: Option<&Token>) -> Self { + if let Some(found) = found { + Self(format!("expected {expected:?}, found {found:?}")) + } else { + Self(format!("expected {expected:?}, found nothing")) + } + } + + pub fn unexpected_token(value: Option<&Token>) -> Self { + if let Some(value) = value { + Self(format!("unexpected {value:?}")) + } else { + Self("unexpected nothing".into()) + } + } + + pub fn invalid_ident(value: &str, expected: &[&str]) -> Self { + let expected = match expected.len() { + 0 => "nothing".into(), + 1 => expected[0].into(), + size => { + let other = expected[0..(size - 1)].join(", "); + let last = expected[size - 1]; + + format!("{other} or {last}") + } + }; + + Self(format!("invalid ident {value} (expected {expected})")) + } + + pub fn too_much_tokens(count: usize) -> Self { + Self(format!( + "found more than zero ({count}) tokens after parsing" + )) + } +} pub struct Parser { pub(crate) tokens: Peekable>, @@ -52,16 +94,7 @@ impl Parser { if self.check(value) { Ok(self.next().unwrap()) } else { - Err(ParseError) - } - } - - /// Consumes the current token if it exists and is equal to one of the values inside `values`, otherwise returning `ParseError`. - pub fn consume_one_of(&mut self, values: &[Token]) -> Result { - if self.check_if(|value| values.contains(value)) { - Ok(self.next().unwrap()) - } else { - Err(ParseError) + Err(ParseError::expected_token(value, self.peek())) } } @@ -70,7 +103,7 @@ impl Parser { if self.check_if(func) { Ok(self.next().unwrap()) } else { - Err(ParseError) + Err(ParseError::unexpected_token(self.peek())) } } @@ -81,7 +114,7 @@ impl Parser { Ok(value) } else { - Err(ParseError) + Err(ParseError::unexpected_token(self.peek())) } } @@ -95,15 +128,6 @@ impl Parser { pub fn peek(&mut self) -> Option<&Token> { self.tokens.peek() } - - /// Consumes the current token and returns it wrapped in `Some` if the result of the `func` function is `true`, otherwise returning `None`. - pub fn next_if bool>(&mut self, func: F) -> Option { - if self.check_if(func) { - self.next() - } else { - None - } - } } // FromStr but we own it so we can impl it on torin and skia_safe types. @@ -128,7 +152,7 @@ pub trait Parse: Sized { let values = Self::from_parser_multiple(&mut parser, separator)?; if parser.tokens.len() > 0 { - Err(ParseError) + Err(ParseError::too_much_tokens(parser.tokens.len())) } else { Ok(values) } @@ -140,7 +164,7 @@ pub trait Parse: Sized { let value = Self::from_parser(&mut parser); if parser.tokens.len() > 0 { - Err(ParseError) + Err(ParseError::too_much_tokens(parser.tokens.len())) } else { value } @@ -148,11 +172,37 @@ pub trait Parse: Sized { } pub fn parse_angle(parser: &mut Parser) -> Result { - let value = parser.consume_if(Token::is_i64).map(Token::into_f32)?; + let value = parser.consume_map(Token::try_as_i64)?; parser.consume(&Token::ident("deg"))?; - Ok(value) + Ok((value % 360) as f32) +} + +pub fn parse_range(parser: &mut Parser, range: impl RangeBounds) -> Result { + let value = parser.consume_map(Token::try_as_i64)?; + + if range.contains(&value) { + Ok(value as f32) + } else { + let start = match range.start_bound() { + Bound::Included(value) => Some(format!("greater than or equal to {value}")), + _ => None, + }; + + let end = match range.end_bound() { + Bound::Included(value) => Some(format!("less than or equal to {value}")), + Bound::Excluded(value) => Some(format!("less than {value}")), + Bound::Unbounded => None, + }; + + Err(match [start, end] { + [Some(start), Some(end)] => ParseError(format!("{value} must be {start} and {end}")), + [Some(start), None] => ParseError(format!("{value} must be {start}")), + [None, Some(end)] => ParseError(format!("{value} must be {end}")), + [None, None] => unreachable!(), + }) + } } pub fn parse_func, F: FnOnce(&mut Parser) -> Result, O>( @@ -182,9 +232,9 @@ pub trait ParseAttribute: Sized { { let error_attr = attr.clone(); - if self.parse_attribute(attr).is_err() { + if let Err(ParseError(message)) = self.parse_attribute(attr) { panic!( - "Failed to parse attribute '{:?}' with value '{:?}'", + "Failed to parse attribute '{:?}' with value '{:?}': {message}", error_attr.attribute, error_attr.value ); } @@ -194,150 +244,3 @@ pub trait ParseAttribute: Sized { self.parse_attribute(attr).ok(); } } - -pub trait ExtSplit { - fn split_excluding_group( - &self, - delimiter: char, - group_start: char, - group_end: char, - ) -> SplitExcludingGroup<'_>; - fn split_ascii_whitespace_excluding_group( - &self, - group_start: char, - group_end: char, - ) -> SplitAsciiWhitespaceExcludingGroup<'_>; -} - -impl ExtSplit for str { - fn split_excluding_group( - &self, - delimiter: char, - group_start: char, - group_end: char, - ) -> SplitExcludingGroup<'_> { - SplitExcludingGroup { - text: self, - chars: self.char_indices(), - delimiter, - group_start, - group_end, - trailing_empty: true, - } - } - - fn split_ascii_whitespace_excluding_group( - &self, - group_start: char, - group_end: char, - ) -> SplitAsciiWhitespaceExcludingGroup<'_> { - SplitAsciiWhitespaceExcludingGroup { - text: self, - chars: self.char_indices(), - group_start, - group_end, - } - } -} - -#[derive(Clone, Debug)] -pub struct SplitExcludingGroup<'a> { - pub text: &'a str, - pub chars: CharIndices<'a>, - pub delimiter: char, - pub group_start: char, - pub group_end: char, - pub trailing_empty: bool, -} - -impl<'a> Iterator for SplitExcludingGroup<'a> { - type Item = &'a str; - - fn next(&mut self) -> Option<&'a str> { - let first = self.chars.next(); - - let (start, mut prev) = match first { - None => { - if self.text.ends_with(self.delimiter) && self.trailing_empty { - self.trailing_empty = false; - return Some(""); - } - return None; - } - Some((_, c)) if c == self.delimiter => return Some(""), - Some(v) => v, - }; - - let mut in_group = false; - let mut nesting = -1; - - loop { - if prev == self.group_start { - if nesting == -1 { - in_group = true; - } - nesting += 1; - } else if prev == self.group_end { - nesting -= 1; - if nesting == -1 { - in_group = false; - } - } - - prev = match self.chars.next() { - None => return Some(&self.text[start..]), - Some((end, c)) if c == self.delimiter && !in_group => { - return Some(&self.text[start..end]) - } - Some((_, c)) => c, - } - } - } -} - -#[derive(Clone, Debug)] -pub struct SplitAsciiWhitespaceExcludingGroup<'a> { - pub text: &'a str, - pub chars: CharIndices<'a>, - pub group_start: char, - pub group_end: char, -} - -impl<'a> Iterator for SplitAsciiWhitespaceExcludingGroup<'a> { - type Item = &'a str; - - fn next(&mut self) -> Option<&'a str> { - let first = self.chars.next(); - - let (start, mut prev) = match first { - None => return None, - Some((_, c)) if c.is_ascii_whitespace() => return self.next(), - Some(v) => v, - }; - - let mut in_group = false; - let mut nesting = -1; - - loop { - if prev == self.group_start { - if nesting == -1 { - in_group = true; - } - nesting += 1; - } else if prev == self.group_end { - nesting -= 1; - if nesting == -1 { - in_group = false; - } - } - - prev = match self.chars.next() { - None => return Some(&self.text[start..]), - Some((end, c)) if c.is_ascii_whitespace() && !in_group => { - return Some(&self.text[start..end]) - } - Some((_, c)) => c, - } - } - } -} diff --git a/crates/state/src/transform.rs b/crates/state/src/transform.rs index d7dfc634d..0d9ebe6ff 100644 --- a/crates/state/src/transform.rs +++ b/crates/state/src/transform.rs @@ -41,18 +41,17 @@ impl ParseAttribute for TransformState { match attr.attribute { AttributeName::Rotate => { if let Some(value) = attr.value.as_text() { - if value.ends_with("deg") { - let rotation = value - .replacen("deg", "", 1) - .parse::() - .map_err(|_| ParseError)?; - self.rotations.push((self.node_id, rotation)); - } + let mut parser = crate::Parser::new(crate::Lexer::parse(value)); + + self.rotations + .push((self.node_id, crate::parse_angle(&mut parser)?)); } } AttributeName::Opacity => { if let Some(value) = attr.value.as_text() { - let opacity = value.parse::().map_err(|_| ParseError)?; + let opacity = value + .parse::() + .map_err(|err| ParseError(err.to_string()))?; self.opacities.push(opacity) } } diff --git a/crates/state/src/values/border.rs b/crates/state/src/values/border.rs index 696641443..89a86e3d2 100644 --- a/crates/state/src/values/border.rs +++ b/crates/state/src/values/border.rs @@ -52,25 +52,24 @@ impl Parse for BorderWidth { bottom, left, }, - (top, Some(horizontal), Some(bottom), None) => Self { + (top, Some(horizontal), Some(bottom), _) => Self { top, right: horizontal, bottom, left: horizontal, }, - (vertical, Some(horizontal), None, None) => Self { + (vertical, Some(horizontal), ..) => Self { top: vertical, right: horizontal, bottom: vertical, left: horizontal, }, - (all, None, None, None) => Self { + (all, ..) => Self { top: all, right: all, bottom: all, left: all, }, - _ => return Err(ParseError), }, ) } diff --git a/crates/state/src/values/color.rs b/crates/state/src/values/color.rs index 9cfd321bb..bd5cefdb9 100644 --- a/crates/state/src/values/color.rs +++ b/crates/state/src/values/color.rs @@ -5,6 +5,7 @@ use freya_engine::prelude::*; use crate::{ parse_angle, parse_func, + parse_range, Parse, ParseError, Parser, @@ -39,7 +40,10 @@ impl Parse for Color { "white" => Ok(Color::WHITE), "orange" => Ok(Color::from_rgb(255, 165, 0)), "transparent" | "none" => Ok(Color::TRANSPARENT), - _ => Err(ParseError), + value => Err(ParseError::invalid_ident( + value, + &["built-in color name", "color functions", "none"], + )), } } } @@ -104,50 +108,19 @@ fn parse_hsl(parser: &mut Parser) -> Result { parse_func(parser, "hsl", |parser| { let h = parse_angle(parser)?; - if !(0.0..=360.0).contains(&h) { - return Err(ParseError); - } - parser.consume(&Token::Comma)?; - let mut s = parser - .consume_if(Token::is_i64) - .map(Token::into_f32) - .and_then(|value| { - if (0.0..=100.0).contains(&value) { - Ok(value / 100.0) - } else { - Err(ParseError) - } - })?; + let mut s = parse_range(parser, 0..=100)?; parser.consume(&Token::Percent)?; parser.consume(&Token::Comma)?; - let mut l = parser - .consume_if(Token::is_i64) - .map(Token::into_f32) - .and_then(|value| { - if (0.0..=100.0).contains(&value) { - Ok(value / 100.0) - } else { - Err(ParseError) - } - })?; + let mut l = parse_range(parser, 0..=100)?; parser.consume(&Token::Percent)?; - let a = if parser.consume(&Token::Comma).is_ok() { - let value = parser - .consume_if(Token::is_i64) - .map(Token::into_f32) - .and_then(|value| { - if (0.0..=100.0).contains(&value) { - Ok(value / 100.0) - } else { - Err(ParseError) - } - })?; + let a = if parser.try_consume(&Token::Comma) { + let value = parse_range(parser, 0..=100)?; parser.consume(&Token::Percent)?; @@ -180,20 +153,27 @@ fn parse_hex_color(parser: &mut Parser) -> Result { let hex = parser.consume_if(Token::is_ident).map(Token::into_string)?; if ![6, 8].contains(&hex.len()) { - return Err(ParseError); + return Err(ParseError("invalid hex color".into())); } - let value = i64::from_str_radix(&hex, 16).map_err(|_| ParseError)?; + let value = + i64::from_str_radix(&hex, 16).map_err(|_| ParseError("invalid hex color".into()))?; let a = if hex.len() == 8 { - Some(u8::try_from((value >> 24) & 0xFF).map_err(|_| ParseError)?) + Some( + u8::try_from((value >> 24) & 0xFF) + .map_err(|_| ParseError("failed to parse hex color part as u8".into()))?, + ) } else { None }; - let r = u8::try_from((value >> 16) & 0xFF).map_err(|_| ParseError)?; - let g = u8::try_from((value >> 8) & 0xFF).map_err(|_| ParseError)?; - let b = u8::try_from(value & 0xFF).map_err(|_| ParseError)?; + let r = u8::try_from((value >> 16) & 0xFF) + .map_err(|_| ParseError("failed to parse hex color part as u8".into()))?; + let g = u8::try_from((value >> 8) & 0xFF) + .map_err(|_| ParseError("failed to parse hex color part as u8".into()))?; + let b = u8::try_from(value & 0xFF) + .map_err(|_| ParseError("failed to parse hex color part as u8".into()))?; Ok(a.map(|a| Color::from_argb(a, r, g, b)) .unwrap_or_else(|| Color::from_rgb(r, g, b))) diff --git a/crates/state/src/values/corner_radius.rs b/crates/state/src/values/corner_radius.rs index e21a9346b..ab9a66785 100644 --- a/crates/state/src/values/corner_radius.rs +++ b/crates/state/src/values/corner_radius.rs @@ -210,18 +210,6 @@ impl Parse for CornerRadius { parser.consume_map(Token::try_as_f32).ok(), parser.consume_map(Token::try_as_f32).ok(), ) { - // Same in all corners - (value, None, None, None) => { - radius.fill_all(value); - } - // By Top and Bottom - (top, Some(bottom), None, None) => { - // Top - radius.fill_top(top); - - // Bottom - radius.fill_bottom(bottom) - } // Each corner (top_left, Some(top_right), Some(bottom_left), Some(bottom_right)) => { radius = CornerRadius { @@ -232,7 +220,18 @@ impl Parse for CornerRadius { ..Default::default() } } - _ => return Err(ParseError), + // By Top and Bottom + (top, Some(bottom), ..) => { + // Top + radius.fill_top(top); + + // Bottom + radius.fill_bottom(bottom) + } + // Same in all corners + (value, ..) => { + radius.fill_all(value); + } } Ok(radius) diff --git a/crates/state/src/values/size.rs b/crates/state/src/values/size.rs index d17bdec24..6c593844a 100644 --- a/crates/state/src/values/size.rs +++ b/crates/state/src/values/size.rs @@ -23,7 +23,10 @@ impl Parse for Size { "auto" => Ok(Self::Inner), "fill" => Ok(Self::Fill), "fill-min" => Ok(Self::FillMinimum), - _ => Err(ParseError), + value => Err(ParseError::invalid_ident( + value, + &["auto", "fill", "fill-min"], + )), } } else { let value = parser.consume_map(Token::try_as_f32)?; diff --git a/crates/state/tests/parse_focusable.rs b/crates/state/tests/parse_focusable.rs index 62ae1d9c9..27832e9a7 100644 --- a/crates/state/tests/parse_focusable.rs +++ b/crates/state/tests/parse_focusable.rs @@ -1,4 +1,3 @@ -use freya_engine::prelude::*; use freya_node_state::{ Focusable, Parse, From bee0ee58a7c1533a72ac3f186e5941563e76c97e Mon Sep 17 00:00:00 2001 From: Aiving Date: Sun, 29 Sep 2024 18:50:10 +0500 Subject: [PATCH 17/24] fix rgb(a) parsing --- crates/state/src/lexing.rs | 24 +++++++++++++++++++++++- crates/state/src/parsing.rs | 6 +++--- crates/state/src/values/color.rs | 15 +++++++++++---- 3 files changed, 37 insertions(+), 8 deletions(-) diff --git a/crates/state/src/lexing.rs b/crates/state/src/lexing.rs index 6bce4ef1a..8ee788c66 100644 --- a/crates/state/src/lexing.rs +++ b/crates/state/src/lexing.rs @@ -1,4 +1,7 @@ -use std::iter; +use std::{ + fmt, + iter, +}; #[derive(Debug, PartialEq, Clone)] pub enum Token { @@ -106,6 +109,25 @@ impl Token { } } +impl fmt::Display for Token { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Token::Ident(value) => f.write_str(value), + Token::Float(value) => write!(f, "float {value}"), + Token::Integer(value) => write!(f, "integer {value}"), + Token::ParenOpen => write!(f, "\"(\""), + Token::ParenClose => write!(f, "\")\""), + Token::Minus => write!(f, "\"-\""), + Token::Plus => write!(f, "\"+\""), + Token::Slash => write!(f, "\"/\""), + Token::Star => write!(f, "\"*\""), + Token::Pound => write!(f, "\"#\""), + Token::Percent => write!(f, "\"%\""), + Token::Comma => write!(f, "\",\""), + } + } +} + pub struct Lexer; impl Lexer { diff --git a/crates/state/src/parsing.rs b/crates/state/src/parsing.rs index ecd3f06ec..1c5ae02ba 100644 --- a/crates/state/src/parsing.rs +++ b/crates/state/src/parsing.rs @@ -21,15 +21,15 @@ pub struct ParseError(pub String); impl ParseError { pub fn expected_token(expected: &Token, found: Option<&Token>) -> Self { if let Some(found) = found { - Self(format!("expected {expected:?}, found {found:?}")) + Self(format!("expected {expected}, found {found}")) } else { - Self(format!("expected {expected:?}, found nothing")) + Self(format!("expected {expected}, found nothing")) } } pub fn unexpected_token(value: Option<&Token>) -> Self { if let Some(value) = value { - Self(format!("unexpected {value:?}")) + Self(format!("unexpected {value}")) } else { Self("unexpected nothing".into()) } diff --git a/crates/state/src/values/color.rs b/crates/state/src/values/color.rs index bd5cefdb9..b6ffafcf6 100644 --- a/crates/state/src/values/color.rs +++ b/crates/state/src/values/color.rs @@ -29,8 +29,6 @@ impl Parse for Color { let value = parser.consume_if(Token::is_ident).map(Token::into_string)?; match value.as_str() { - "rgb" => parse_rgb(parser), - "hsl" => parse_hsl(parser), "red" => Ok(Color::RED), "green" => Ok(Color::GREEN), "blue" => Ok(Color::BLUE), @@ -42,7 +40,12 @@ impl Parse for Color { "transparent" | "none" => Ok(Color::TRANSPARENT), value => Err(ParseError::invalid_ident( value, - &["built-in color name", "color functions", "none"], + &[ + "hex color", + "built-in color name", + "color functions", + "none", + ], )), } } @@ -95,7 +98,11 @@ fn parse_rgb(parser: &mut Parser) -> Result { let blue = parser.consume_map(Token::try_as_u8)?; Ok(if parser.try_consume(&Token::Comma) { - let alpha = parser.consume_map(Token::try_as_u8)?; + let alpha = if let Ok(value) = parser.consume_if(Token::is_f32).map(Token::into_f32) { + (value * 255.0).round() as u8 + } else { + parser.consume_map(Token::try_as_u8)? + }; Color::from_argb(alpha, red, green, blue) } else { From 873306faed6b6da4e9936b9b6e270f47be9aec66 Mon Sep 17 00:00:00 2001 From: Aiving Date: Sun, 29 Sep 2024 18:54:23 +0500 Subject: [PATCH 18/24] fix text shadow parsing --- crates/state/src/font_style.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/state/src/font_style.rs b/crates/state/src/font_style.rs index fd6dd6a15..18c263c90 100644 --- a/crates/state/src/font_style.rs +++ b/crates/state/src/font_style.rs @@ -130,7 +130,9 @@ impl ParseAttribute for FontStyleState { } AttributeName::TextShadow => { if let Some(value) = attr.value.as_text() { - self.text_shadows = TextShadow::parse_with_separator(value, &Token::Comma)?; + if value != "none" { + self.text_shadows = TextShadow::parse_with_separator(value, &Token::Comma)?; + } } } AttributeName::FontFamily => { From 6fed2d5af8bb6d8a2e6f2ccf6190bbf4f1c79de5 Mon Sep 17 00:00:00 2001 From: Aiving Date: Sun, 29 Sep 2024 19:57:10 +0500 Subject: [PATCH 19/24] maybe fixed text shadow parsing --- crates/state/src/font_style.rs | 4 +--- crates/state/src/values/text_shadow.rs | 24 +++++++++++++++--------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/crates/state/src/font_style.rs b/crates/state/src/font_style.rs index 18c263c90..fd6dd6a15 100644 --- a/crates/state/src/font_style.rs +++ b/crates/state/src/font_style.rs @@ -130,9 +130,7 @@ impl ParseAttribute for FontStyleState { } AttributeName::TextShadow => { if let Some(value) = attr.value.as_text() { - if value != "none" { - self.text_shadows = TextShadow::parse_with_separator(value, &Token::Comma)?; - } + self.text_shadows = TextShadow::parse_with_separator(value, &Token::Comma)?; } } AttributeName::FontFamily => { diff --git a/crates/state/src/values/text_shadow.rs b/crates/state/src/values/text_shadow.rs index 196ce1539..a17e3f0dd 100644 --- a/crates/state/src/values/text_shadow.rs +++ b/crates/state/src/values/text_shadow.rs @@ -10,14 +10,20 @@ use crate::{ // Same as shadow, but no inset or spread. impl Parse for TextShadow { fn from_parser(parser: &mut Parser) -> Result { - Ok(TextShadow { - offset: ( - parser.consume_map(Token::try_as_f32)?, - parser.consume_map(Token::try_as_f32)?, - ) - .into(), - blur_sigma: parser.consume_map(Token::try_as_f32)? as f64 / 2.0, - color: Color::from_parser(parser)?, - }) + let shadow = if parser.try_consume(&Token::ident("none")) { + Self::default() + } else { + TextShadow { + offset: ( + parser.consume_map(Token::try_as_f32)?, + parser.consume_map(Token::try_as_f32)?, + ) + .into(), + blur_sigma: parser.consume_map(Token::try_as_f32)? as f64 / 2.0, + color: Color::from_parser(parser)?, + } + }; + + Ok(shadow) } } From 31aec74e0b8ef5027686ac7e0db93240e14e1bae Mon Sep 17 00:00:00 2001 From: Aiving Date: Sun, 29 Sep 2024 20:01:54 +0500 Subject: [PATCH 20/24] fix hsl color parsing --- crates/state/src/values/color.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/crates/state/src/values/color.rs b/crates/state/src/values/color.rs index b6ffafcf6..6447cdcc2 100644 --- a/crates/state/src/values/color.rs +++ b/crates/state/src/values/color.rs @@ -136,8 +136,6 @@ fn parse_hsl(parser: &mut Parser) -> Result { None }; - parser.consume(&Token::ParenClose)?; - // HSL to HSV Conversion l *= 2.0; s *= if l <= 1.0 { l } else { 2.0 - l }; From 49cf1caa6b4b3e7d5b72b7b3e7b610264fa2e68a Mon Sep 17 00:00:00 2001 From: Aiving Date: Sun, 29 Sep 2024 20:09:42 +0500 Subject: [PATCH 21/24] fix corner radius parsing --- crates/state/src/values/corner_radius.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/state/src/values/corner_radius.rs b/crates/state/src/values/corner_radius.rs index ab9a66785..827d417dd 100644 --- a/crates/state/src/values/corner_radius.rs +++ b/crates/state/src/values/corner_radius.rs @@ -221,7 +221,7 @@ impl Parse for CornerRadius { } } // By Top and Bottom - (top, Some(bottom), ..) => { + (top, Some(bottom), None, None) => { // Top radius.fill_top(top); @@ -229,9 +229,10 @@ impl Parse for CornerRadius { radius.fill_bottom(bottom) } // Same in all corners - (value, ..) => { + (value, None, None, None) => { radius.fill_all(value); } + _ => return Err(ParseError("invalid count of numbers (must be 1, 2 or 4)".into())) } Ok(radius) From d6f33965c12cc6314ccbdf30869b7f6aad83f9da Mon Sep 17 00:00:00 2001 From: Aiving Date: Sun, 29 Sep 2024 20:13:01 +0500 Subject: [PATCH 22/24] forgor to fmt --- crates/state/src/values/corner_radius.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/crates/state/src/values/corner_radius.rs b/crates/state/src/values/corner_radius.rs index 827d417dd..f58e75555 100644 --- a/crates/state/src/values/corner_radius.rs +++ b/crates/state/src/values/corner_radius.rs @@ -232,7 +232,11 @@ impl Parse for CornerRadius { (value, None, None, None) => { radius.fill_all(value); } - _ => return Err(ParseError("invalid count of numbers (must be 1, 2 or 4)".into())) + _ => { + return Err(ParseError( + "invalid count of numbers (must be 1, 2 or 4)".into(), + )) + } } Ok(radius) From 315ad4d4baa094f1199c5b779e15dbb2a34c28bc Mon Sep 17 00:00:00 2001 From: Aiving Date: Sun, 29 Sep 2024 20:19:06 +0500 Subject: [PATCH 23/24] fix gradients angle parsing --- crates/state/src/values/gradient.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/state/src/values/gradient.rs b/crates/state/src/values/gradient.rs index 5ffe90029..a3757e3eb 100644 --- a/crates/state/src/values/gradient.rs +++ b/crates/state/src/values/gradient.rs @@ -86,7 +86,7 @@ impl Parse for LinearGradient { if let Ok(angle) = parse_angle(parser) { parser.consume(&Token::Comma)?; - gradient.angle = angle.to_radians(); + gradient.angle = angle; } gradient.stops = GradientStop::from_parser_multiple(parser, &Token::Comma)?; @@ -192,7 +192,7 @@ impl Parse for ConicGradient { let mut gradient = Self::default(); if let Ok(angle) = parse_angle(parser) { - gradient.angle = Some(angle.to_radians()); + gradient.angle = Some(angle); parser.consume(&Token::Comma)?; } From e7caef3170eb6e86602e6a0dc792114205af9a2a Mon Sep 17 00:00:00 2001 From: Savchenko Ivan <73419411+Aiving@users.noreply.github.com> Date: Wed, 16 Oct 2024 21:13:50 +0500 Subject: [PATCH 24/24] implement new parse for text height --- crates/state/src/values/text_height.rs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/crates/state/src/values/text_height.rs b/crates/state/src/values/text_height.rs index 4977b383d..cb0f25033 100644 --- a/crates/state/src/values/text_height.rs +++ b/crates/state/src/values/text_height.rs @@ -6,14 +6,16 @@ use crate::{ }; impl Parse for TextHeightBehavior { - fn parse(value: &str) -> Result { - match value { - "all" => Ok(TextHeightBehavior::All), - "disable-first-ascent" => Ok(TextHeightBehavior::DisableFirstAscent), - "disable-least-ascent" => Ok(TextHeightBehavior::DisableLastDescent), - "disable-all" => Ok(TextHeightBehavior::DisableAll), - _ => Err(ParseError), - } + fn from_parser(parser: &mut Parser) -> Result { + parser.consume_map(|value| { + value.try_as_str().and_then(|value| match value { + "all" => Some(TextHeightBehavior::All), + "disable-first-ascent" => Some(TextHeightBehavior::DisableFirstAscent), + "disable-least-ascent" => Some(TextHeightBehavior::DisableLastDescent), + "disable-all" => Some(TextHeightBehavior::DisableAll), + _ => None, + }) + }) } }