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
use crate::constants::{COLON, NEWLINE, RBRACK, SPACE};
use crate::node_pool::NodeID;
use crate::parse::parse_object;
use crate::types::{Cursor, MarkupKind, MatchError, ParseOpts, Parseable, Parser, Result};

// [fn:LABEL]
// [fn:LABEL:DEFINITION]
// [fn::DEFINITION]
#[derive(Debug, Clone)]
pub struct FootnoteRef<'a> {
    pub label: Option<&'a str>,
    pub children: Option<Vec<NodeID>>,
}

impl<'a> Parseable<'a> for FootnoteRef<'a> {
    fn parse(
        parser: &mut Parser<'a>,
        mut cursor: Cursor<'a>,
        parent: Option<NodeID>,
        mut parse_opts: ParseOpts,
    ) -> Result<NodeID> {
        let start = cursor.index;
        cursor.word("[fn:")?;

        // TODO: verify contents of label
        let label_match = cursor.fn_until(|chr| matches!(chr, NEWLINE | COLON | RBRACK | SPACE))?;
        cursor.index = label_match.end;

        match cursor.curr() {
            RBRACK => {
                // [fn:] is not valid
                if label_match.obj.is_empty() {
                    return Err(MatchError::InvalidLogic);
                }

                Ok(parser.alloc(
                    Self {
                        label: Some(label_match.obj),
                        children: None,
                    },
                    start,
                    cursor.index + 1,
                    parent,
                ))
            }
            COLON => {
                cursor.next();
                parse_opts.from_object = false;
                parse_opts.markup.insert(MarkupKind::FootnoteRef);

                let mut content_vec: Vec<NodeID> = Vec::new();
                // if we're being called, that means the first split is the thing
                loop {
                    let begin_def = cursor.index;
                    match parse_object(parser, cursor, parent, parse_opts) {
                        Ok(id) => {
                            cursor.index = parser.pool[id].end;
                            content_vec.push(id);
                        }
                        Err(MatchError::MarkupEnd(kind)) => {
                            if !kind.contains(MarkupKind::FootnoteRef) || cursor.index < start + 2 {
                                return Err(MatchError::InvalidLogic);
                            }

                            // the markup is going to exist,
                            // so update the children's parents
                            let new_id = parser.pool.reserve_id();
                            for id in content_vec.iter_mut() {
                                parser.pool[*id].parent = Some(new_id)
                            }

                            return Ok(parser.alloc_with_id(
                                Self {
                                    label: if label_match.obj.is_empty() {
                                        None
                                    } else {
                                        Some(label_match.obj)
                                    },
                                    children: Some(content_vec),
                                },
                                start,
                                cursor.index + 1,
                                parent,
                                new_id,
                            ));
                        }
                        ret @ Err(_) => {
                            return ret;
                        }
                    }
                }
            }
            _ => Err(MatchError::InvalidLogic),
        }
    }
}