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),
}
}
}