Skip to main content

xiv_emote_parser/log_message/ast/
parser.rs

1use super::types::*;
2use crate::log_message::parser::{LogMessageParser, Rule};
3
4use pest_consume::{match_nodes, Error};
5use std::str::FromStr;
6
7type Result<T> = std::result::Result<T, Error<Rule>>;
8type Node<'i> = pest_consume::Node<'i, Rule, ()>;
9
10#[pest_consume::parser]
11impl LogMessageParser {
12    #[allow(dead_code)]
13    fn EOI(_input: Node) -> Result<()> {
14        Ok(())
15    }
16
17    fn text(input: Node) -> Result<String> {
18        Ok(input.as_str().to_string())
19    }
20
21    fn tag_name(input: Node) -> Result<TagName> {
22        TagName::from_str(input.as_str()).map_err(|e| input.error(e))
23    }
24
25    fn func_name(input: Node) -> Result<FuncName> {
26        FuncName::from_str(input.as_str()).map_err(|e| input.error(e))
27    }
28
29    fn param_num(input: Node) -> Result<u32> {
30        input.as_str().parse().map_err(|e| input.error(e))
31    }
32
33    fn param_obj(input: Node) -> Result<Obj> {
34        Obj::from_str(input.as_str()).map_err(|e| input.error(e))
35    }
36
37    fn open_tag(input: Node) -> Result<Tag> {
38        Ok(match_nodes!(input.into_children();
39            [tag_name(name), param(params)..] => Tag { name, params: params.collect() }
40        ))
41    }
42
43    fn auto_closing_tag(input: Node) -> Result<Tag> {
44        Ok(match_nodes!(input.into_children();
45            [tag_name(name), param(params)..] => Tag { name, params: params.collect() }
46        ))
47    }
48
49    fn close_tag(input: Node) -> Result<TagName> {
50        Ok(match_nodes!(input.into_children();
51            [tag_name(name)] => name
52        ))
53    }
54
55    fn if_else_then_content(input: Node) -> Result<IfElseThen> {
56        Ok(match_nodes!(input.into_children();
57            [function(fun)] => IfElseThen::Function(fun),
58            [element(ele)] => IfElseThen::Element(ele),
59            [text(text)] => IfElseThen::Text(text),
60        ))
61    }
62
63    fn if_else_then(input: Node) -> Result<Vec<IfElseThen>> {
64        Ok(match_nodes!(input.into_children();
65            [if_else_then_content(content)..] => content.collect()
66        ))
67    }
68
69    fn if_param(input: Node) -> Result<IfParam> {
70        Ok(match_nodes!(input.into_children();
71            [function(fun)] => IfParam::Function(fun),
72            [auto_closing_tag(tag)] => IfParam::Tag(tag)
73        ))
74    }
75
76    fn if_else_element(input: Node) -> Result<Box<IfElse>> {
77        Ok(match_nodes!(input.into_children();
78            [if_param(if_cond), if_else_then(if_then), if_else_then(else_then)] =>
79                Box::new(IfElse { if_cond, if_then, else_then })
80        ))
81    }
82
83    fn element(input: Node) -> Result<Element> {
84        // lose input when calling into_children, so create this in advance just in case
85        let nonmatch_err = Err(input.error("open and close tags do not match"));
86
87        Ok(match_nodes!(input.into_children();
88            [if_else_element(if_else)] => Element::IfElse(if_else),
89            [open_tag(tag), text(mut text).., close_tag(close_tag)] => if tag.name == close_tag {
90                Element::Tag(tag, text.next())
91            } else {
92                return nonmatch_err;
93            },
94            [auto_closing_tag(tag)] => Element::Tag(tag, None)
95        ))
96    }
97
98    fn function(input: Node) -> Result<Function> {
99        Ok(match_nodes!(input.into_children();
100            [func_name(name), param(params)..] => Function { name, params: params.collect() }
101        ))
102    }
103
104    fn param(input: Node) -> Result<Param> {
105        Ok(match_nodes!(input.into_children();
106            [element(element)] => Param::Element(element),
107            [param_num(num)] => Param::Num(num),
108            [param_obj(obj)] => Param::Obj(obj),
109            [function(func)] => Param::Function(func)
110        ))
111    }
112
113    fn message_part(input: Node) -> Result<MessagePart> {
114        Ok(match_nodes!(input.into_children();
115            [text(text)] => MessagePart::Text(text),
116            [element(element)] => MessagePart::Element(element)
117        ))
118    }
119
120    pub fn message(input: Node) -> Result<Message> {
121        Ok(match_nodes!(input.into_children();
122            [message_part(parts).., _] => Message(parts.collect())
123        ))
124    }
125}