use super::custom::CustomNode;
use super::html::HtmlElement;
use ecow::EcoString;
use std::boxed::Box;
#[derive(Debug, Clone, PartialEq, Default)]
pub enum CodeBlockType {
Indented,
#[default]
Fenced,
}
#[derive(Debug, Clone, PartialEq, Default)]
pub enum HeadingType {
#[default]
Atx,
Setext,
}
#[cfg(feature = "gfm")]
#[derive(Debug, Clone, PartialEq, Default)]
pub enum TableAlignment {
#[default]
Left,
Center,
Right,
None,
}
#[cfg(feature = "gfm")]
#[derive(Debug, Clone, PartialEq)]
pub enum TaskListStatus {
Checked,
Unchecked,
}
#[derive(Debug, Clone, PartialEq)]
pub enum Node {
Document(Vec<Node>),
ThematicBreak,
Heading {
level: u8,
content: Vec<Node>,
heading_type: HeadingType,
},
CodeBlock {
language: Option<EcoString>,
content: EcoString,
block_type: CodeBlockType,
},
HtmlBlock(EcoString),
LinkReferenceDefinition {
label: EcoString,
destination: EcoString,
title: Option<EcoString>,
},
Paragraph(Vec<Node>),
BlockQuote(Vec<Node>),
OrderedList {
start: u32,
items: Vec<ListItem>,
},
UnorderedList(Vec<ListItem>),
Table {
headers: Vec<Node>,
#[cfg(feature = "gfm")]
alignments: Vec<TableAlignment>,
rows: Vec<Vec<Node>>,
},
InlineCode(EcoString),
Emphasis(Vec<Node>),
Strong(Vec<Node>),
Strikethrough(Vec<Node>),
Link {
url: EcoString,
title: Option<EcoString>,
content: Vec<Node>,
},
ReferenceLink {
label: EcoString,
content: Vec<Node>,
},
Image {
url: EcoString,
title: Option<EcoString>,
alt: Vec<Node>,
},
Autolink {
url: EcoString,
is_email: bool,
},
ExtendedAutolink(EcoString),
HtmlElement(HtmlElement),
HardBreak,
SoftBreak,
Text(EcoString),
Custom(Box<dyn CustomNode>),
}
impl Default for Node {
fn default() -> Self {
Node::Document(vec![])
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum ListItem {
Unordered {
content: Vec<Node>,
},
Ordered {
number: Option<u32>,
content: Vec<Node>,
},
#[cfg(feature = "gfm")]
Task {
status: TaskListStatus,
content: Vec<Node>,
},
}
impl Node {
pub fn is_block(&self) -> bool {
matches!(
self,
Node::Document(_)
| Node::ThematicBreak
| Node::Heading { .. }
| Node::CodeBlock { .. }
| Node::HtmlBlock(_)
| Node::LinkReferenceDefinition { .. }
| Node::Paragraph(_)
| Node::BlockQuote(_)
| Node::OrderedList { .. }
| Node::UnorderedList(_)
| Node::Table { .. }
| Node::Custom(_)
)
}
pub fn is_inline(&self) -> bool {
matches!(
self,
Node::InlineCode(_)
| Node::Emphasis(_)
| Node::Strong(_)
| Node::Strikethrough(_)
| Node::Link { .. }
| Node::ReferenceLink { .. }
| Node::Image { .. }
| Node::Autolink { .. }
| Node::ExtendedAutolink(_)
| Node::HtmlElement(_)
| Node::HardBreak
| Node::SoftBreak
| Node::Text(_)
| Node::Custom(_)
)
}
pub fn heading(level: u8, content: Vec<Node>) -> Self {
Node::Heading {
level,
content,
heading_type: HeadingType::default(),
}
}
pub fn code_block(language: Option<EcoString>, content: EcoString) -> Self {
Node::CodeBlock {
language,
content,
block_type: CodeBlockType::default(),
}
}
pub fn strikethrough(content: Vec<Node>) -> Self {
Node::Strikethrough(content)
}
#[cfg(feature = "gfm")]
pub fn task_list_item(status: TaskListStatus, content: Vec<Node>) -> Self {
Node::UnorderedList(vec![ListItem::Task { status, content }])
}
#[cfg(feature = "gfm")]
pub fn table_with_alignment(
headers: Vec<Node>,
alignments: Vec<TableAlignment>,
rows: Vec<Vec<Node>>,
) -> Self {
Node::Table {
headers,
alignments,
rows,
}
}
pub fn as_custom_type<T: CustomNode + 'static>(&self) -> Option<&T> {
if let Node::Custom(node) = self {
node.as_any().downcast_ref::<T>()
} else {
None
}
}
pub fn is_custom_type<T: CustomNode + 'static>(&self) -> bool {
self.as_custom_type::<T>().is_some()
}
}