use super::prelude::*;
use crate::parsing::{process_depths, DepthItem, DepthList};
use crate::span_wrap::SpanWrap;
use crate::tree::{ListItem, ListType};
const MAX_LIST_DEPTH: usize = 20;
const fn get_list_type(token: Token) -> Option<ListType> {
match token {
Token::BulletItem => Some(ListType::Bullet),
Token::NumberedItem => Some(ListType::Numbered),
_ => None,
}
}
pub const RULE_LIST: Rule = Rule {
name: "list",
try_consume_fn,
};
fn try_consume_fn<'p, 'r, 't>(
log: &slog::Logger,
parser: &'p mut Parser<'r, 't>,
) -> ParseResult<'r, 't, Elements<'t>> {
debug!(log, "Parsing a list");
assert!(
parser.current().token == Token::InputStart
|| parser.current().token == Token::LineBreak,
"Starting token for list is not start of input or newline",
);
parser.step()?;
let mut depths = Vec::new();
let mut exceptions = Vec::new();
loop {
let current = parser.current();
let depth = match current.token {
Token::Whitespace => {
let spaces = parser.current().slice;
parser.step()?;
spaces.len()
}
Token::BulletItem | Token::NumberedItem => 0,
_ => {
debug!(
log,
"Didn't find correct bullet token or couldn't determine list depth, ending list iteration";
"token" => current.token,
"slice" => current.slice,
"span" => SpanWrap::from(¤t.span),
);
break;
}
};
if depth > MAX_LIST_DEPTH {
info!(
log,
"List item has a depth greater than the maximum! Failing";
"depth" => depth,
"max-depth" => MAX_LIST_DEPTH,
);
return Err(parser.make_warn(ParseWarningKind::ListDepthExceeded));
}
let current = parser.current();
let list_type = match get_list_type(current.token) {
Some(ltype) => ltype,
None => {
debug!(
log,
"Didn't find bullet token, couldn't determine list type, ending list iteration";
"token" => current.token,
"slice" => current.slice,
"span" => SpanWrap::from(¤t.span),
);
break;
}
};
parser.step()?;
debug!(
log,
"Parsing listen item";
"list-type" => list_type.name(),
);
let current = parser.current();
if current.token != Token::Whitespace {
debug!(
log,
"Didn't find whitespace after bullet token, ending list iteration";
"token" => current.token,
"slice" => current.slice,
"span" => SpanWrap::from(¤t.span),
);
break;
}
parser.step()?;
let elements = collect_consume(
log,
parser,
RULE_LIST,
&[
ParseCondition::current(Token::LineBreak),
ParseCondition::current(Token::InputEnd),
],
&[ParseCondition::current(Token::ParagraphBreak)],
None,
)?
.chain(&mut exceptions);
depths.push((depth, list_type, elements));
}
if depths.is_empty() {
return Err(parser.make_warn(ParseWarningKind::RuleFailed));
}
let depth_lists = process_depths(ListType::Generic, depths);
let elements: Vec<Element> = depth_lists
.into_iter()
.map(|(ltype, depth_list)| build_list_element(ltype, depth_list))
.collect();
ok!(elements, exceptions)
}
fn build_list_element(
top_ltype: ListType,
list: DepthList<ListType, Vec<Element>>,
) -> Element {
let build_item = |item| match item {
DepthItem::Item(elements) => ListItem::Elements(elements),
DepthItem::List(ltype, list) => {
ListItem::SubList(build_list_element(ltype, list))
}
};
let items = list.into_iter().map(build_item).collect();
Element::List {
ltype: top_ltype,
items,
}
}