use cmark_writer::HtmlAttribute;
use cmark_writer::HtmlElement;
use cmark_writer::HtmlWriteResult;
use cmark_writer::HtmlWriter;
use cmark_writer::HtmlWriterOptions;
use cmark_writer::WriteResult;
use cmark_writer::ast::Node;
use cmark_writer::custom_node;
use cmark_writer::writer::{BlockWriterProxy, InlineWriterProxy};
use ecow::EcoString;
use ecow::eco_format;
use std::path::PathBuf;
use crate::Result;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ListState {
Ordered,
Unordered,
}
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
pub enum Format {
#[default]
Md,
LaTeX,
Text,
#[cfg(feature = "docx")]
Docx,
}
#[derive(Debug, PartialEq, Clone)]
#[custom_node(block = true, html_impl = true)]
pub struct FigureNode {
pub body: Box<Node>,
pub caption: String,
}
impl FigureNode {
fn write_custom(&self, writer: &mut BlockWriterProxy) -> WriteResult<()> {
let content = writer.capture_block(|block| {
block.write_block(&self.body)?;
Ok(())
})?;
writer.write_str(&content)?;
writer.write_str("\n")?;
writer.write_str(&self.caption)?;
Ok(())
}
fn write_html_custom(&self, writer: &mut HtmlWriter) -> HtmlWriteResult<()> {
let body = self.body.clone();
let node = Node::HtmlElement(HtmlElement {
tag: EcoString::inline("figure"),
attributes: vec![HtmlAttribute {
name: EcoString::inline("class"),
value: EcoString::inline("figure"),
}],
children: vec![*body],
self_closing: false,
});
writer.write_node(&node)?;
Ok(())
}
}
#[derive(Debug, PartialEq, Clone)]
#[custom_node(block = true, html_impl = true)]
pub struct ExternalFrameNode {
pub file_path: PathBuf,
pub alt_text: EcoString,
pub svg: String,
}
impl ExternalFrameNode {
fn write_custom(&self, writer: &mut BlockWriterProxy) -> WriteResult<()> {
writer.write_str(&format!(
"",
self.alt_text,
self.file_path.display()
))?;
Ok(())
}
fn write_html_custom(&self, writer: &mut HtmlWriter) -> HtmlWriteResult<()> {
let node = Node::HtmlElement(HtmlElement {
tag: EcoString::inline("img"),
attributes: vec![
HtmlAttribute {
name: EcoString::inline("src"),
value: self.file_path.display().to_string().into(),
},
HtmlAttribute {
name: EcoString::inline("alt"),
value: self.alt_text.clone(),
},
],
children: vec![],
self_closing: true,
});
writer.write_node(&node)?;
Ok(())
}
}
#[derive(Debug, PartialEq, Clone)]
#[custom_node(block = false, html_impl = true)]
pub struct HighlightNode {
pub content: Vec<Node>,
}
impl HighlightNode {
fn write_custom(&self, writer: &mut InlineWriterProxy) -> WriteResult<()> {
writer.write_str("==")?;
writer.write_inline_nodes(&self.content)?;
writer.write_str("==")?;
Ok(())
}
fn write_html_custom(&self, writer: &mut HtmlWriter) -> HtmlWriteResult<()> {
let node = Node::HtmlElement(HtmlElement {
tag: EcoString::inline("mark"),
attributes: vec![],
children: self.content.clone(),
self_closing: false,
});
writer.write_node(&node)?;
Ok(())
}
}
#[derive(Debug, PartialEq, Clone)]
#[custom_node(block = true, html_impl = true)]
pub struct CenterNode {
pub node: Node,
}
impl CenterNode {
pub fn new(children: Vec<Node>) -> Self {
CenterNode {
node: Node::HtmlElement(cmark_writer::ast::HtmlElement {
tag: EcoString::inline("p"),
attributes: vec![cmark_writer::ast::HtmlAttribute {
name: EcoString::inline("align"),
value: EcoString::inline("center"),
}],
children,
self_closing: false,
}),
}
}
fn write_custom(&self, writer: &mut BlockWriterProxy) -> WriteResult<()> {
let content = writer.capture_inline(|inline| {
inline.write_inline(&self.node)?;
Ok(())
})?;
writer.write_str(&content)?;
writer.write_str("\n")?;
Ok(())
}
fn write_html_custom(&self, writer: &mut HtmlWriter) -> HtmlWriteResult<()> {
let mut temp_writer = HtmlWriter::with_options(HtmlWriterOptions {
strict: false,
..Default::default()
});
temp_writer.write_node(&self.node)?;
let content = temp_writer.into_string()?;
writer.write_trusted_html(&content)?;
Ok(())
}
}
#[derive(Debug, PartialEq, Clone)]
#[custom_node(block = false, html_impl = true)]
pub struct InlineNode {
pub content: Vec<Node>,
}
impl InlineNode {
fn write_custom(&self, writer: &mut InlineWriterProxy) -> WriteResult<()> {
writer.write_inline_nodes(&self.content)
}
fn write_html_custom(&self, writer: &mut HtmlWriter) -> HtmlWriteResult<()> {
for node in &self.content {
writer.write_node(node)?;
}
Ok(())
}
}
#[derive(Debug, PartialEq, Clone)]
#[custom_node(block = false, html_impl = true)]
pub struct VerbatimNode {
pub content: EcoString,
}
impl VerbatimNode {
fn write_custom(&self, writer: &mut InlineWriterProxy) -> WriteResult<()> {
writer.write_str(&self.content)
}
fn write_html_custom(&self, writer: &mut HtmlWriter) -> HtmlWriteResult<()> {
writer.write_trusted_html(&self.content)
}
}
#[derive(Debug, PartialEq, Clone)]
#[custom_node(block = true, html_impl = true)]
pub struct BlockVerbatimNode {
pub content: EcoString,
}
impl BlockVerbatimNode {
fn write_custom(&self, writer: &mut BlockWriterProxy) -> WriteResult<()> {
writer.write_str("\n\n")?;
writer.write_str(&self.content)?;
writer.write_str("\n\n")
}
fn write_html_custom(&self, writer: &mut HtmlWriter) -> HtmlWriteResult<()> {
writer.write_trusted_html("\n\n")?;
writer.write_trusted_html(&self.content)?;
writer.write_trusted_html("\n\n")
}
}
#[derive(Debug, PartialEq, Clone)]
#[custom_node(block = true, html_impl = false)]
pub struct AlertNode {
pub content: Vec<Node>,
pub class: EcoString,
}
impl AlertNode {
fn write_custom(&self, writer: &mut BlockWriterProxy) -> WriteResult<()> {
let quote = Node::BlockQuote(vec![
Node::Paragraph(vec![Node::Text(eco_format!(
"[!{}]",
self.class.to_ascii_uppercase()
))]),
Node::Paragraph(vec![Node::Text("".into())]),
]);
writer.with_temporary_options(
|options| options.escape_special_chars = false,
|writer| writer.write_block("e),
)?;
let quote = Node::BlockQuote(self.content.clone());
writer.write_block("e)?;
Ok(())
}
}
pub trait FormatWriter {
fn write_eco(&mut self, document: &Node, output: &mut EcoString) -> Result<()>;
fn write_vec(&mut self, document: &Node) -> Result<Vec<u8>>;
}