use super::Handle;
use crate::markdown::{
state::{Info, State},
util::longest_char_streak::longest_char_streak,
};
use crate::{
mdast::{Frontmatter, Node},
message::Message,
};
use alloc::{
boxed::Box,
format,
string::{String, ToString},
};
impl Handle for Frontmatter {
fn handle(
&self,
_state: &mut State,
_info: &Info,
_parent: Option<&Node>,
_node: &Node,
) -> Result<String, Message> {
if !matches!(self.fence, '`' | '~' | '-' | '+')
|| self.info.trim_matches([' ', '\t']).is_empty()
|| self.info.contains(['\r', '\n'])
|| (self.fence == '`' && self.info.contains('`'))
{
return Err(Message {
place: None,
reason:
"Frontmatter requires a valid fence and a nonempty, single-line info string"
.into(),
rule_id: Box::new("invalid-frontmatter".into()),
source: Box::new("mdast-util-to-markdown".into()),
});
}
let mut marker = self.fence;
if matches!(marker, '-' | '+') {
let delimiter = marker.to_string().repeat(3);
if self
.value
.split(['\r', '\n'])
.any(|line| line.trim_end_matches([' ', '\t']) == delimiter)
{
marker = '~';
}
}
let (opening_length, closing_length) = if matches!(marker, '-' | '+') {
(3, 3)
} else {
let minimum = (longest_char_streak(&self.value, marker) + 1).max(3);
let opening = self.fence_length.max(minimum);
(opening, self.closing_fence_length.max(opening))
};
let opening = marker.to_string().repeat(opening_length);
let closing = marker.to_string().repeat(closing_length);
let info = self.info.trim_matches([' ', '\t']);
let separator = if matches!(marker, '-' | '+') || info.starts_with(marker) {
" "
} else {
""
};
let mut output = format!("{opening}{separator}{}\n", info);
output.push_str(&self.value);
if !self.value.is_empty() && !self.value.ends_with(['\r', '\n']) {
output.push('\n');
}
output.push_str(&closing);
Ok(output)
}
}