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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
use crate::{
    comment,
    error::PError,
    expr_partial_block, partial, raw,
    source_map::Span,
    strnom::{Cursor, LexError, PResult},
    ErrorMessage, Partial,
};

pub fn parse_partials(rest: &str) -> Result<Vec<Partial>, ErrorMessage<PError>> {
    let (c, res) = eat_partials(Cursor { rest, off: 0 })?;
    if c.is_empty() {
        Ok(res)
    } else {
        let end = (rest.len() - c.len()) as u32;
        Err(ErrorMessage {
            message: PError::Uncompleted,
            span: Span { lo: end, hi: end },
        })
    }
}

fn eat_partials(mut i: Cursor) -> PResult<Vec<Partial>> {
    let mut nodes = vec![];

    loop {
        if let Some(j) = i.find('{') {
            macro_rules! _switch {
                ($n:expr, $t:expr, $ws:expr) => {
                    match $n {
                        b'>' => {
                            let i = i.adv(j + 3 + $t);
                            match partial_block(i, $ws) {
                                Ok((i, n)) => {
                                    nodes.push(n);
                                    i
                                }
                                Err(e @ LexError::Fail(..)) => break Err(e),
                                Err(LexError::Next(..)) => i,
                            }
                        }
                        b'R' => {
                            let i = i.adv(j + 3 + $t);
                            match raw(i, $ws) {
                                Ok((i, _)) => i,
                                Err(e @ LexError::Fail(..)) => break Err(e),
                                Err(LexError::Next(..)) => i,
                            }
                        }
                        b'!' => {
                            let i = i.adv(j + 3);
                            match comment(i) {
                                Ok((i, _)) => i,
                                Err(_) => i,
                            }
                        }
                        b'#' if i.adv(j + 3 + $t).starts_with(">") => {
                            match partial(i.adv(j + 4 + $t), $ws) {
                                Ok((i, n)) => {
                                    nodes.push(n);
                                    i
                                }
                                Err(e @ LexError::Fail(..)) => break Err(e),
                                Err(LexError::Next(..)) => i.adv(j + 3 + $t),
                            }
                        }
                        _ => i.adv(j + 2 + $t),
                    }
                };
            }
            let n = i.rest[j + 1..].as_bytes();
            i = if 2 < n.len() && n[0] == b'{' {
                if n[1] == b'~' {
                    _switch!(n[2], 1, true)
                } else {
                    _switch!(n[1], 0, false)
                }
            } else {
                // next
                i.adv(j + 1)
            };
        } else {
            break Ok((i.adv(i.len()), nodes));
        }
    }
}

#[inline]
fn partial_block(i: Cursor, lws: bool) -> PResult<Partial> {
    match expr_partial_block(i, lws) {
        Ok(_) => Err(LexError::Next(PError::PartialBlock, Span::from(i))),
        Err(_) => partial(i, lws),
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::source_map::{Span, S};

    #[test]
    fn test_empty() {
        let src = r#""#;
        assert_eq!(parse_partials(src).unwrap(), vec![]);
        let src = r#"{{/"#;
        assert_eq!(parse_partials(src).unwrap(), vec![]);
        let src = r#"{{"#;
        assert_eq!(parse_partials(src).unwrap(), vec![]);
        let src = r#"{"#;
        assert_eq!(parse_partials(src).unwrap(), vec![]);
        let src = r#"{{>"#;
        assert_eq!(parse_partials(src).unwrap(), vec![]);
        let src = r#"{{>}}"#;
        assert_eq!(parse_partials(src).unwrap(), vec![]);
        let src = r#"{{! {{> foo }} !}}"#;
        assert_eq!(parse_partials(src).unwrap(), vec![]);
        let src = r#"{{R}} {{> foo }} {{/R}}"#;
        assert_eq!(parse_partials(src).unwrap(), vec![]);
    }

    #[test]
    fn test_partial_block() {
        let src = "{{#> foo }}bar{{/foo }}";
        assert_eq!(
            parse_partials(src).unwrap(),
            vec![Partial(
                (false, false),
                S("foo", Span { lo: 5, hi: 8 }),
                S(vec![], Span { lo: 9, hi: 9 })
            )]
        );
    }
}