⎇ 70341c9f697b610470b0806a53266aa75343563b
src/util/parse.rs
Ñawpa willay
Pi imata llamkarqan
ch'uya
Add support for parsing Taffy style types from string (#929)
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;