use super::prelude::*;
use crate::parsing::{process_depths, DepthItem, DepthList};
use crate::span_wrap::SpanWrap;
use crate::tree::{AttributeMap, StyledContainer, StyledContainerType};
const MAX_BLOCKQUOTE_DEPTH: usize = 30;
pub const RULE_BLOCKQUOTE: Rule = Rule {
name: "blockquote",
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 nested native blockquotes");
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::Quote => current.slice.len(),
_ => {
debug!(
log,
"Didn't find blockquote token, ending list iteration";
"token" => current.token,
"slice" => current.slice,
"span" => SpanWrap::from(¤t.span),
);
break;
}
};
parser.step()?;
parser.get_optional_space()?;
if depth > MAX_BLOCKQUOTE_DEPTH {
info!(
log,
"Native blockquote has a depth greater than the maximum! Failing";
"depth" => depth,
"max-depth" => MAX_BLOCKQUOTE_DEPTH,
);
return Err(parser.make_warn(ParseWarningKind::BlockquoteDepthExceeded));
}
let mut elements = collect_consume(
log,
parser,
RULE_BLOCKQUOTE,
&[
ParseCondition::current(Token::LineBreak),
ParseCondition::current(Token::InputEnd),
],
&[ParseCondition::current(Token::ParagraphBreak)],
None,
)?
.chain(&mut exceptions);
elements.push(Element::LineBreak);
depths.push((depth - 1, (), elements))
}
if depths.is_empty() {
return Err(parser.make_warn(ParseWarningKind::RuleFailed));
}
let depth_lists = process_depths((), depths);
let elements: Vec<Element> = depth_lists
.into_iter()
.map(|(_, depth_list)| build_blockquote_element(depth_list))
.collect();
ok!(elements, exceptions)
}
fn build_blockquote_element(list: DepthList<(), Vec<Element>>) -> Element {
let mut all_elements = Vec::new();
macro_rules! remove_trailing_line_break {
() => {
if let Some(Element::LineBreak) = all_elements.last() {
all_elements.pop();
}
};
}
for item in list {
match item {
DepthItem::Item(mut elements) => all_elements.append(&mut elements),
DepthItem::List(_, list) => {
remove_trailing_line_break!();
let element = build_blockquote_element(list);
all_elements.push(element);
}
}
}
remove_trailing_line_break!();
let paragraph = Container::new(ContainerType::Paragraph, all_elements);
Element::StyledContainer(StyledContainer::new(
StyledContainerType::Blockquote,
vec![Element::Container(paragraph)],
AttributeMap::new(),
))
}