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, PartialOrd, 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    pub fn has_id(&self, name: &str) -> bool {
211        match self {
212            Self::Identifier(value) => value == name,
213            Self::Value(value) => value.as_str() == Some(name),
214            Self::Array(values) => values.iter().any(|meta| meta.has_id(name)),
215            Self::Map(values) => values.iter().any(|(key, _)| key == name),
216            Self::Named(key, _) => key == name,
217        }
218    }
219
220    pub fn extract_by_id(&'_ self, name: &str) -> Option<MetaExtract<'_>> {
221        match self {
222            Self::Identifier(value) => {
223                if value == name {
224                    Some(MetaExtract::Identifier(value.as_str()))
225                } else {
226                    None
227                }
228            }
229            Self::Value(value) => {
230                if value.as_str() == Some(name) {
231                    Some(MetaExtract::Value(value))
232                } else {
233                    None
234                }
235            }
236            Self::Array(values) => values
237                .iter()
238                .filter_map(|meta| meta.extract_by_id(name))
239                .next(),
240            Self::Map(values) => values
241                .iter()
242                .filter_map(|(key, value)| {
243                    if key == name {
244                        Some(MetaExtract::Meta(value))
245                    } else {
246                        None
247                    }
248                })
249                .next(),
250            Self::Named(key, value) => {
251                if key == name {
252                    Some(MetaExtract::Meta(value))
253                } else {
254                    None
255                }
256            }
257        }
258    }
259
260    pub fn items_iter(&'_ self) -> MetaExtractIter<'_> {
261        match self {
262            Self::Identifier(name) => {
263                MetaExtractIter::new(std::iter::once(MetaExtract::Identifier(name.as_str())))
264            }
265            Self::Value(value) => MetaExtractIter::new(std::iter::once(MetaExtract::Value(value))),
266            Self::Array(values) => MetaExtractIter::new(values.iter().map(MetaExtract::Meta)),
267            Self::Map(values) => MetaExtractIter::new(values.values().map(MetaExtract::Meta)),
268            Self::Named(_, value) => {
269                MetaExtractIter::new(std::iter::once(MetaExtract::Meta(value)))
270            }
271        }
272    }
273}
274
275impl std::fmt::Display for Meta {
276    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
277        match self {
278            Self::Identifier(value) => value.fmt(f),
279            Self::Value(value) => value.fmt(f),
280            Self::Array(value) => {
281                f.write_char('[')?;
282                for (index, value) in value.iter().enumerate() {
283                    if index > 0 {
284                        f.write_str(", ")?;
285                    }
286                    value.fmt(f)?;
287                }
288                f.write_char(']')
289            }
290            Self::Map(value) => {
291                f.write_char('{')?;
292                for (index, (key, value)) in value.iter().enumerate() {
293                    if index > 0 {
294                        f.write_str(", ")?;
295                    }
296                    key.fmt(f)?;
297                    f.write_str(": ")?;
298                    value.fmt(f)?;
299                }
300                f.write_char('}')
301            }
302            Self::Named(name, value) => {
303                f.write_str(name)?;
304                f.write_str(" = ")?;
305                value.fmt(f)
306            }
307        }
308    }
309}
310
311#[derive(Debug, PartialEq)]
312pub enum MetaExtract<'a> {
313    Undefined,
314    Identifier(&'a str),
315    Meta(&'a Meta),
316    Value(&'a MetaValue),
317}
318
319impl MetaExtract<'_> {
320    pub fn is_undefined(&self) -> bool {
321        matches!(self, Self::Undefined)
322    }
323
324    pub fn as_identifier(&self) -> Option<&str> {
325        match self {
326            Self::Identifier(value) => Some(*value),
327            _ => None,
328        }
329    }
330
331    pub fn as_value(&self) -> Option<&MetaValue> {
332        match self {
333            Self::Value(value) => Some(*value),
334            _ => None,
335        }
336    }
337
338    pub fn as_meta(&self) -> Option<&Meta> {
339        match self {
340            Self::Meta(value) => Some(*value),
341            _ => None,
342        }
343    }
344}
345
346pub struct MetaExtractIter<'a>(Box<dyn Iterator<Item = MetaExtract<'a>> + 'a>);
347
348impl<'a> MetaExtractIter<'a> {
349    fn new(iter: impl Iterator<Item = MetaExtract<'a>> + 'a) -> Self {
350        Self(Box::new(iter))
351    }
352}
353
354impl<'a> Iterator for MetaExtractIter<'a> {
355    type Item = MetaExtract<'a>;
356
357    fn next(&mut self) -> Option<Self::Item> {
358        self.0.next()
359    }
360}
361
362#[macro_export]
363macro_rules! meta {
364    (@item { $( $key:ident : $item:tt ),* }) => {{
365        #[allow(unused_mut)]
366        let mut result = std::collections::HashMap::default();
367        $(
368            result.insert(
369                stringify!($key).to_owned(),
370                $crate::meta!(@item $item),
371            );
372        )*
373        $crate::meta::Meta::Map(result)
374    }};
375    (@item [ $( $item:tt ),* ]) => {
376        $crate::meta::Meta::Array(vec![ $( $crate::meta!(@item $item) ),* ])
377    };
378    (@item ( $name:ident = $item:tt )) => {
379        $crate::meta::Meta::Named(
380            stringify!($name).to_owned(),
381            Box::new($crate::meta!(@item $item))
382        )
383    };
384    (@item $value:literal) => {
385        $crate::meta::Meta::Value($crate::meta::MetaValue::from($value))
386    };
387    (@item $value:ident) => {
388        $crate::meta::Meta::Identifier(stringify!($value).to_owned())
389    };
390    ($tree:tt) => {
391        $crate::meta!(@item $tree)
392    };
393}
394
395#[cfg(test)]
396mod tests {
397    use super::*;
398
399    #[test]
400    fn test_parser() {
401        println!("{}", Meta::parse("foo").unwrap());
402        println!("{}", Meta::parse("true").unwrap());
403        println!("{}", Meta::parse("42").unwrap());
404        println!("{}", Meta::parse("4.2").unwrap());
405        println!("{}", Meta::parse("'foo'").unwrap());
406        println!("{}", Meta::parse("foo = true").unwrap());
407        println!(
408            "{}",
409            Meta::parse("[true, 42, 4.2, 'foo', foo = true]").unwrap()
410        );
411        println!(
412            "{}",
413            Meta::parse("{bool: true, integer: 42, float: 4.2, string: 'foo', named: foo = true}")
414                .unwrap()
415        );
416    }
417
418    #[test]
419    fn test_macro() {
420        let meta = crate::meta!(foo);
421        assert!(matches!(meta, Meta::Identifier(_)));
422        assert_eq!(meta.as_identifier().unwrap(), "foo");
423
424        let meta = crate::meta!(true);
425        assert!(matches!(meta, Meta::Value(MetaValue::Bool(_))));
426        assert!(meta.as_value().unwrap().as_bool().unwrap());
427
428        let meta = crate::meta!(42);
429        assert!(matches!(meta, Meta::Value(MetaValue::Integer(_))));
430        assert_eq!(meta.as_value().unwrap().as_integer().unwrap(), 42);
431
432        let meta = crate::meta!(4.2);
433        assert!(matches!(meta, Meta::Value(MetaValue::Float(_))));
434        assert_eq!(meta.as_value().unwrap().as_float().unwrap(), 4.2);
435
436        let meta = crate::meta!("foo");
437        assert!(matches!(meta, Meta::Value(MetaValue::String(_))));
438        assert_eq!(meta.as_value().unwrap().as_str().unwrap(), "foo");
439
440        let meta = crate::meta!([]);
441        assert!(matches!(meta, Meta::Array(_)));
442
443        let meta = crate::meta!([true, 42, 4.2, "foo"]);
444        assert!(
445            meta.as_array().unwrap()[0]
446                .as_value()
447                .unwrap()
448                .as_bool()
449                .unwrap()
450        );
451        assert_eq!(
452            meta.as_array().unwrap()[1]
453                .as_value()
454                .unwrap()
455                .as_integer()
456                .unwrap(),
457            42
458        );
459        assert_eq!(
460            meta.as_array().unwrap()[2]
461                .as_value()
462                .unwrap()
463                .as_float()
464                .unwrap(),
465            4.2
466        );
467        assert_eq!(
468            meta.as_array().unwrap()[3]
469                .as_value()
470                .unwrap()
471                .as_str()
472                .unwrap(),
473            "foo"
474        );
475
476        let meta = crate::meta!({});
477        assert!(matches!(meta, Meta::Map(_)));
478
479        let meta = crate::meta!({bool: true, integer: 42, float: 4.2, string: "foo"});
480        assert!(
481            meta.as_map().unwrap()["bool"]
482                .as_value()
483                .unwrap()
484                .as_bool()
485                .unwrap()
486        );
487        assert_eq!(
488            meta.as_map().unwrap()["integer"]
489                .as_value()
490                .unwrap()
491                .as_integer()
492                .unwrap(),
493            42
494        );
495        assert_eq!(
496            meta.as_map().unwrap()["float"]
497                .as_value()
498                .unwrap()
499                .as_float()
500                .unwrap(),
501            4.2
502        );
503        assert_eq!(
504            meta.as_map().unwrap()["string"]
505                .as_value()
506                .unwrap()
507                .as_str()
508                .unwrap(),
509            "foo"
510        );
511
512        let meta = crate::meta!((foo = true));
513        assert!(matches!(meta, Meta::Named(_, _)));
514        assert_eq!(meta.as_named().unwrap().0, "foo");
515        assert!(
516            meta.as_named()
517                .unwrap()
518                .1
519                .as_value()
520                .unwrap()
521                .as_bool()
522                .unwrap()
523        );
524    }
525
526    #[test]
527    fn test_meta_extract() {
528        let meta = crate::meta!(foo);
529        assert!(meta.has_id("foo"));
530        assert_eq!(
531            meta.extract_by_id("foo").unwrap(),
532            MetaExtract::Identifier("foo")
533        );
534        assert_eq!(
535            meta.items_iter().collect::<Vec<_>>(),
536            vec![MetaExtract::Identifier("foo")]
537        );
538
539        let meta = crate::meta!("foo");
540        assert!(meta.has_id("foo"));
541        assert_eq!(
542            meta.extract_by_id("foo").unwrap(),
543            MetaExtract::Value(&MetaValue::String("foo".to_owned()))
544        );
545        assert_eq!(
546            meta.items_iter().collect::<Vec<_>>(),
547            vec![MetaExtract::Value(&MetaValue::String("foo".to_owned()))]
548        );
549
550        let meta = crate::meta!([true, 42, 4.2, "foo"]);
551        assert!(meta.has_id("foo"));
552        assert_eq!(
553            meta.extract_by_id("foo").unwrap(),
554            MetaExtract::Value(&MetaValue::String("foo".to_owned()))
555        );
556        assert_eq!(
557            meta.items_iter().collect::<Vec<_>>(),
558            vec![
559                MetaExtract::Meta(&Meta::Value(MetaValue::Bool(true))),
560                MetaExtract::Meta(&Meta::Value(MetaValue::Integer(42))),
561                MetaExtract::Meta(&Meta::Value(MetaValue::Float(4.2))),
562                MetaExtract::Meta(&Meta::Value(MetaValue::String("foo".to_owned()))),
563            ]
564        );
565
566        let meta = crate::meta!({bool: true, integer: 42, float: 4.2, string: "foo"});
567        assert!(meta.has_id("bool"));
568        assert!(meta.has_id("integer"));
569        assert!(meta.has_id("float"));
570        assert!(meta.has_id("string"));
571        assert_eq!(
572            meta.extract_by_id("bool").unwrap(),
573            MetaExtract::Meta(&Meta::Value(MetaValue::Bool(true)))
574        );
575        assert_eq!(
576            meta.extract_by_id("integer").unwrap(),
577            MetaExtract::Meta(&Meta::Value(MetaValue::Integer(42)))
578        );
579        assert_eq!(
580            meta.extract_by_id("float").unwrap(),
581            MetaExtract::Meta(&Meta::Value(MetaValue::Float(4.2)))
582        );
583        assert_eq!(
584            meta.extract_by_id("string").unwrap(),
585            MetaExtract::Meta(&Meta::Value(MetaValue::String("foo".to_owned())))
586        );
587        let mut result = meta.items_iter().collect::<Vec<_>>();
588        result.sort_by(|a, b| {
589            let a = match a {
590                MetaExtract::Meta(Meta::Value(MetaValue::Bool(_))) => 0,
591                MetaExtract::Meta(Meta::Value(MetaValue::Integer(_))) => 1,
592                MetaExtract::Meta(Meta::Value(MetaValue::Float(_))) => 2,
593                MetaExtract::Meta(Meta::Value(MetaValue::String(_))) => 3,
594                _ => 4,
595            };
596            let b = match b {
597                MetaExtract::Meta(Meta::Value(MetaValue::Bool(_))) => 0,
598                MetaExtract::Meta(Meta::Value(MetaValue::Integer(_))) => 1,
599                MetaExtract::Meta(Meta::Value(MetaValue::Float(_))) => 2,
600                MetaExtract::Meta(Meta::Value(MetaValue::String(_))) => 3,
601                _ => 4,
602            };
603            a.cmp(&b)
604        });
605        assert_eq!(
606            result,
607            vec![
608                MetaExtract::Meta(&Meta::Value(MetaValue::Bool(true))),
609                MetaExtract::Meta(&Meta::Value(MetaValue::Integer(42))),
610                MetaExtract::Meta(&Meta::Value(MetaValue::Float(4.2))),
611                MetaExtract::Meta(&Meta::Value(MetaValue::String("foo".to_owned()))),
612            ]
613        );
614
615        let meta = crate::meta!((foo = true));
616        assert!(meta.has_id("foo"));
617        assert_eq!(
618            meta.extract_by_id("foo").unwrap(),
619            MetaExtract::Meta(&Meta::Value(MetaValue::Bool(true)))
620        );
621        assert_eq!(
622            meta.items_iter().collect::<Vec<_>>(),
623            vec![MetaExtract::Meta(&Meta::Value(MetaValue::Bool(true)))]
624        );
625    }
626}