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
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
use memchr::{memchr, memchr2};

#[cfg_attr(test, derive(PartialEq))]
#[derive(Debug)]
pub struct InlineCall<'a> {
    pub name: &'a str,
    pub args: &'a str,
    // header args for block
    pub inside_header: Option<&'a str>,
    // header args for call line
    pub end_header: Option<&'a str>,
}

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

        let bytes = src.as_bytes();
        let mut pos = memchr2(b'[', b'(', bytes)
            .filter(|&i| bytes[5..i].iter().all(|c| c.is_ascii_graphic()))?;
        let mut pos_;

        let name = &src[5..pos];

        let inside_header = if bytes[pos] == b'[' {
            pos_ = pos;
            pos = memchr(b']', &bytes[pos..])
                .map(|i| i + pos)
                .filter(|&i| bytes[pos..i].iter().all(|&c| c != b'\n'))?
                + 1;
            expect!(src, pos, b'(')?;
            Some(&src[pos_ + 1..pos - 1])
        } else {
            None
        };

        pos_ = pos;
        pos = memchr(b')', &bytes[pos..])
            .map(|i| i + pos)
            .filter(|&i| bytes[pos..i].iter().all(|&c| c != b'\n'))?;
        let args = &src[pos_ + 1..pos];

        let end_header = if src.len() > pos + 1 && src.as_bytes()[pos + 1] == b'[' {
            pos_ = pos;
            pos = memchr(b']', &bytes[pos_ + 1..])
                .map(|i| i + pos_ + 1)
                .filter(|&i| bytes[pos_ + 1..i].iter().all(|&c| c != b'\n' && c != b')'))?;
            Some(&src[pos_ + 2..pos])
        } else {
            None
        };

        Some((
            InlineCall {
                name,
                inside_header,
                args,
                end_header,
            },
            pos + 1,
        ))
    }
}

#[test]
fn parse() {
    assert_eq!(
        InlineCall::parse("call_square(4)").unwrap(),
        (
            InlineCall {
                name: "square",
                args: "4",
                inside_header: None,
                end_header: None,
            },
            "call_square(4)".len()
        )
    );
    assert_eq!(
        InlineCall::parse("call_square[:results output](4)").unwrap(),
        (
            InlineCall {
                name: "square",
                args: "4",
                inside_header: Some(":results output"),
                end_header: None,
            },
            "call_square[:results output](4)".len()
        )
    );
    assert_eq!(
        InlineCall::parse("call_square(4)[:results html]").unwrap(),
        (
            InlineCall {
                name: "square",
                args: "4",
                inside_header: None,
                end_header: Some(":results html"),
            },
            "call_square(4)[:results html]".len()
        )
    );
    assert_eq!(
        InlineCall::parse("call_square[:results output](4)[:results html]").unwrap(),
        (
            InlineCall {
                name: "square",
                args: "4",
                inside_header: Some(":results output"),
                end_header: Some(":results html"),
            },
            "call_square[:results output](4)[:results html]".len()
        )
    );
}