Skip to main content

ferroui_markup/markup/parsers/
container_query_parser.rs

1//! Port of `Markup/Parsers/ContainerQueryParser.cs`: creates a container
2//! query from its text at run time.
3
4use super::{ContainerQueryGrammar, ContainerQuerySyntax};
5use ferroui_base::data::core::ExpressionParseException;
6use ferroui_base::styling::{StyleQueries, StyleQuery};
7use std::fmt;
8
9/// What [`ContainerQueryParser::parse`] fails with: the exceptions the
10/// managed original throws.
11#[derive(Clone, Debug, PartialEq, Eq)]
12pub enum ContainerQueryParserError {
13    /// The text is not a container query (`ExpressionParseException`).
14    Parse(ExpressionParseException),
15    /// The query cannot be created from this syntax
16    /// (`NotSupportedException`).
17    NotSupported(String),
18}
19
20impl fmt::Display for ContainerQueryParserError {
21    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
22        match self {
23            ContainerQueryParserError::Parse(error) => error.fmt(f),
24            ContainerQueryParserError::NotSupported(message) => f.write_str(message),
25        }
26    }
27}
28
29impl std::error::Error for ContainerQueryParserError {}
30
31impl From<ExpressionParseException> for ContainerQueryParserError {
32    fn from(error: ExpressionParseException) -> Self {
33        ContainerQueryParserError::Parse(error)
34    }
35}
36
37/// Parses a [`StyleQuery`] from text.
38#[derive(Clone, Copy, Debug, Default)]
39pub struct ContainerQueryParser;
40
41impl ContainerQueryParser {
42    /// Creates a parser.
43    pub fn new() -> Self {
44        Self
45    }
46
47    /// Parses a [`StyleQuery`] from text. `None` for an empty query.
48    pub fn parse(&self, s: &str) -> Result<Option<StyleQuery>, ContainerQueryParserError> {
49        let syntax = ContainerQueryGrammar::parse(s)?;
50        self.create(&syntax)
51    }
52
53    fn create(&self, syntax: &[ContainerQuerySyntax]) -> Result<Option<StyleQuery>, ContainerQueryParserError> {
54        let mut result: Option<StyleQuery> = None;
55        let mut results: Option<Vec<StyleQuery>> = None;
56
57        for i in syntax {
58            match i {
59                ContainerQuerySyntax::Width { value, operator } => {
60                    result = Some(StyleQueries::width(result, *operator, *value));
61                }
62                ContainerQuerySyntax::Height { value, operator } => {
63                    result = Some(StyleQueries::height(result, *operator, *value));
64                }
65                ContainerQuerySyntax::Or | ContainerQuerySyntax::And => {
66                    let query = result
67                        .take()
68                        .ok_or_else(|| ContainerQueryParserError::NotSupported("Invalid query!".to_string()))?;
69                    results.get_or_insert_with(Vec::new).push(query);
70                }
71            }
72        }
73
74        if let Some(mut results) = results {
75            if let Some(result) = result.take() {
76                results.push(result);
77            }
78
79            result = if results.len() > 1 { Some(StyleQueries::or(results)) } else { results.into_iter().next() };
80        }
81
82        Ok(result)
83    }
84}