yamd/parser/
code.rs

1use crate::{lexer::TokenKind, nodes::Code};
2
3use super::Parser;
4
5pub(crate) fn code(p: &mut Parser<'_>) -> Option<Code> {
6    let start_pos = p.pos();
7    let lang = p
8        .advance_or_backtrack(|t| t.kind == TokenKind::Eol)
9        .map(|(_, end)| end)?;
10
11    let Some((start, end)) = p.advance_until(
12        |t| t.kind == TokenKind::Backtick && t.position.column == 0 && t.slice.len() == 3,
13        true,
14    ) else {
15        p.backtrack(start_pos);
16        p.flip_to_literal_at(start_pos);
17        return None;
18    };
19
20    p.next_token();
21    Some(Code::new(
22        p.range_to_string(start_pos + 1..lang),
23        p.range_to_string(start..end - 1),
24    ))
25}
26
27#[cfg(test)]
28mod tests {
29
30    use crate::{
31        lexer::{Position, Token, TokenKind},
32        nodes::Code,
33        parser::Parser,
34    };
35
36    use super::code;
37    use pretty_assertions::assert_eq;
38
39    #[test]
40    fn happy_path() {
41        let mut p = Parser::new("```rust\nprintln!(\"hello\");\n```");
42        assert_eq!(
43            code(&mut p),
44            Some(Code::new("rust", "println!(\"hello\");"))
45        );
46    }
47
48    #[test]
49    fn eol_before_lang() {
50        let mut p = Parser::new("```\nprintln!(\"hello\");\n```");
51        assert_eq!(code(&mut p), Some(Code::new("", "println!(\"hello\");")));
52    }
53
54    #[test]
55    fn terminator_before_lang() {
56        let mut p = Parser::new("```\n\nprintln!(\"hello\");\n```");
57        assert_eq!(code(&mut p), None);
58        assert_eq!(
59            p.peek(),
60            Some((
61                &Token::new(TokenKind::Literal, "```", Position::default()),
62                0
63            ))
64        )
65    }
66
67    #[test]
68    fn do_not_have_closing_token() {
69        let mut p = Parser::new("```\nprintln!(\"hello\");\n``");
70        assert_eq!(code(&mut p), None);
71        assert_eq!(
72            p.peek(),
73            Some((
74                &Token::new(TokenKind::Literal, "```", Position::default()),
75                0
76            ))
77        );
78    }
79
80    #[test]
81    fn terminator_in_the_middle_and_do_not_have_closing_token() {
82        let mut p = Parser::new("```\nprintln!(\"hello\");\n\n\n``");
83        assert_eq!(code(&mut p), None);
84        assert_eq!(
85            p.peek(),
86            Some((
87                &Token::new(TokenKind::Literal, "```", Position::default()),
88                0
89            ))
90        );
91    }
92}