use crate::ast::Node;
pub(crate) fn node_contains_newline(node: &Node) -> bool {
match node {
Node::Text(s) | Node::InlineCode(s) => s.contains('\n'),
Node::Emphasis(children) | Node::Strong(children) => {
children.iter().any(node_contains_newline)
}
#[cfg(feature = "gfm")]
Node::Strikethrough(children) => children.iter().any(node_contains_newline),
Node::HtmlElement(element) => element.children.iter().any(node_contains_newline),
Node::Link { content, .. } => content.iter().any(node_contains_newline),
Node::Image { alt, .. } => alt.iter().any(node_contains_newline),
Node::SoftBreak | Node::HardBreak => true,
Node::Custom(_) => false,
_ => false,
}
}
pub(crate) fn table_contains_block_elements(headers: &[Node], rows: &[Vec<Node>]) -> bool {
if headers.iter().any(Node::is_block) {
return true;
}
rows.iter().any(|row| row.iter().any(Node::is_block))
}
pub(crate) fn escape_str<E: Escapes>(s: &str) -> std::borrow::Cow<'_, str> {
if E::str_needs_escaping(s) {
std::borrow::Cow::Owned(format!("{}", Escaped::<E>::new(s)))
} else {
std::borrow::Cow::Borrowed(s)
}
}
pub(crate) trait Escapes {
fn str_needs_escaping(s: &str) -> bool;
fn char_needs_escaping(c: char) -> bool;
fn escape_char(c: char) -> Option<&'static str>;
}
pub(crate) struct CommonMarkEscapes;
impl Escapes for CommonMarkEscapes {
fn str_needs_escaping(s: &str) -> bool {
s.chars().any(Self::char_needs_escaping)
}
fn char_needs_escaping(c: char) -> bool {
matches!(c, '\\' | '*' | '_' | '[' | ']' | '<' | '>' | '`')
}
fn escape_char(c: char) -> Option<&'static str> {
match c {
'\\' => Some(r"\\"),
'*' => Some(r"\*"),
'_' => Some(r"\_"),
'[' => Some(r"\["),
']' => Some(r"\]"),
'<' => Some(r"\<"),
'>' => Some(r"\>"),
'`' => Some(r"\`"),
_ => None,
}
}
}
pub(crate) struct Escaped<'a, E: Escapes> {
inner: &'a str,
_phantom: std::marker::PhantomData<E>,
}
impl<'a, E: Escapes> Escaped<'a, E> {
pub fn new(s: &'a str) -> Self {
Self {
inner: s,
_phantom: std::marker::PhantomData,
}
}
}
impl<E: Escapes> std::fmt::Display for Escaped<'_, E> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for c in self.inner.chars() {
if E::char_needs_escaping(c) {
f.write_str(E::escape_char(c).unwrap())?;
} else {
write!(f, "{c}")?;
}
}
Ok(())
}
}