use alloc::{string::String, sync::Arc, vec::Vec};
use crate::diagnostic::Diagnostic;
use crate::parse::Definitions;
use crate::span::Span;
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct NodeMeta {
pub span: Option<Span>,
}
impl NodeMeta {
pub const fn new(span: Option<Span>) -> Self {
Self { span }
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct Document {
pub meta: NodeMeta,
pub children: Vec<Block>,
source: Option<Arc<str>>,
}
impl Document {
pub fn new(children: Vec<Block>) -> Self {
Self {
meta: NodeMeta::default(),
children,
source: None,
}
}
pub(crate) fn parsed(meta: NodeMeta, children: Vec<Block>, source: &str) -> Self {
Self {
meta,
children,
source: Some(Arc::from(source)),
}
}
pub fn source(&self) -> Option<&str> {
self.source.as_deref()
}
#[must_use]
pub fn without_source(mut self) -> Self {
self.source = None;
self
}
}
impl From<Vec<Block>> for Document {
fn from(children: Vec<Block>) -> Self {
Self::new(children)
}
}
#[derive(Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct NodePath(pub Vec<usize>);
impl From<Vec<usize>> for NodePath {
fn from(indices: Vec<usize>) -> Self {
Self(indices)
}
}
impl From<&[usize]> for NodePath {
fn from(indices: &[usize]) -> Self {
Self(indices.into())
}
}
impl<const N: usize> From<[usize; N]> for NodePath {
fn from(indices: [usize; N]) -> Self {
Self(indices.into())
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Block {
Paragraph(Paragraph),
Heading(Heading),
ThematicBreak(ThematicBreak),
BlockQuote(BlockQuote),
Alert(Alert),
List(List),
CodeBlock(CodeBlock),
HtmlBlock(HtmlBlock),
HtmlContainer(HtmlContainer),
Definition(Definition),
FootnoteDefinition(FootnoteDefinition),
Table(Table),
MathBlock(MathBlock),
Frontmatter(Frontmatter),
LeafDirective(LeafDirective),
ContainerDirective(ContainerDirective),
BlankLine(BlankLine),
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Paragraph {
pub meta: NodeMeta,
pub children: Vec<Inline>,
pub(crate) source: Option<String>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Heading {
pub meta: NodeMeta,
pub depth: u8,
pub children: Vec<Inline>,
pub(crate) source: Option<String>,
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct BlankLine {
pub meta: NodeMeta,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ThematicBreak {
pub meta: NodeMeta,
pub marker: ThematicBreakMarker,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ThematicBreakMarker {
Dash,
Asterisk,
Underscore,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct BlockQuote {
pub meta: NodeMeta,
pub children: Vec<Block>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Alert {
pub meta: NodeMeta,
pub kind: AlertKind,
pub title: Option<String>,
pub children: Vec<Block>,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum AlertKind {
Note,
Tip,
Important,
Warning,
Caution,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct List {
pub meta: NodeMeta,
pub ordered: bool,
pub delimiter: ListDelimiter,
pub tight: bool,
pub children: Vec<ListItem>,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ListDelimiter {
Dash,
Asterisk,
Plus,
Period,
Paren,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ListItem {
pub meta: NodeMeta,
pub checked: Option<bool>,
pub number: Option<u64>,
pub children: Vec<Block>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct CodeBlock {
pub meta: NodeMeta,
pub kind: CodeBlockKind,
pub info: Option<String>,
pub value: String,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum CodeBlockKind {
Fenced {
marker: FenceMarker,
length: usize,
},
Indented,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum FenceMarker {
Backtick,
Tilde,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct HtmlBlock {
pub meta: NodeMeta,
pub value: String,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct HtmlContainer {
pub meta: NodeMeta,
pub opening: HtmlTag,
pub content: HtmlContainerContent,
pub closing: HtmlTag,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct HtmlTag {
pub meta: NodeMeta,
pub name: String,
pub raw: String,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum HtmlContainerContent {
Blocks(Vec<Block>),
Inlines(Vec<Inline>),
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Definition {
pub meta: NodeMeta,
pub label: String,
pub identifier: String,
pub destination: String,
pub destination_kind: LinkDestinationKind,
pub title: Option<Title>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct FootnoteDefinition {
pub meta: NodeMeta,
pub label: String,
pub identifier: String,
pub children: Vec<Block>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Table {
pub meta: NodeMeta,
pub alignments: Vec<TableAlignment>,
pub rows: Vec<TableRow>,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum TableAlignment {
None,
Left,
Center,
Right,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct TableRow {
pub meta: NodeMeta,
pub cells: Vec<TableCell>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct TableCell {
pub meta: NodeMeta,
pub children: Vec<Inline>,
pub(crate) source: Option<String>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct MathBlock {
pub meta: NodeMeta,
pub value: String,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Frontmatter {
pub meta: NodeMeta,
pub kind: FrontmatterKind,
pub value: String,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum FrontmatterKind {
Yaml,
Toml,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct LeafDirective {
pub meta: NodeMeta,
pub name: String,
pub label: Vec<Inline>,
pub attributes: Vec<DirectiveAttribute>,
pub(crate) label_source: Option<String>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ContainerDirective {
pub meta: NodeMeta,
pub name: String,
pub label: Vec<Inline>,
pub attributes: Vec<DirectiveAttribute>,
pub children: Vec<Block>,
pub(crate) label_source: Option<String>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Inline {
Text(Text),
Escape(Escape),
CharacterReference(CharacterReference),
Emphasis(Emphasis),
Strong(Strong),
Delete(Delete),
Mark(Mark),
Shortcode(Shortcode),
Code(CodeInline),
Link(Link),
Autolink(Autolink),
Image(Image),
LinkReference(LinkReference),
ImageReference(ImageReference),
Html(HtmlInline),
SoftBreak(SoftBreak),
LineBreak(LineBreak),
Math(MathInline),
FootnoteReference(FootnoteReference),
InlineFootnote(InlineFootnote),
WikiLink(WikiLink),
TextDirective(TextDirective),
Tag(Tag),
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Text {
pub meta: NodeMeta,
pub value: String,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Escape {
pub meta: NodeMeta,
pub value: char,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct CharacterReference {
pub meta: NodeMeta,
pub reference: String,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Emphasis {
pub meta: NodeMeta,
pub delimiter: EmphasisDelimiter,
pub children: Vec<Inline>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Strong {
pub meta: NodeMeta,
pub delimiter: EmphasisDelimiter,
pub children: Vec<Inline>,
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum EmphasisDelimiter {
#[default]
Asterisk,
Underscore,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Delete {
pub meta: NodeMeta,
pub children: Vec<Inline>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Mark {
pub meta: NodeMeta,
pub children: Vec<Inline>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Shortcode {
pub meta: NodeMeta,
pub name: String,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Tag {
pub meta: NodeMeta,
pub name: String,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct CodeInline {
pub meta: NodeMeta,
pub value: String,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Link {
pub meta: NodeMeta,
pub destination: String,
pub destination_kind: LinkDestinationKind,
pub title: Option<Title>,
pub children: Vec<Inline>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Autolink {
pub meta: NodeMeta,
pub form: AutolinkForm,
pub text: String,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum AutolinkForm {
Angle,
Literal,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Image {
pub meta: NodeMeta,
pub destination: String,
pub destination_kind: LinkDestinationKind,
pub title: Option<Title>,
pub alt: Vec<Inline>,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum LinkDestinationKind {
Bare,
Angle,
Omitted,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum LinkTitleKind {
DoubleQuote,
SingleQuote,
Paren,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Title {
pub value: String,
pub kind: LinkTitleKind,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct LinkReference {
pub meta: NodeMeta,
pub identifier: String,
pub label: String,
pub kind: ReferenceKind,
pub children: Vec<Inline>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ImageReference {
pub meta: NodeMeta,
pub identifier: String,
pub label: String,
pub kind: ReferenceKind,
pub alt: Vec<Inline>,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ReferenceKind {
Full,
Collapsed,
Shortcut,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct HtmlInline {
pub meta: NodeMeta,
pub value: String,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct SoftBreak {
pub meta: NodeMeta,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct LineBreak {
pub meta: NodeMeta,
pub kind: LineBreakKind,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum LineBreakKind {
Backslash,
Spaces,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum MathInlineKind {
Dollar {
dollars: u8,
},
Code,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct MathInline {
pub meta: NodeMeta,
pub value: String,
pub kind: MathInlineKind,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct FootnoteReference {
pub meta: NodeMeta,
pub label: String,
pub identifier: String,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct InlineFootnote {
pub meta: NodeMeta,
pub children: Vec<Inline>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct WikiLink {
pub meta: NodeMeta,
pub target: String,
pub label: String,
pub embed: bool,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct TextDirective {
pub meta: NodeMeta,
pub name: String,
pub label: Vec<Inline>,
pub attributes: Vec<DirectiveAttribute>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct DirectiveAttribute {
pub name: String,
pub value: Option<String>,
}
macro_rules! impl_meta_accessors {
($enum:ident { $($variant:ident),+ $(,)? }) => {
impl $enum {
pub fn meta(&self) -> &NodeMeta {
match self { $( $enum::$variant(node) => &node.meta, )+ }
}
pub fn span(&self) -> Option<Span> {
self.meta().span
}
}
};
}
macro_rules! impl_from_variants {
($enum:ident { $($variant:ident($ty:ty)),+ $(,)? }) => {
$(
impl From<$ty> for $enum {
fn from(node: $ty) -> Self {
$enum::$variant(node)
}
}
)+
};
}
impl_meta_accessors!(Block {
Paragraph,
Heading,
ThematicBreak,
BlockQuote,
Alert,
List,
CodeBlock,
HtmlBlock,
HtmlContainer,
Definition,
FootnoteDefinition,
Table,
MathBlock,
Frontmatter,
LeafDirective,
ContainerDirective,
BlankLine,
});
impl_from_variants!(Block {
Paragraph(Paragraph), Heading(Heading), ThematicBreak(ThematicBreak),
BlockQuote(BlockQuote), Alert(Alert), List(List),
CodeBlock(CodeBlock), HtmlBlock(HtmlBlock), Definition(Definition),
HtmlContainer(HtmlContainer),
FootnoteDefinition(FootnoteDefinition), Table(Table), MathBlock(MathBlock),
Frontmatter(Frontmatter), LeafDirective(LeafDirective), ContainerDirective(ContainerDirective),
BlankLine(BlankLine),
});
impl_meta_accessors!(Inline {
Text,
Escape,
CharacterReference,
Emphasis,
Strong,
Delete,
Mark,
Shortcode,
Code,
Link,
Autolink,
Image,
LinkReference,
ImageReference,
Html,
SoftBreak,
LineBreak,
Math,
FootnoteReference,
InlineFootnote,
WikiLink,
TextDirective,
Tag,
});
impl_from_variants!(Inline {
Text(Text), Escape(Escape), CharacterReference(CharacterReference),
Emphasis(Emphasis), Strong(Strong), Delete(Delete), Mark(Mark),
Shortcode(Shortcode), Code(CodeInline), Link(Link), Autolink(Autolink), Image(Image),
LinkReference(LinkReference), ImageReference(ImageReference), Html(HtmlInline),
SoftBreak(SoftBreak), LineBreak(LineBreak), Math(MathInline),
FootnoteReference(FootnoteReference), InlineFootnote(InlineFootnote), WikiLink(WikiLink),
TextDirective(TextDirective), Tag(Tag),
});
impl Block {
pub fn children(&self) -> &[Block] {
match self {
Block::BlockQuote(n) => &n.children,
Block::Alert(n) => &n.children,
Block::FootnoteDefinition(n) => &n.children,
Block::ContainerDirective(n) => &n.children,
Block::HtmlContainer(n) => match &n.content {
HtmlContainerContent::Blocks(blocks) => blocks,
HtmlContainerContent::Inlines(_) => &[],
},
Block::Paragraph(_)
| Block::Heading(_)
| Block::ThematicBreak(_)
| Block::List(_)
| Block::CodeBlock(_)
| Block::HtmlBlock(_)
| Block::Definition(_)
| Block::Table(_)
| Block::MathBlock(_)
| Block::Frontmatter(_)
| Block::LeafDirective(_)
| Block::BlankLine(_) => &[],
}
}
}
impl Inline {
pub fn children(&self) -> &[Inline] {
match self {
Inline::Emphasis(n) => &n.children,
Inline::Strong(n) => &n.children,
Inline::Delete(n) => &n.children,
Inline::Mark(n) => &n.children,
Inline::Link(n) => &n.children,
Inline::Image(n) => &n.alt,
Inline::LinkReference(n) => &n.children,
Inline::ImageReference(n) => &n.alt,
Inline::InlineFootnote(n) => &n.children,
Inline::TextDirective(n) => &n.label,
Inline::Text(_)
| Inline::Escape(_)
| Inline::CharacterReference(_)
| Inline::Shortcode(_)
| Inline::Code(_)
| Inline::Autolink(_)
| Inline::Html(_)
| Inline::SoftBreak(_)
| Inline::LineBreak(_)
| Inline::Math(_)
| Inline::FootnoteReference(_)
| Inline::WikiLink(_)
| Inline::Tag(_) => &[],
}
}
}
impl CharacterReference {
pub fn new(reference: impl Into<String>) -> Self {
Self {
meta: NodeMeta::default(),
reference: reference.into(),
}
}
pub fn value(&self) -> Option<String> {
crate::decode::decode_character_reference(&self.reference)
}
}
impl Autolink {
pub fn new(form: AutolinkForm, text: impl Into<String>) -> Self {
Self {
meta: NodeMeta::default(),
form,
text: text.into(),
}
}
pub fn destination(&self) -> Option<String> {
match self.form {
AutolinkForm::Angle => crate::parse::angle_autolink_destination(&self.text),
AutolinkForm::Literal => crate::parse::literal_autolink_destination(&self.text),
}
}
}
impl Title {
pub fn new(value: impl Into<String>, kind: LinkTitleKind) -> Self {
Self {
value: value.into(),
kind,
}
}
}
impl Shortcode {
pub fn glyph(&self) -> Option<&'static str> {
crate::gemoji::glyph(&self.name)
}
}
impl Tag {
pub fn new(name: impl Into<String>) -> Self {
Self {
meta: NodeMeta::default(),
name: name.into(),
}
}
}
impl WikiLink {
pub fn decoded_target(&self) -> String {
crate::decode::decode_escapes_and_references(&self.target)
}
pub fn decoded_label(&self) -> String {
crate::decode::decode_escapes_and_references(&self.label)
}
}
impl Text {
pub fn new(value: impl Into<String>) -> Self {
Self {
meta: NodeMeta::default(),
value: value.into(),
}
}
}
impl From<&str> for Text {
fn from(value: &str) -> Self {
Text::new(value)
}
}
impl From<String> for Text {
fn from(value: String) -> Self {
Text::new(value)
}
}
impl Paragraph {
pub fn new<I, T>(children: I) -> Self
where
I: IntoIterator<Item = T>,
T: Into<Inline>,
{
Self {
meta: NodeMeta::default(),
children: children.into_iter().map(Into::into).collect(),
source: None,
}
}
pub fn with_source(text: &str, definitions: &Definitions) -> (Self, Vec<Diagnostic>) {
let (children, diagnostics) = read_source(text, definitions);
let paragraph = Self {
source: Some(text.into()),
..Self::new(children)
};
(paragraph, diagnostics)
}
pub fn source(&self) -> Option<&str> {
self.source.as_deref()
}
}
impl Heading {
pub fn new<I, T>(depth: u8, children: I) -> Self
where
I: IntoIterator<Item = T>,
T: Into<Inline>,
{
Self {
meta: NodeMeta::default(),
depth,
children: children.into_iter().map(Into::into).collect(),
source: None,
}
}
pub fn with_source(
depth: u8,
text: &str,
definitions: &Definitions,
) -> (Self, Vec<Diagnostic>) {
let (children, diagnostics) = read_source(text, definitions);
let heading = Self {
source: Some(text.into()),
..Self::new(depth, children)
};
(heading, diagnostics)
}
pub fn source(&self) -> Option<&str> {
self.source.as_deref()
}
}
impl TableCell {
pub fn new<I, T>(children: I) -> Self
where
I: IntoIterator<Item = T>,
T: Into<Inline>,
{
Self {
meta: NodeMeta::default(),
children: children.into_iter().map(Into::into).collect(),
source: None,
}
}
pub fn with_source(text: &str, definitions: &Definitions) -> (Self, Vec<Diagnostic>) {
let (children, diagnostics) = read_source(text, definitions);
let cell = Self {
source: Some(text.into()),
..Self::new(children)
};
(cell, diagnostics)
}
pub fn source(&self) -> Option<&str> {
self.source.as_deref()
}
}
impl LeafDirective {
pub fn new(name: impl Into<String>) -> Self {
Self {
meta: NodeMeta::default(),
name: name.into(),
label: Vec::new(),
attributes: Vec::new(),
label_source: None,
}
}
pub fn with_source(
name: impl Into<String>,
text: &str,
definitions: &Definitions,
) -> (Self, Vec<Diagnostic>) {
let (label, diagnostics) = read_source(text, definitions);
let directive = Self {
label,
label_source: Some(text.into()),
..Self::new(name)
};
(directive, diagnostics)
}
pub fn label_source(&self) -> Option<&str> {
self.label_source.as_deref()
}
}
impl ContainerDirective {
pub fn new(name: impl Into<String>, children: Vec<Block>) -> Self {
Self {
meta: NodeMeta::default(),
name: name.into(),
label: Vec::new(),
attributes: Vec::new(),
children,
label_source: None,
}
}
pub fn with_source(
name: impl Into<String>,
text: &str,
children: Vec<Block>,
definitions: &Definitions,
) -> (Self, Vec<Diagnostic>) {
let (label, diagnostics) = read_source(text, definitions);
let directive = Self {
label,
label_source: Some(text.into()),
..Self::new(name, children)
};
(directive, diagnostics)
}
pub fn label_source(&self) -> Option<&str> {
self.label_source.as_deref()
}
}
fn read_source(text: &str, definitions: &Definitions) -> (Vec<Inline>, Vec<Diagnostic>) {
let output = crate::parse::parse_inline_tree(text, definitions);
(output.inlines, output.diagnostics)
}
impl Link {
pub fn new<I, T>(destination: impl Into<String>, children: I) -> Self
where
I: IntoIterator<Item = T>,
T: Into<Inline>,
{
Self {
meta: NodeMeta::default(),
destination: destination.into(),
destination_kind: LinkDestinationKind::Bare,
title: None,
children: children.into_iter().map(Into::into).collect(),
}
}
}
impl CodeInline {
pub fn new(value: impl Into<String>) -> Self {
Self {
meta: NodeMeta::default(),
value: value.into(),
}
}
}
impl List {
pub fn new<I>(children: I) -> Self
where
I: IntoIterator<Item = ListItem>,
{
Self {
meta: NodeMeta::default(),
ordered: false,
delimiter: ListDelimiter::Dash,
tight: true,
children: children.into_iter().collect(),
}
}
}
impl ListItem {
pub fn new<I, T>(children: I) -> Self
where
I: IntoIterator<Item = T>,
T: Into<Block>,
{
Self {
meta: NodeMeta::default(),
checked: None,
number: None,
children: children.into_iter().map(Into::into).collect(),
}
}
}
impl BlankLine {
pub fn new() -> Self {
Self::default()
}
}