use std::{convert, fmt};
use atoms::{tokens, FragmentClose, FragmentOpen};
use proc_macro2::{Ident, TokenStream};
use quote::ToTokens;
use syn::{parse::ParseStream, ExprPath, LitStr, Token};
pub mod atoms;
mod attribute;
mod node_name;
mod node_value;
pub mod parse;
mod parser_ext;
mod raw_text;
pub use attribute::{
AttributeValueExpr, FnBinding, KVAttributeValue, KeyedAttribute, KeyedAttributeValue,
NodeAttribute,
};
pub use node_name::{NodeName, NodeNameFragment};
pub use node_value::{InvalidBlock, NodeBlock};
pub use self::raw_text::RawText;
use crate::recoverable::{ParseRecoverable, RecoverableContext};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum NodeType {
Element,
Text,
Comment,
Doctype,
Block,
Fragment,
RawText,
Custom,
}
impl fmt::Display for NodeType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
match self {
Self::Element => "NodeType::Element",
Self::Text => "NodeType::Text",
Self::RawText => "NodeType::RawText",
Self::Comment => "NodeType::Comment",
Self::Doctype => "NodeType::Doctype",
Self::Block => "NodeType::Block",
Self::Fragment => "NodeType::Fragment",
Self::Custom => "NodeType::Custom",
}
)
}
}
#[derive(Clone, Debug)]
pub enum Node<C = Infallible> {
Comment(NodeComment),
Doctype(NodeDoctype),
Fragment(NodeFragment<C>),
Element(NodeElement<C>),
Block(NodeBlock),
Text(NodeText),
RawText(RawText<C>),
Custom(C),
}
impl<C: CustomNode> ToTokens for Node<C> {
fn to_tokens(&self, tokens: &mut TokenStream) {
match self {
Self::Comment(comment) => comment.to_tokens(tokens),
Self::Doctype(doctype) => doctype.to_tokens(tokens),
Self::Fragment(fragment) => fragment.to_tokens(tokens),
Self::Element(element) => element.to_tokens(tokens),
Self::Block(block) => block.to_tokens(tokens),
Self::Text(text) => text.to_tokens(tokens),
Self::RawText(raw_text) => raw_text.to_tokens(tokens),
Self::Custom(custom) => custom.to_tokens(tokens),
}
}
}
impl<C: CustomNode> Node<C> {
pub fn flatten(mut self) -> Vec<Self> {
let children = self
.children_mut()
.map(|children| children.drain(..))
.into_iter()
.flatten()
.collect::<Vec<_>>();
std::iter::once(self)
.chain(children.into_iter().flat_map(Self::flatten))
.collect()
}
pub fn r#type(&self) -> NodeType {
match &self {
Self::Element(_) => NodeType::Element,
Self::Text(_) => NodeType::Text,
Self::Comment(_) => NodeType::Comment,
Self::Doctype(_) => NodeType::Element,
Self::Block(_) => NodeType::Block,
Self::Fragment(_) => NodeType::Fragment,
Self::RawText(_) => NodeType::RawText,
Self::Custom(_) => NodeType::Custom,
}
}
pub fn children(&self) -> Option<&Vec<Self>> {
match self {
Self::Fragment(NodeFragment { children, .. })
| Self::Element(NodeElement { children, .. }) => Some(children),
_ => None,
}
}
pub fn children_mut(&mut self) -> Option<&mut Vec<Self>> {
match self {
Self::Fragment(NodeFragment { children, .. })
| Self::Element(NodeElement { children, .. }) => Some(children),
_ => None,
}
}
}
#[derive(Clone, Debug)]
pub struct NodeElement<C> {
pub open_tag: atoms::OpenTag,
pub children: Vec<Node<C>>,
pub close_tag: Option<atoms::CloseTag>,
}
impl<C: CustomNode> ToTokens for NodeElement<C> {
fn to_tokens(&self, tokens: &mut TokenStream) {
self.open_tag.to_tokens(tokens);
for child in &self.children {
child.to_tokens(tokens);
}
if let Some(close_tag) = &self.close_tag {
close_tag.to_tokens(tokens);
}
}
}
impl<C: CustomNode> NodeElement<C> {
pub fn name(&self) -> &NodeName {
&self.open_tag.name
}
pub fn attributes(&self) -> &[NodeAttribute] {
&self.open_tag.attributes
}
pub fn attributes_mut(&mut self) -> &mut Vec<NodeAttribute> {
&mut self.open_tag.attributes
}
pub fn chidlren(&self) -> &[Node<C>] {
&self.children
}
pub fn children_mut(&mut self) -> &mut Vec<Node<C>> {
&mut self.children
}
}
#[derive(Clone, Debug, syn_derive::Parse, syn_derive::ToTokens)]
pub struct NodeText {
pub value: LitStr,
}
impl NodeText {
pub fn value_string(&self) -> String {
self.value.value()
}
}
#[derive(Clone, Debug, syn_derive::Parse, syn_derive::ToTokens)]
pub struct NodeComment {
pub token_start: tokens::ComStart,
pub value: LitStr,
pub token_end: tokens::ComEnd,
}
#[derive(Clone, Debug, syn_derive::ToTokens)]
pub struct NodeDoctype {
pub token_start: tokens::DocStart,
pub token_doctype: Ident,
pub value: RawText,
pub token_end: Token![>],
}
#[derive(Clone, Debug)]
pub struct NodeFragment<C> {
pub tag_open: FragmentOpen,
pub children: Vec<Node<C>>,
pub tag_close: Option<FragmentClose>,
}
impl<C: CustomNode> ToTokens for NodeFragment<C> {
fn to_tokens(&self, tokens: &mut TokenStream) {
self.tag_open.to_tokens(tokens);
for child in &self.children {
child.to_tokens(tokens);
}
if let Some(close_tag) = &self.tag_close {
close_tag.to_tokens(tokens);
}
}
}
impl<C> NodeFragment<C> {
pub fn children(&self) -> &[Node<C>] {
&self.children
}
pub fn children_mut(&mut self) -> &mut Vec<Node<C>> {
&mut self.children
}
}
fn path_to_string(expr: &ExprPath) -> String {
expr.path
.segments
.iter()
.map(|segment| segment.ident.to_string())
.collect::<Vec<String>>()
.join("::")
}
pub trait CustomNode: ParseRecoverable + ToTokens {
fn peek_element(input: ParseStream) -> bool;
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Infallible(convert::Infallible);
impl From<convert::Infallible> for Infallible {
fn from(s: convert::Infallible) -> Self {
match s {}
}
}
impl ToTokens for Infallible {
fn to_tokens(&self, _tokens: &mut TokenStream) {
match self.0 {}
}
}
impl ParseRecoverable for Infallible {
fn parse_recoverable(_: &mut RecoverableContext, _: ParseStream) -> Option<Self> {
unreachable!("Infallible::peek_element returns false")
}
}
impl CustomNode for Infallible {
fn peek_element(_: ParseStream) -> bool {
false
}
}