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}