1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
use jetscii::Substring;
use memchr::memchr;

#[cfg_attr(test, derive(PartialEq))]
#[derive(Debug)]
pub struct Snippet<'a> {
    pub name: &'a str,
    pub value: &'a str,
}

impl<'a> Snippet<'a> {
    pub fn parse(src: &'a str) -> Option<(Snippet<'a>, usize)> {
        debug_assert!(src.starts_with("@@"));

        let name = memchr(b':', src.as_bytes()).filter(|&i| {
            i != 2
                && src.as_bytes()[2..i]
                    .iter()
                    .all(|&c| c.is_ascii_alphanumeric() || c == b'-')
        })?;

        let end = Substring::new("@@")
            .find(&src[name + 1..])
            .map(|i| i + name + 1)?;

        Some((
            Snippet {
                name: &src[2..name],
                value: &src[name + 1..end],
            },
            end + 2,
        ))
    }
}

#[test]
fn parse() {
    assert_eq!(
        Snippet::parse("@@html:<b>@@").unwrap(),
        (
            Snippet {
                name: "html",
                value: "<b>"
            },
            "@@html:<b>@@".len()
        )
    );
    assert_eq!(
        Snippet::parse("@@latex:any arbitrary LaTeX code@@").unwrap(),
        (
            Snippet {
                name: "latex",
                value: "any arbitrary LaTeX code"
            },
            "@@latex:any arbitrary LaTeX code@@".len()
        )
    );
    assert_eq!(
        Snippet::parse("@@html:@@").unwrap(),
        (
            Snippet {
                name: "html",
                value: ""
            },
            "@@html:@@".len()
        )
    );
    assert!(Snippet::parse("@@html:<b>@").is_none());
    assert!(Snippet::parse("@@html<b>@@").is_none());
    assert!(Snippet::parse("@@:<b>@@").is_none());
}