Skip to main content

matugen_parser/filters/
helpers.rs

1use colorsys::{Hsl, Rgb};
2
3use crate::Value;
4
5#[cfg(feature = "filter-docs")]
6#[derive(Debug, Clone)]
7pub struct FilterDoc {
8    pub name: &'static str,
9    pub category: &'static str,
10    pub description: &'static str,
11}
12
13#[cfg(feature = "filter-docs")]
14use std::sync::{Mutex, OnceLock};
15
16#[cfg(feature = "filter-docs")]
17pub static FILTER_DOCS: OnceLock<Mutex<Vec<FilterDoc>>> = OnceLock::new();
18
19#[cfg(feature = "filter-docs")]
20pub fn filter_docs() -> Vec<FilterDoc> {
21    FILTER_DOCS
22        .get_or_init(|| Mutex::new(Vec::new()))
23        .lock()
24        .unwrap()
25        .clone()
26}
27
28#[cfg(feature = "filter-docs")]
29#[macro_export]
30macro_rules! __register_filter_doc {
31    ($name:expr, $category:expr, $doc:expr) => {{
32        $crate::parser::helpers::FILTER_DOCS
33            .get_or_init(|| std::sync::Mutex::new(Vec::new()))
34            .lock()
35            .unwrap()
36            .push($crate::parser::helpers::FilterDoc {
37                name: $name,
38                category: $category,
39                description: $doc,
40            });
41    }};
42}
43
44#[cfg(not(feature = "filter-docs"))]
45#[macro_export]
46macro_rules! __register_filter_doc {
47    ($name:expr, $category:expr, $doc:expr) => {};
48}
49
50#[macro_export]
51macro_rules! register_filters {
52    (($engine:expr) {
53        $(
54            $category:literal => {
55                $(
56                    $(#[doc = $doc:literal])*
57                    $name:literal => $func:path
58                ),* $(,)?
59            }
60        ),* $(,)?
61    }) => {{
62        $(
63            $(
64                $engine.add_filter($name, $func);
65
66                $crate::__register_filter_doc!(
67                    $name,
68                    $category,
69                    concat!($($doc, "\n"),*)
70                );
71            )*
72        )*
73    }};
74}
75
76// #[cfg(feature = "filter-docs")]
77// pub fn filters_to_html() -> String {
78//     let mut out = String::new();
79
80//     for doc in filter_docs() {
81//         out.push_str(&format!(
82//             "<div class='filter-doc' data-type={}>
83//     <h2>{}</h2>
84//     {}
85// </div>\n",
86//             doc.category, doc.name, doc.description
87//         ));
88//     }
89
90//     out
91// }
92
93#[cfg(feature = "filter-docs")]
94pub fn filters_to_html() -> String {
95    use std::collections::BTreeMap;
96
97    let mut grouped: BTreeMap<&str, Vec<&FilterDoc>> = BTreeMap::new();
98    let docs = filter_docs();
99
100    for doc in docs.iter() {
101        grouped.entry(doc.category).or_default().push(doc);
102    }
103
104    let mut out = String::new();
105
106    for (category, docs) in grouped {
107        out.push_str(&format!("<h2>{}</h2><md-divider></md-divider>\n", category));
108
109        for doc in docs {
110            out.push_str(&format!(
111                "<div class='filter-doc'>
112    <h3>{}</h3>
113    {}
114</div>\n",
115                doc.name, doc.description
116            ));
117        }
118    }
119
120    out
121}
122
123#[macro_export]
124macro_rules! expect_args {
125    ($args:expr, $( $ty:ty ),* $(,)?) => {{
126        let expected_len = [$(
127            stringify!($ty)
128        ),*].len();
129        if $args.len() < expected_len {
130            return Err(
131                $crate::FilterError::NotEnoughArguments,
132            );
133        }
134
135        let mut _i = 0;
136        (
137            $(
138                {
139                    let spanned = &$args[_i];
140                    _i += 1;
141                    match <$ty as $crate::helpers::ExpectFromValue>::expect_from(&spanned.value) {
142                        Ok(v) => v,
143                        Err(actual) => {
144                            return Err(
145                                $crate::FilterError::InvalidArgumentType {
146                                    span: spanned.span,
147                                    expected: stringify!($ty).to_string(),
148                                    actual,
149                                }
150                            )
151                    }}
152                }
153            ),*
154        )
155    }};
156}
157
158pub trait ExpectFromValue: Sized {
159    fn expect_from(value: &Value) -> Result<Self, String>;
160}
161
162impl ExpectFromValue for String {
163    fn expect_from(value: &Value) -> Result<Self, String> {
164        match value {
165            Value::Ident(s) => Ok(s.clone()),
166            other => Err(other.variant_name()),
167        }
168    }
169}
170
171impl ExpectFromValue for i64 {
172    fn expect_from(value: &Value) -> Result<Self, String> {
173        match value {
174            Value::Int(i) => Ok(*i),
175            other => Err(other.variant_name()),
176        }
177    }
178}
179
180impl ExpectFromValue for f64 {
181    fn expect_from(value: &Value) -> Result<Self, String> {
182        match value {
183            Value::Float(f) => Ok(*f),
184            Value::Int(i) => Ok(*i as f64),
185            other => Err(other.variant_name()),
186        }
187    }
188}
189
190impl ExpectFromValue for Rgb {
191    fn expect_from(value: &Value) -> Result<Self, String> {
192        match value {
193            Value::Color(color) => Ok(color.clone()),
194            Value::LazyColor { color, scheme: _ } => Ok(color.clone()),
195            other => Err(other.variant_name()),
196        }
197    }
198}
199
200impl ExpectFromValue for Hsl {
201    fn expect_from(value: &Value) -> Result<Self, String> {
202        match value {
203            Value::HslColor(color) => Ok(color.clone()),
204            other => Err(other.variant_name()),
205        }
206    }
207}