use super::utils::{escape_str, CommonMarkEscapes};
use super::CommonMarkWriter;
use crate::ast::Node;
use crate::error::{WriteError, WriteResult};
use ecow::EcoString;
use log;
impl CommonMarkWriter {
pub fn write_text_content(&mut self, content: &str) -> WriteResult<()> {
if self.options.escape_special_chars {
let escaped = escape_str::<CommonMarkEscapes>(content);
self.write_str(&escaped)?
} else {
self.write_str(content)?
}
Ok(())
}
pub fn write_code_content(&mut self, content: &str) -> WriteResult<()> {
self.write_char('`')?;
self.write_str(content)?;
self.write_char('`')?;
Ok(())
}
pub fn write_emphasis(&mut self, content: &[Node]) -> WriteResult<()> {
let delimiter = self.options.emphasis_char.to_string();
self.write_delimited(content, &delimiter)
}
pub fn write_strong(&mut self, content: &[Node]) -> WriteResult<()> {
let char = self.options.strong_char;
let delimiter = format!("{}{}", char, char);
self.write_delimited(content, &delimiter)
}
#[cfg(feature = "gfm")]
pub fn write_strikethrough(&mut self, content: &[Node]) -> WriteResult<()> {
if !self.options.enable_gfm || !self.options.gfm_strikethrough {
for node in content.iter() {
self.write_node_content(node)?;
}
return Ok(());
}
self.write_delimited(content, "~~")
}
pub fn write_link(
&mut self,
url: &str,
title: &Option<EcoString>,
content: &[Node],
) -> WriteResult<()> {
for node in content {
self.check_no_newline(node, "Link content")?;
}
self.write_char('[')?;
for node in content {
self.write_node_content(node)?;
}
self.write_str("](")?;
self.write_str(url)?;
if let Some(title_text) = title {
self.write_str(" \"")?;
self.write_str(title_text)?;
self.write_char('"')?;
}
self.write_char(')')?;
Ok(())
}
pub fn write_image(
&mut self,
url: &str,
title: &Option<EcoString>,
alt: &[Node],
) -> WriteResult<()> {
for node in alt {
self.check_no_newline(node, "Image alt text")?;
}
self.write_str("?;
self.write_str(url)?;
if let Some(title_text) = title {
self.write_str(" \"")?;
self.write_str(title_text)?;
self.write_char('"')?;
}
self.write_char(')')?;
Ok(())
}
pub fn write_soft_break(&mut self) -> WriteResult<()> {
self.write_char('\n')?;
Ok(())
}
pub fn write_hard_break(&mut self) -> WriteResult<()> {
if self.options.hard_break_spaces {
self.write_str(" \n")?;
} else {
self.write_str("\\\n")?;
}
Ok(())
}
pub fn write_autolink(&mut self, url: &str, is_email: bool) -> WriteResult<()> {
if url.contains('\n') {
if self.is_strict_mode() {
return Err(WriteError::NewlineInInlineElement(
"Autolink URL".to_string().into(),
));
} else {
log::warn!(
"Newline character found in autolink URL '{}'. Writing it as is, which might result in an invalid link. Strict mode is off.",
url
);
}
}
self.write_char('<')?;
if !is_email && !url.contains(':') {
self.write_str("https://")?;
}
self.write_str(url)?;
self.write_char('>')?;
Ok(())
}
#[cfg(feature = "gfm")]
pub fn write_extended_autolink(&mut self, url: &str) -> WriteResult<()> {
if !self.options.gfm_autolinks {
self.write_text_content(url)?;
return Ok(());
}
if url.contains('\n') {
if self.is_strict_mode() {
return Err(WriteError::NewlineInInlineElement(
"Extended Autolink URL".to_string().into(),
));
} else {
log::warn!(
"Newline character found in extended autolink URL '{}'. Writing it as is, which might result in an invalid link. Strict mode is off.",
url
);
}
}
self.write_str(url)?;
Ok(())
}
pub fn write_reference_link(&mut self, label: &str, content: &[Node]) -> WriteResult<()> {
for node in content {
self.check_no_newline(node, "Reference Link Text")?;
}
if content.is_empty() {
self.write_char('[')?;
self.write_str(label)?;
self.write_char(']')?;
return Ok(());
}
let is_shortcut =
content.len() == 1 && matches!(&content[0], Node::Text(text) if text == label);
if is_shortcut {
self.write_char('[')?;
self.write_str(label)?;
self.write_char(']')?;
} else {
self.write_char('[')?;
for node in content {
self.write_node_content(node)?;
}
self.write_str("][")?;
self.write_str(label)?;
self.write_char(']')?;
}
Ok(())
}
pub fn write_html_element(&mut self, element: &crate::ast::HtmlElement) -> WriteResult<()> {
if self.options.strict {
if element.tag.contains('<') || element.tag.contains('>') {
return Err(WriteError::InvalidHtmlTag(element.tag.clone()));
}
for attr in &element.attributes {
if attr.name.contains('<') || attr.name.contains('>') {
return Err(WriteError::InvalidHtmlAttribute(attr.name.clone()));
}
}
}
use crate::writer::html::{HtmlWriter, HtmlWriterOptions};
let html_options = if let Some(ref custom_options) = self.options.html_writer_options {
custom_options.clone()
} else {
HtmlWriterOptions {
strict: self.options.strict,
code_block_language_class_prefix: Some("language-".into()),
#[cfg(feature = "gfm")]
enable_gfm: self.options.enable_gfm,
#[cfg(feature = "gfm")]
gfm_disallowed_html_tags: self.options.gfm_disallowed_html_tags.clone(),
}
};
let mut html_writer = HtmlWriter::with_options(html_options);
html_writer.write_node_internal(&Node::HtmlElement(element.clone()))?;
let html_output = html_writer.into_string();
self.write_str(&html_output)
}
}