Skip to main content

rushdown/parser/
auto_link.rs

1extern crate alloc;
2
3use alloc::string::String;
4
5use crate::ast::{Arena, Link, NodeRef, Text};
6use crate::parser::{Context, InlineParser};
7use crate::scanner::{scan_email, scan_url};
8use crate::text::{self, Reader, Segment};
9
10/// InlineParser that parses auto links.
11#[derive(Debug, Default)]
12pub struct AutoLinkParser {}
13
14impl AutoLinkParser {
15    /// Returns a new [`AutoLinkParser`].
16    pub fn new() -> Self {
17        Self::default()
18    }
19}
20
21impl InlineParser for AutoLinkParser {
22    fn trigger(&self) -> &[u8] {
23        b"<"
24    }
25
26    fn parse(
27        &self,
28        arena: &mut Arena,
29        _parent_ref: NodeRef,
30        reader: &mut text::BlockReader,
31        _ctx: &mut Context,
32    ) -> Option<NodeRef> {
33        let (line, segment) = reader.peek_line_bytes()?;
34        if let Some(p) = scan_url(&line[1..]) {
35            reader.advance(1 + p);
36            if reader.peek_byte() == b'>' {
37                reader.advance(1);
38                let seg: Segment = (segment.start() + 1, segment.start() + p + 1).into();
39                let seg_text: Segment = (segment.start(), segment.start() + p + 2).into();
40                let node_ref = arena.new_node(Link::auto(seg, seg_text));
41                let text_ref = arena.new_node(Text::new(seg));
42                node_ref.append_child_fast(arena, text_ref);
43                return Some(node_ref);
44            }
45        }
46        if let Some(p) = scan_email(&line[1..]) {
47            reader.advance(1 + p);
48            if reader.peek_byte() == b'>' {
49                reader.advance(1);
50                let mut s = String::new();
51                s.push_str("mailto:");
52                let addr = unsafe { core::str::from_utf8_unchecked(&line[1..p + 1]) };
53                let addr_seg: Segment = (segment.start() + 1, segment.start() + p + 1).into();
54                s.push_str(addr);
55                let seg: Segment = (segment.start(), segment.start() + p + 2).into();
56                let node_ref = arena.new_node(Link::auto(s, seg));
57                let text_ref = arena.new_node(Text::new(addr_seg));
58                node_ref.append_child_fast(arena, text_ref);
59                return Some(node_ref);
60            }
61        }
62        None
63    }
64}