use std::collections::BTreeMap;
use crate::value::Value;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Serialize)]
#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
#[serde(rename_all = "snake_case")]
pub enum NodeKind {
Root,
Heading,
Paragraph,
Code,
Quote,
List,
Item,
Table,
Row,
Cell,
Link,
Image,
Emphasis,
Strong,
Strikethrough,
Text,
CodeInline,
Html,
HtmlInline,
BreakSoft,
BreakHard,
Rule,
FootnoteRef,
FootnoteDef,
Section,
}
impl NodeKind {
#[must_use]
pub fn as_str(self) -> &'static str {
KIND_NAMES
.iter()
.find(|(_, k)| *k == self)
.map_or("unknown", |(s, _)| *s)
}
#[must_use]
pub fn from_name(name: &str) -> Option<Self> {
KIND_NAMES.iter().find(|(s, _)| *s == name).map(|(_, k)| *k)
}
}
const KIND_NAMES: &[(&str, NodeKind)] = &[
("root", NodeKind::Root),
("heading", NodeKind::Heading),
("paragraph", NodeKind::Paragraph),
("code", NodeKind::Code),
("quote", NodeKind::Quote),
("list", NodeKind::List),
("item", NodeKind::Item),
("table", NodeKind::Table),
("row", NodeKind::Row),
("cell", NodeKind::Cell),
("link", NodeKind::Link),
("image", NodeKind::Image),
("emphasis", NodeKind::Emphasis),
("strong", NodeKind::Strong),
("strikethrough", NodeKind::Strikethrough),
("text", NodeKind::Text),
("code_inline", NodeKind::CodeInline),
("html", NodeKind::Html),
("html_inline", NodeKind::HtmlInline),
("break_soft", NodeKind::BreakSoft),
("break_hard", NodeKind::BreakHard),
("rule", NodeKind::Rule),
("footnote_ref", NodeKind::FootnoteRef),
("footnote_def", NodeKind::FootnoteDef),
("section", NodeKind::Section),
];
#[derive(Debug, Clone, Copy, serde::Serialize)]
#[cfg_attr(feature = "schema-export", derive(schemars::JsonSchema))]
pub struct Span {
pub start: usize,
pub end: usize,
}
#[derive(Debug, Clone)]
pub struct Node {
pub kind: NodeKind,
pub attrs: BTreeMap<&'static str, Value>,
pub children: Vec<Value>,
pub span: Option<Span>,
pub dirty: bool,
}
impl Node {
#[must_use]
pub fn new(kind: NodeKind) -> Self {
Self {
kind,
attrs: BTreeMap::new(),
children: Vec::new(),
span: None,
dirty: false,
}
}
#[must_use]
pub fn with_attr(mut self, key: &'static str, value: impl Into<Value>) -> Self {
self.attrs.insert(key, value.into());
self
}
}
pub mod attr {
pub const LEVEL: &str = "level";
pub const ANCHOR: &str = "anchor";
pub const LANG: &str = "lang";
pub const LITERAL: &str = "literal";
pub const HREF: &str = "href";
pub const TITLE: &str = "title";
pub const ALT: &str = "alt";
pub const ORDERED: &str = "ordered";
pub const START: &str = "start";
pub const TIGHT: &str = "tight";
pub const CHECKED: &str = "checked";
pub const ALIGNS: &str = "aligns";
pub const VALUE: &str = "value";
pub const KIND_DETAIL: &str = "kind_detail";
pub const FRONTMATTER: &str = "frontmatter";
#[must_use]
pub fn by_name(name: &str) -> Option<&'static str> {
Some(match name {
"level" => LEVEL,
"anchor" => ANCHOR,
"lang" => LANG,
"literal" => LITERAL,
"href" => HREF,
"title" => TITLE,
"alt" => ALT,
"ordered" => ORDERED,
"start" => START,
"tight" => TIGHT,
"checked" => CHECKED,
"aligns" => ALIGNS,
"value" => VALUE,
"kind_detail" => KIND_DETAIL,
"frontmatter" => FRONTMATTER,
_ => return None,
})
}
#[must_use]
pub fn expected_type(key: &'static str) -> &'static str {
match key {
LEVEL | START => "number",
ORDERED | TIGHT | CHECKED => "boolean",
ALIGNS => "array",
FRONTMATTER => "any",
_ => "string",
}
}
}