//! HTML syntax tree: [hast][]. //! //! [hast]: https://github.com/syntax-tree/hast #![allow(dead_code)] // ^-- To do: externalize. extern crate alloc; extern crate micromark; use alloc::{ fmt, string::{String, ToString}, vec::Vec, }; pub use micromark::mdast::{AttributeContent, AttributeValue, MdxJsxAttribute, Stop}; use micromark::unist::Position; /// Nodes. #[derive(Clone, PartialEq, Eq)] pub enum Node { /// Root. Root(Root), /// Element. Element(Element), /// Document type. Doctype(Doctype), /// Comment. Comment(Comment), /// Text. Text(Text), // MDX being passed through. /// MDX: JSX element. MdxJsxElement(MdxJsxElement), /// MDX.js ESM. MdxjsEsm(MdxjsEsm), // MDX: expression. MdxExpression(MdxExpression), } impl fmt::Debug for Node { // Debug the wrapped struct. fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Node::Root(x) => write!(f, "{:?}", x), Node::Element(x) => write!(f, "{:?}", x), Node::Doctype(x) => write!(f, "{:?}", x), Node::Comment(x) => write!(f, "{:?}", x), Node::Text(x) => write!(f, "{:?}", x), Node::MdxJsxElement(x) => write!(f, "{:?}", x), Node::MdxExpression(x) => write!(f, "{:?}", x), Node::MdxjsEsm(x) => write!(f, "{:?}", x), } } } fn children_to_string(children: &[Node]) -> String { children.iter().map(ToString::to_string).collect() } impl ToString for Node { fn to_string(&self) -> String { match self { // Parents. Node::Root(x) => children_to_string(&x.children), Node::Element(x) => children_to_string(&x.children), Node::MdxJsxElement(x) => children_to_string(&x.children), // Literals. Node::Comment(x) => x.value.clone(), Node::Text(x) => x.value.clone(), Node::MdxExpression(x) => x.value.clone(), Node::MdxjsEsm(x) => x.value.clone(), // Voids. Node::Doctype(_) => "".into(), } } } impl Node { #[must_use] pub fn children(&self) -> Option<&Vec> { match self { // Parent. Node::Root(x) => Some(&x.children), Node::Element(x) => Some(&x.children), Node::MdxJsxElement(x) => Some(&x.children), // Non-parent. _ => None, } } pub fn children_mut(&mut self) -> Option<&mut Vec> { match self { // Parent. Node::Root(x) => Some(&mut x.children), Node::Element(x) => Some(&mut x.children), Node::MdxJsxElement(x) => Some(&mut x.children), // Non-parent. _ => None, } } pub fn position(&self) -> Option<&Position> { match self { Node::Root(x) => x.position.as_ref(), Node::Element(x) => x.position.as_ref(), Node::Doctype(x) => x.position.as_ref(), Node::Comment(x) => x.position.as_ref(), Node::Text(x) => x.position.as_ref(), Node::MdxJsxElement(x) => x.position.as_ref(), Node::MdxExpression(x) => x.position.as_ref(), Node::MdxjsEsm(x) => x.position.as_ref(), } } pub fn position_mut(&mut self) -> Option<&mut Position> { match self { Node::Root(x) => x.position.as_mut(), Node::Element(x) => x.position.as_mut(), Node::Doctype(x) => x.position.as_mut(), Node::Comment(x) => x.position.as_mut(), Node::Text(x) => x.position.as_mut(), Node::MdxJsxElement(x) => x.position.as_mut(), Node::MdxExpression(x) => x.position.as_mut(), Node::MdxjsEsm(x) => x.position.as_mut(), } } pub fn position_set(&mut self, position: Option) { match self { Node::Root(x) => x.position = position, Node::Element(x) => x.position = position, Node::Doctype(x) => x.position = position, Node::Comment(x) => x.position = position, Node::Text(x) => x.position = position, Node::MdxJsxElement(x) => x.position = position, Node::MdxExpression(x) => x.position = position, Node::MdxjsEsm(x) => x.position = position, } } } /// Document. /// /// ```html /// > | a /// ^ /// ``` #[derive(Clone, Debug, PartialEq, Eq)] pub struct Root { // Parent. /// Content model. pub children: Vec, /// Positional info. pub position: Option, } /// Document type. /// /// ```html /// > | /// ^^^^^^^^^^^^^^^ /// ``` #[derive(Clone, Debug, PartialEq, Eq)] pub struct Element { pub tag_name: String, pub properties: Vec<(String, PropertyValue)>, // Parent. pub children: Vec, /// Positional info. pub position: Option, } #[derive(Clone, Debug, PartialEq, Eq)] pub enum PropertyValue { Boolean(bool), String(String), CommaSeparated(Vec), SpaceSeparated(Vec), } /// Document type. /// /// ```html /// > | /// ^^^^^^^^^^^^^^^ /// ``` #[derive(Clone, Debug, PartialEq, Eq)] pub struct Doctype { // Void. /// Positional info. pub position: Option, } /// Comment. /// /// ```html /// > | /// ^^^^^^^^^^ /// ``` #[derive(Clone, Debug, PartialEq, Eq)] pub struct Comment { // Text. /// Content model. pub value: String, /// Positional info. pub position: Option, } /// Text. /// /// ```html /// > | a /// ^ /// ``` #[derive(Clone, Debug, PartialEq, Eq)] pub struct Text { // Text. /// Content model. pub value: String, /// Positional info. pub position: Option, } /// MDX: JSX element. /// /// ```markdown /// > | /// ^^^^^ /// ``` #[derive(Clone, Debug, PartialEq, Eq)] pub struct MdxJsxElement { // Parent. /// Content model. pub children: Vec, /// Positional info. pub position: Option, // JSX element. /// Name. /// /// Fragments have no name. pub name: Option, /// Attributes. pub attributes: Vec, } /// MDX: expression. /// /// ```markdown /// > | {a} /// ^^^ /// ``` #[derive(Clone, Debug, PartialEq, Eq)] pub struct MdxExpression { // Literal. /// Content model. pub value: String, /// Positional info. pub position: Option, // Custom data on where each slice of `value` came from. pub stops: Vec, } /// MDX: ESM. /// /// ```markdown /// > | import a from 'b' /// ^^^^^^^^^^^^^^^^^ /// ``` #[derive(Clone, Debug, PartialEq, Eq)] pub struct MdxjsEsm { // Literal. /// Content model. pub value: String, /// Positional info. pub position: Option, // Custom data on where each slice of `value` came from. pub stops: Vec, }