use arena_tree::Node;
use std::cell::RefCell;
#[derive(Debug, Clone)]
pub enum NodeValue {
Document,
BlockQuote,
List(NodeList),
Item(NodeList),
DescriptionList,
DescriptionItem(NodeDescriptionItem),
DescriptionTerm,
DescriptionDetails,
CodeBlock(NodeCodeBlock),
HtmlBlock(NodeHtmlBlock),
Paragraph,
Heading(NodeHeading),
ThematicBreak,
FootnoteDefinition(Vec<u8>),
Table(Vec<TableAlignment>),
TableRow(bool),
TableCell,
Text(Vec<u8>),
TaskItem(bool),
SoftBreak,
LineBreak,
Code(Vec<u8>),
HtmlInline(Vec<u8>),
Emph,
Strong,
Strikethrough,
Superscript,
Link(NodeLink),
Image(NodeLink),
FootnoteReference(Vec<u8>),
}
#[derive(Debug, Copy, Clone)]
pub enum TableAlignment {
None,
Left,
Center,
Right,
}
#[derive(Debug, Clone)]
pub struct NodeLink {
pub url: Vec<u8>,
pub title: Vec<u8>,
}
#[derive(Debug, Default, Clone, Copy)]
pub struct NodeList {
pub list_type: ListType,
#[doc(hidden)]
pub marker_offset: usize,
#[doc(hidden)]
pub padding: usize,
pub start: usize,
pub delimiter: ListDelimType,
pub bullet_char: u8,
pub tight: bool,
}
#[derive(Debug, Default, Clone, Copy)]
pub struct NodeDescriptionItem {
#[doc(hidden)]
pub marker_offset: usize,
#[doc(hidden)]
pub padding: usize,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ListType {
Bullet,
Ordered,
}
impl Default for ListType {
fn default() -> ListType {
ListType::Bullet
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ListDelimType {
Period,
Paren,
}
impl Default for ListDelimType {
fn default() -> ListDelimType {
ListDelimType::Period
}
}
#[derive(Default, Debug, Clone)]
pub struct NodeCodeBlock {
pub fenced: bool,
pub fence_char: u8,
pub fence_length: usize,
#[doc(hidden)]
pub fence_offset: usize,
pub info: Vec<u8>,
pub literal: Vec<u8>,
}
#[derive(Default, Debug, Clone, Copy)]
pub struct NodeHeading {
pub level: u32,
pub setext: bool,
}
#[derive(Debug, Default, Clone)]
pub struct NodeHtmlBlock {
#[doc(hidden)]
pub block_type: u8,
pub literal: Vec<u8>,
}
impl NodeValue {
pub fn block(&self) -> bool {
match *self {
NodeValue::Document
| NodeValue::BlockQuote
| NodeValue::FootnoteDefinition(_)
| NodeValue::List(..)
| NodeValue::DescriptionList
| NodeValue::DescriptionItem(_)
| NodeValue::DescriptionTerm
| NodeValue::DescriptionDetails
| NodeValue::Item(..)
| NodeValue::CodeBlock(..)
| NodeValue::HtmlBlock(..)
| NodeValue::Paragraph
| NodeValue::Heading(..)
| NodeValue::ThematicBreak
| NodeValue::Table(..)
| NodeValue::TableRow(..)
| NodeValue::TableCell => true,
_ => false,
}
}
#[doc(hidden)]
pub fn accepts_lines(&self) -> bool {
match *self {
NodeValue::Paragraph | NodeValue::Heading(..) | NodeValue::CodeBlock(..) => true,
_ => false,
}
}
pub fn contains_inlines(&self) -> bool {
match *self {
NodeValue::Paragraph | NodeValue::Heading(..) | NodeValue::TableCell => true,
_ => false,
}
}
pub fn text(&self) -> Option<&Vec<u8>> {
match *self {
NodeValue::Text(ref t) => Some(t),
_ => None,
}
}
pub fn text_mut(&mut self) -> Option<&mut Vec<u8>> {
match *self {
NodeValue::Text(ref mut t) => Some(t),
_ => None,
}
}
}
#[derive(Debug, Clone)]
pub struct Ast {
pub value: NodeValue,
pub start_line: u32,
#[doc(hidden)]
pub content: Vec<u8>,
#[doc(hidden)]
pub open: bool,
#[doc(hidden)]
pub last_line_blank: bool,
}
impl Ast {
pub fn new(value: NodeValue) -> Self {
Ast {
value: value,
content: vec![],
start_line: 0,
open: true,
last_line_blank: false,
}
}
}
pub type AstNode<'a> = Node<'a, RefCell<Ast>>;
impl<'a> From<NodeValue> for AstNode<'a> {
fn from(value: NodeValue) -> Self {
Node::new(RefCell::new(Ast::new(value)))
}
}
#[doc(hidden)]
pub fn last_child_is_open<'a>(node: &'a AstNode<'a>) -> bool {
node.last_child().map_or(false, |n| n.data.borrow().open)
}
#[doc(hidden)]
pub fn can_contain_type<'a>(node: &'a AstNode<'a>, child: &NodeValue) -> bool {
if let NodeValue::Document = *child {
return false;
}
match node.data.borrow().value {
NodeValue::Document
| NodeValue::BlockQuote
| NodeValue::FootnoteDefinition(_)
| NodeValue::DescriptionTerm
| NodeValue::DescriptionDetails
| NodeValue::Item(..) => {
child.block() && match *child {
NodeValue::Item(..) => false,
_ => true,
}
}
NodeValue::List(..) => match *child {
NodeValue::Item(..) => true,
_ => false,
},
NodeValue::DescriptionList => match *child {
NodeValue::DescriptionItem(_) => true,
_ => false,
},
NodeValue::DescriptionItem(_) => match *child {
NodeValue::DescriptionTerm | NodeValue::DescriptionDetails => true,
_ => false,
},
NodeValue::Paragraph
| NodeValue::Heading(..)
| NodeValue::Emph
| NodeValue::Strong
| NodeValue::Link(..)
| NodeValue::Image(..) => !child.block(),
NodeValue::Table(..) => match *child {
NodeValue::TableRow(..) => true,
_ => false,
},
NodeValue::TableRow(..) => match *child {
NodeValue::TableCell => true,
_ => false,
},
NodeValue::TableCell => match *child {
NodeValue::Text(..)
| NodeValue::Code(..)
| NodeValue::Emph
| NodeValue::Strong
| NodeValue::Link(..)
| NodeValue::Image(..)
| NodeValue::Strikethrough
| NodeValue::HtmlInline(..) => true,
_ => false,
},
_ => false,
}
}
#[doc(hidden)]
pub fn ends_with_blank_line<'a>(node: &'a AstNode<'a>) -> bool {
let mut it = Some(node);
while let Some(cur) = it {
if cur.data.borrow().last_line_blank {
return true;
}
match cur.data.borrow().value {
NodeValue::List(..) | NodeValue::Item(..) => it = cur.last_child(),
_ => it = None,
};
}
false
}
#[doc(hidden)]
pub fn containing_block<'a>(node: &'a AstNode<'a>) -> Option<&'a AstNode<'a>> {
let mut ch = Some(node);
while let Some(n) = ch {
if n.data.borrow().value.block() {
return Some(n);
}
ch = n.parent();
}
None
}