org_rust_parser/object/
footnote_ref.rs

1use crate::constants::{COLON, NEWLINE, RBRACK, SPACE};
2use crate::node_pool::NodeID;
3use crate::parse::parse_object;
4use crate::types::{Cursor, MarkupKind, MatchError, ParseOpts, Parseable, Parser, Result};
5
6// [fn:LABEL]
7// [fn:LABEL:DEFINITION]
8// [fn::DEFINITION]
9#[derive(Debug, Clone)]
10pub struct FootnoteRef<'a> {
11    pub label: Option<&'a str>,
12    pub children: Option<Vec<NodeID>>,
13}
14
15impl<'a> Parseable<'a> for FootnoteRef<'a> {
16    fn parse(
17        parser: &mut Parser<'a>,
18        mut cursor: Cursor<'a>,
19        parent: Option<NodeID>,
20        mut parse_opts: ParseOpts,
21    ) -> Result<NodeID> {
22        let start = cursor.index;
23        cursor.word("[fn:")?;
24
25        // TODO: verify contents of label
26        let label_match = cursor.fn_until(|chr| matches!(chr, NEWLINE | COLON | RBRACK | SPACE))?;
27        cursor.index = label_match.end;
28
29        match cursor.curr() {
30            RBRACK => {
31                // [fn:] is not valid
32                if label_match.obj.is_empty() {
33                    return Err(MatchError::InvalidLogic);
34                }
35
36                Ok(parser.alloc(
37                    Self {
38                        label: Some(label_match.obj),
39                        children: None,
40                    },
41                    start,
42                    cursor.index + 1,
43                    parent,
44                ))
45            }
46            COLON => {
47                cursor.next();
48                parse_opts.from_object = false;
49                parse_opts.markup.insert(MarkupKind::FootnoteRef);
50
51                let mut content_vec: Vec<NodeID> = Vec::new();
52                // if we're being called, that means the first split is the thing
53                loop {
54                    let begin_def = cursor.index;
55                    match parse_object(parser, cursor, parent, parse_opts) {
56                        Ok(id) => {
57                            cursor.index = parser.pool[id].end;
58                            content_vec.push(id);
59                        }
60                        Err(MatchError::MarkupEnd(kind)) => {
61                            if !kind.contains(MarkupKind::FootnoteRef) || cursor.index < start + 2 {
62                                return Err(MatchError::InvalidLogic);
63                            }
64
65                            // the markup is going to exist,
66                            // so update the children's parents
67                            let new_id = parser.pool.reserve_id();
68                            for id in content_vec.iter_mut() {
69                                parser.pool[*id].parent = Some(new_id)
70                            }
71
72                            let ret_id = parser.alloc_with_id(
73                                Self {
74                                    label: if label_match.obj.is_empty() {
75                                        None
76                                    } else {
77                                        Some(label_match.obj)
78                                    },
79                                    children: Some(content_vec),
80                                },
81                                start,
82                                cursor.index + 1,
83                                parent,
84                                new_id,
85                            );
86                            if !label_match.obj.is_empty() {
87                                parser.footnotes.insert(label_match.obj, ret_id);
88                            }
89                            return Ok(ret_id);
90                        }
91                        ret @ Err(_) => {
92                            return ret;
93                        }
94                    }
95                }
96            }
97            _ => Err(MatchError::InvalidLogic),
98        }
99    }
100}