1use super::MjNavbarChild;
2use crate::comment::Comment;
3use crate::mj_navbar_link::NAME as MJ_NAVBAR_LINK;
4#[cfg(feature = "async")]
5use crate::prelude::parser::{AsyncMrmlParser, AsyncParseChildren, AsyncParseElement};
6use crate::prelude::parser::{
7    Error, MrmlCursor, MrmlParser, MrmlToken, ParseChildren, ParseElement,
8};
9
10impl ParseChildren<Vec<MjNavbarChild>> for MrmlParser<'_> {
11    fn parse_children(&self, cursor: &mut MrmlCursor<'_>) -> Result<Vec<MjNavbarChild>, Error> {
12        let mut result = Vec::new();
13
14        loop {
15            match cursor.assert_next()? {
16                MrmlToken::Comment(inner) => {
17                    result.push(MjNavbarChild::Comment(Comment::from(inner.text.as_str())));
18                }
19                MrmlToken::ElementStart(inner) => {
20                    if inner.local.as_str() == MJ_NAVBAR_LINK {
21                        result.push(MjNavbarChild::MjNavbarLink(
22                            self.parse(cursor, inner.local)?,
23                        ));
24                    } else {
25                        return Err(Error::UnexpectedElement {
26                            origin: cursor.origin(),
27                            position: inner.span.into(),
28                        });
29                    }
30                }
31                MrmlToken::ElementClose(inner) => {
32                    cursor.rewind(MrmlToken::ElementClose(inner));
33                    return Ok(result);
34                }
35                other => {
36                    return Err(Error::UnexpectedToken {
37                        origin: cursor.origin(),
38                        position: other.span(),
39                    })
40                }
41            }
42        }
43    }
44}
45
46#[cfg(feature = "async")]
47#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))]
48#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)]
49impl AsyncParseChildren<Vec<MjNavbarChild>> for AsyncMrmlParser {
50    async fn async_parse_children<'a>(
51        &self,
52        cursor: &mut MrmlCursor<'a>,
53    ) -> Result<Vec<MjNavbarChild>, Error> {
54        let mut result = Vec::new();
55
56        loop {
57            match cursor.assert_next()? {
58                MrmlToken::Comment(inner) => {
59                    result.push(MjNavbarChild::Comment(Comment::from(inner.text.as_str())));
60                }
61                MrmlToken::ElementStart(inner) => {
62                    if inner.local.as_str() == MJ_NAVBAR_LINK {
63                        result.push(MjNavbarChild::MjNavbarLink(
64                            self.async_parse(cursor, inner.local).await?,
65                        ));
66                    } else {
67                        return Err(Error::UnexpectedElement {
68                            origin: cursor.origin(),
69                            position: inner.span.into(),
70                        });
71                    }
72                }
73                MrmlToken::ElementClose(inner) => {
74                    cursor.rewind(MrmlToken::ElementClose(inner));
75                    return Ok(result);
76                }
77                other => {
78                    return Err(Error::UnexpectedToken {
79                        origin: cursor.origin(),
80                        position: other.span(),
81                    })
82                }
83            }
84        }
85    }
86}
87
88#[cfg(test)]
89mod tests {
90    use crate::mj_navbar::MjNavbar;
91
92    macro_rules! assert_success {
93        ($title:ident, $template:expr) => {
94            crate::should_sync_parse!($title, MjNavbar, $template);
95        };
96    }
97
98    macro_rules! assert_fail {
99        ($title:ident, $template:expr, $error:expr) => {
100            crate::should_not_sync_parse!($title, MjNavbar, $template, $error);
101        };
102    }
103
104    assert_success!(should_handle_empty_children, "<mj-navbar />");
105
106    assert_success!(
107        should_handle_comments,
108        "<mj-navbar><!-- comment --></mj-navbar>"
109    );
110
111    assert_fail!(
112        should_error_with_text,
113        "<mj-navbar>Hello</mj-navbar>",
114        "UnexpectedToken { origin: Root, position: Span { start: 11, end: 16 } }"
115    );
116
117    assert_fail!(
118        should_error_with_other_element,
119        "<mj-navbar><span /></mj-navbar>",
120        "UnexpectedElement { origin: Root, position: Span { start: 11, end: 16 } }"
121    );
122}