gioser

tawasuyu/taffy

A high performance rust-powered UI layout library

HTML 5544Rust 88Markdown 11TOML 91926 Definitions

⎇ 70341c9f697b610470b0806a53266aa75343563b src/util/parse.rs Rust · 118 lines · 4.2 KB History Who touched what raw

Add support for parsing Taffy style types from string (#929) 70341c9f6 · Nico Burns ·

1//! Helpers for parsing style types from strings
2
3use cssparser::BasicParseError;
4pub(crate) use cssparser::{Parser, ParserInput, Token};
5use std::borrow::Cow;
6
7/// Error type for parsing a type from `cssparser::Parser`
8pub(crate) type CssParseError<'i> = cssparser::ParseError<'i, Cow<'i, str>>;
9
10/// Result type for parsing a type from `cssparser::Parser`
11pub(crate) type CssParseResult<'i, T> = Result<T, CssParseError<'i>>;
12
13/// Error type for parsing a type from string
14#[derive(Clone, Debug)]
15pub struct ParseError(String);
16
17impl core::fmt::Display for ParseError {
18    fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
19        write!(fmt, "{}", &self.0)
20    }
21}
22impl<'i> From<CssParseError<'i>> for ParseError {
23    fn from(value: CssParseError<'i>) -> Self {
24        Self(value.to_string())
25    }
26}
27impl<'i> From<BasicParseError<'i>> for ParseError {
28    fn from(value: BasicParseError<'i>) -> Self {
29        Self(CssParseError::from(value).to_string())
30    }
31}
32#[cfg(feature = "std")]
33impl std::error::Error for ParseError {}
34
35/// Result type for parsing a type from string
36pub type ParseResult<T> = Result<T, ParseError>;
37
38/// Trait for parsing a type from a `cssparser::Parser` input
39pub(crate) trait FromCss: Sized {
40    /// Parse type from a `cssparser::Parser` input
41    fn from_css<'i>(parser: &mut Parser<'i, '_>) -> CssParseResult<'i, Self>;
42}
43
44/// Parse a string into a type that implements `FromCss`, ensuring that the
45/// entire input string is consumed.
46pub(crate) fn parse_css_str_entirely<T: FromCss>(input: &str) -> Result<T, ParseError> {
47    let mut parser_input = ParserInput::new(input);
48    let mut parser = Parser::new(&mut parser_input);
49    parser.parse_entirely(|parser| T::from_css(parser)).map_err(|err| ParseError(err.to_string()))
50}
51
52/// Automatically implement `FromStr` for a type that already implemented `FromCss`
53macro_rules! from_str_from_css {
54    ($ty:ident) => {
55        #[cfg(feature = "parse")]
56        impl core::str::FromStr for $ty {
57            type Err = $crate::ParseError;
58            fn from_str(input: &str) -> Result<Self, Self::Err> {
59                parse_css_str_entirely(input)
60            }
61        }
62    };
63}
64pub(crate) use from_str_from_css;
65
66/// Implements the `FromCss` and `FromStr` traits for a simple enum that consists of just keywords
67macro_rules! impl_parse_for_keyword_enum {
68    (
69        // The type name (followed by a comma)
70        $ty:ident,
71
72        // Repeat 1-or-more times
73        $(
74            // 0-or-more metadata attributes (e.g. #[cfg] attributes for conditional compilation)
75            $( #[$meta: meta] )*
76
77            // keyword => enum_variant (e.g. "center" => AlignItems::Center)
78            $keyword:literal => $enum_variant:ident,
79        )+
80    ) => {
81
82        // Impl FromCss for the type
83        impl $crate::util::parse::FromCss for $ty {
84            fn from_css<'i>(input: &mut cssparser::Parser<'i, '_>) -> $crate::util::parse::CssParseResult<'i, Self> {
85
86                // Parse an ident (or return an error)
87                let ident = input.expect_ident()?;
88
89                // Match on the ident (case insensitive)
90                cssparser::match_ignore_ascii_case! { &*ident,
91
92                    // Define each item in the macro definition as a match case
93                    $(
94                        // Add the metadata attributes to the match case
95                        $( #[$meta] )*
96                        // If the keyword matches, return the corresponding variant of the enum
97                        $keyword => return Ok(Self::$enum_variant),
98                    )+
99
100                    // If none of the cases match the input, return an "unexpected token" error
101                    _ => {
102                        let ident = ident.clone();
103                        Err(input.new_unexpected_token_error(cssparser::Token::Ident(ident)))
104                    }
105                }
106            }
107        }
108
109        // Impl FromStr for the type
110        impl core::str::FromStr for $ty {
111            type Err = $crate::ParseError;
112            fn from_str(input: &str) -> Result<Self, Self::Err> {
113                $crate::util::parse::parse_css_str_entirely(input)
114            }
115        }
116    };
117}
118pub(crate) use impl_parse_for_keyword_enum;