intuicio_core/
meta.rs

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