intuicio_core/
meta.rs

1use pest::{Parser, iterators::Pair};
2use pest_derive::Parser;
3use serde::{Deserialize, Serialize};
4use std::{collections::HashMap, fmt::Write};
5
6#[derive(Parser)]
7#[grammar = "meta.pest"]
8struct MetaParser;
9
10impl MetaParser {
11    fn parse_main(content: &str) -> Result<Meta, String> {
12        match Self::parse(Rule::main, content) {
13            Ok(mut pairs) => {
14                let pair = pairs.next().unwrap().into_inner().next().unwrap();
15                match pair.as_rule() {
16                    Rule::meta => Ok(Self::parse_meta(pair)),
17                    rule => unreachable!("{:?}", rule),
18                }
19            }
20            Err(error) => Err(format!("{}", error)),
21        }
22    }
23
24    fn parse_meta(pair: Pair<Rule>) -> Meta {
25        let pair = pair.into_inner().next().unwrap();
26        match pair.as_rule() {
27            Rule::identifier => Meta::Identifier(Self::parse_identifier(pair)),
28            Rule::value => Meta::Value(Self::parse_value(pair)),
29            Rule::array => Meta::Array(Self::parse_array(pair)),
30            Rule::map => Meta::Map(Self::parse_map(pair)),
31            Rule::named => {
32                let (id, meta) = Self::parse_named(pair);
33                Meta::Named(id, Box::new(meta))
34            }
35            rule => unreachable!("{:?}", rule),
36        }
37    }
38
39    fn parse_identifier(pair: Pair<Rule>) -> String {
40        pair.as_str().to_owned()
41    }
42
43    fn parse_value(pair: Pair<Rule>) -> MetaValue {
44        let pair = pair.into_inner().next().unwrap();
45        match pair.as_rule() {
46            Rule::literal_bool => MetaValue::Bool(pair.as_str().parse::<bool>().unwrap()),
47            Rule::literal_integer => MetaValue::Integer(pair.as_str().parse::<i64>().unwrap()),
48            Rule::literal_float => MetaValue::Float(pair.as_str().parse::<f64>().unwrap()),
49            Rule::literal_string => {
50                MetaValue::String(pair.into_inner().next().unwrap().as_str().to_owned())
51            }
52            rule => unreachable!("{:?}", rule),
53        }
54    }
55
56    fn parse_array(pair: Pair<Rule>) -> Vec<Meta> {
57        pair.into_inner().map(Self::parse_meta).collect()
58    }
59
60    fn parse_map(pair: Pair<Rule>) -> HashMap<String, Meta> {
61        pair.into_inner()
62            .map(|pair| {
63                let mut pairs = pair.into_inner();
64                (
65                    Self::parse_identifier(pairs.next().unwrap()),
66                    Self::parse_meta(pairs.next().unwrap()),
67                )
68            })
69            .collect()
70    }
71
72    fn parse_named(pair: Pair<Rule>) -> (String, Meta) {
73        let mut pairs = pair.into_inner();
74        (
75            Self::parse_identifier(pairs.next().unwrap()),
76            Self::parse_meta(pairs.next().unwrap()),
77        )
78    }
79}
80
81#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
82pub enum MetaValue {
83    Bool(bool),
84    Integer(i64),
85    Float(f64),
86    String(String),
87}
88
89impl MetaValue {
90    pub fn as_bool(&self) -> Option<bool> {
91        match self {
92            Self::Bool(value) => Some(*value),
93            _ => None,
94        }
95    }
96
97    pub fn as_integer(&self) -> Option<i64> {
98        match self {
99            Self::Integer(value) => Some(*value),
100            _ => None,
101        }
102    }
103
104    pub fn as_float(&self) -> Option<f64> {
105        match self {
106            Self::Float(value) => Some(*value),
107            _ => None,
108        }
109    }
110
111    pub fn as_str(&self) -> Option<&str> {
112        match self {
113            Self::String(value) => Some(value.as_str()),
114            _ => None,
115        }
116    }
117
118    pub fn as_string(&self) -> Option<String> {
119        match self {
120            Self::String(value) => Some(value.to_owned()),
121            _ => None,
122        }
123    }
124}
125
126impl std::fmt::Display for MetaValue {
127    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
128        match self {
129            Self::Bool(value) => value.fmt(f),
130            Self::Integer(value) => value.fmt(f),
131            Self::Float(value) => value.fmt(f),
132            Self::String(value) => f.write_fmt(format_args!("{:?}", value)),
133        }
134    }
135}
136
137impl From<bool> for MetaValue {
138    fn from(value: bool) -> Self {
139        Self::Bool(value)
140    }
141}
142
143impl From<i64> for MetaValue {
144    fn from(value: i64) -> Self {
145        Self::Integer(value)
146    }
147}
148
149impl From<f64> for MetaValue {
150    fn from(value: f64) -> Self {
151        Self::Float(value)
152    }
153}
154
155impl From<&str> for MetaValue {
156    fn from(value: &str) -> Self {
157        Self::String(value.to_owned())
158    }
159}
160
161#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
162pub enum Meta {
163    Identifier(String),
164    Value(MetaValue),
165    Array(Vec<Meta>),
166    Map(HashMap<String, Meta>),
167    Named(String, Box<Meta>),
168}
169
170impl Meta {
171    pub fn parse(content: &str) -> Result<Self, String> {
172        MetaParser::parse_main(content)
173    }
174
175    pub fn as_identifier(&self) -> Option<&str> {
176        match self {
177            Self::Identifier(value) => Some(value.as_str()),
178            _ => None,
179        }
180    }
181
182    pub fn as_value(&self) -> Option<&MetaValue> {
183        match self {
184            Self::Value(value) => Some(value),
185            _ => None,
186        }
187    }
188
189    pub fn as_array(&self) -> Option<&Vec<Meta>> {
190        match self {
191            Self::Array(value) => Some(value),
192            _ => None,
193        }
194    }
195
196    pub fn as_map(&self) -> Option<&HashMap<String, Meta>> {
197        match self {
198            Self::Map(value) => Some(value),
199            _ => None,
200        }
201    }
202
203    pub fn as_named(&self) -> Option<(&str, &Meta)> {
204        match self {
205            Self::Named(name, value) => Some((name.as_str(), value)),
206            _ => None,
207        }
208    }
209}
210
211impl std::fmt::Display for Meta {
212    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
213        match self {
214            Self::Identifier(value) => value.fmt(f),
215            Self::Value(value) => value.fmt(f),
216            Self::Array(value) => {
217                f.write_char('[')?;
218                for (index, value) in value.iter().enumerate() {
219                    if index > 0 {
220                        f.write_str(", ")?;
221                    }
222                    value.fmt(f)?;
223                }
224                f.write_char(']')
225            }
226            Self::Map(value) => {
227                f.write_char('{')?;
228                for (index, (key, value)) in value.iter().enumerate() {
229                    if index > 0 {
230                        f.write_str(", ")?;
231                    }
232                    key.fmt(f)?;
233                    f.write_str(": ")?;
234                    value.fmt(f)?;
235                }
236                f.write_char('}')
237            }
238            Self::Named(name, value) => {
239                f.write_str(name)?;
240                f.write_str(" = ")?;
241                value.fmt(f)
242            }
243        }
244    }
245}
246
247#[macro_export]
248macro_rules! meta {
249    (@item { $( $key:ident : $item:tt ),* }) => {{
250        #[allow(unused_mut)]
251        let mut result = std::collections::HashMap::default();
252        $(
253            result.insert(
254                stringify!($key).to_owned(),
255                $crate::meta!(@item $item),
256            );
257        )*
258        $crate::meta::Meta::Map(result)
259    }};
260    (@item [ $( $item:tt ),* ]) => {
261        $crate::meta::Meta::Array(vec![ $( $crate::meta!(@item $item) ),* ])
262    };
263    (@item ( $name:ident = $item:tt )) => {
264        $crate::meta::Meta::Named(
265            stringify!($name).to_owned(),
266            Box::new($crate::meta!(@item $item))
267        )
268    };
269    (@item $value:literal) => {
270        $crate::meta::Meta::Value($crate::meta::MetaValue::from($value))
271    };
272    (@item $value:ident) => {
273        $crate::meta::Meta::Identifier(stringify!($value).to_owned())
274    };
275    ($tree:tt) => {
276        $crate::meta!(@item $tree)
277    };
278}
279
280#[cfg(test)]
281mod tests {
282    use super::*;
283
284    #[test]
285    fn test_parser() {
286        println!("{}", MetaParser::parse_main("foo").unwrap());
287        println!("{}", MetaParser::parse_main("true").unwrap());
288        println!("{}", MetaParser::parse_main("42").unwrap());
289        println!("{}", MetaParser::parse_main("4.2").unwrap());
290        println!("{}", MetaParser::parse_main("'foo'").unwrap());
291        println!(
292            "{}",
293            MetaParser::parse_main("[true, 42, 4.2, 'foo']").unwrap()
294        );
295        println!(
296            "{}",
297            MetaParser::parse_main("{bool: true, integer: 42, float: 4.2, string: 'foo'}").unwrap()
298        );
299        println!("{}", MetaParser::parse_main("foo = true").unwrap());
300    }
301
302    #[test]
303    fn test_macro() {
304        let meta = crate::meta!(foo);
305        assert!(matches!(meta, Meta::Identifier(_)));
306        assert_eq!(meta.as_identifier().unwrap(), "foo");
307        let meta = crate::meta!(true);
308        assert!(matches!(meta, Meta::Value(MetaValue::Bool(_))));
309        assert!(meta.as_value().unwrap().as_bool().unwrap());
310        let meta = crate::meta!(42);
311        assert!(matches!(meta, Meta::Value(MetaValue::Integer(_))));
312        assert_eq!(meta.as_value().unwrap().as_integer().unwrap(), 42);
313        let meta = crate::meta!(4.2);
314        assert!(matches!(meta, Meta::Value(MetaValue::Float(_))));
315        assert_eq!(meta.as_value().unwrap().as_float().unwrap(), 4.2);
316        let meta = crate::meta!("foo");
317        assert!(matches!(meta, Meta::Value(MetaValue::String(_))));
318        assert_eq!(meta.as_value().unwrap().as_str().unwrap(), "foo");
319        let meta = crate::meta!([]);
320        assert!(matches!(meta, Meta::Array(_)));
321        let meta = crate::meta!([true, 42, 4.2, "foo"]);
322        assert!(
323            meta.as_array().unwrap()[0]
324                .as_value()
325                .unwrap()
326                .as_bool()
327                .unwrap()
328        );
329        assert_eq!(
330            meta.as_array().unwrap()[1]
331                .as_value()
332                .unwrap()
333                .as_integer()
334                .unwrap(),
335            42
336        );
337        assert_eq!(
338            meta.as_array().unwrap()[2]
339                .as_value()
340                .unwrap()
341                .as_float()
342                .unwrap(),
343            4.2
344        );
345        assert_eq!(
346            meta.as_array().unwrap()[3]
347                .as_value()
348                .unwrap()
349                .as_str()
350                .unwrap(),
351            "foo"
352        );
353        let meta = crate::meta!({});
354        assert!(matches!(meta, Meta::Map(_)));
355        let meta = crate::meta!({bool: true, integer: 42, float: 4.2, string: "foo"});
356        assert!(
357            meta.as_map().unwrap()["bool"]
358                .as_value()
359                .unwrap()
360                .as_bool()
361                .unwrap()
362        );
363        assert_eq!(
364            meta.as_map().unwrap()["integer"]
365                .as_value()
366                .unwrap()
367                .as_integer()
368                .unwrap(),
369            42
370        );
371        assert_eq!(
372            meta.as_map().unwrap()["float"]
373                .as_value()
374                .unwrap()
375                .as_float()
376                .unwrap(),
377            4.2
378        );
379        assert_eq!(
380            meta.as_map().unwrap()["string"]
381                .as_value()
382                .unwrap()
383                .as_str()
384                .unwrap(),
385            "foo"
386        );
387        let meta = crate::meta!((foo = true));
388        assert!(matches!(meta, Meta::Named(_, _)));
389        assert_eq!(meta.as_named().unwrap().0, "foo");
390        assert!(
391            meta.as_named()
392                .unwrap()
393                .1
394                .as_value()
395                .unwrap()
396                .as_bool()
397                .unwrap()
398        );
399    }
400}