Skip to main content

panache_parser/syntax/
alerts.rs

1//! Alert block AST node wrappers.
2
3use super::ast::support;
4use super::{AstChildren, AstNode, PanacheLanguage, Paragraph, SyntaxKind, SyntaxNode};
5
6#[derive(Debug, Clone, PartialEq, Eq, Hash)]
7pub struct Alert(SyntaxNode);
8
9impl AstNode for Alert {
10    type Language = PanacheLanguage;
11
12    fn can_cast(kind: SyntaxKind) -> bool {
13        kind == SyntaxKind::ALERT
14    }
15
16    fn cast(syntax: SyntaxNode) -> Option<Self> {
17        Self::can_cast(syntax.kind()).then(|| Self(syntax))
18    }
19
20    fn syntax(&self) -> &SyntaxNode {
21        &self.0
22    }
23}
24
25impl Alert {
26    pub fn marker(&self) -> Option<String> {
27        self.0
28            .children_with_tokens()
29            .filter_map(|child| child.into_token())
30            .find(|token| token.kind() == SyntaxKind::ALERT_MARKER)
31            .map(|token| token.text().to_string())
32    }
33
34    pub fn paragraphs(&self) -> AstChildren<Paragraph> {
35        support::children(&self.0)
36    }
37}
38
39#[cfg(test)]
40mod tests {
41    use super::*;
42    use crate::options::{Extensions, Flavor, ParserOptions};
43    use crate::parse;
44
45    #[test]
46    fn alert_wrapper_extracts_marker_and_paragraphs() {
47        let mut extensions = Extensions::for_flavor(Flavor::Gfm);
48        extensions.alerts = true;
49        let config = ParserOptions {
50            flavor: Flavor::Gfm,
51            extensions,
52            ..Default::default()
53        };
54        let tree = parse("> [!NOTE]\n> Heads up\n> More context\n", Some(config));
55
56        let alert = tree.descendants().find_map(Alert::cast).expect("alert");
57        assert_eq!(alert.marker().as_deref(), Some("[!NOTE]"));
58        let paragraphs = alert.paragraphs().collect::<Vec<_>>();
59        assert_eq!(paragraphs.len(), 1);
60        let text = paragraphs[0].syntax().text().to_string();
61        assert!(text.contains("Heads up"));
62        assert!(text.contains("More context"));
63    }
64}