use proc_macro2::{Span, TokenStream};
use quote::ToTokens;
use std::fmt;
use syn::{
punctuated::Punctuated, spanned::Spanned, token::Colon, Expr, ExprBlock, ExprPath, Ident, Lit,
};
use crate::punctuation::Dash;
#[derive(Debug)]
pub struct Node {
pub name: Option<NodeName>,
pub node_type: NodeType,
pub value: Option<Expr>,
pub attributes: Vec<Node>,
pub children: Vec<Node>,
}
impl Node {
pub fn name_as_string(&self) -> Option<String> {
match self.name.as_ref() {
Some(NodeName::Block(_)) => None,
Some(name) => Some(name.to_string()),
None => None,
}
}
pub fn name_as_block(&self) -> Option<ExprBlock> {
match self.name.as_ref() {
Some(NodeName::Block(Expr::Block(expr))) => Some(expr.to_owned()),
_ => None,
}
}
pub fn name_span(&self) -> Option<Span> {
self.name.as_ref().map(|name| name.span())
}
pub fn value_as_string(&self) -> Option<String> {
match self.value.as_ref() {
Some(Expr::Lit(expr)) => match &expr.lit {
Lit::Str(lit_str) => Some(lit_str.value()),
_ => None,
},
Some(Expr::Path(expr)) => Some(path_to_string(expr)),
_ => None,
}
}
pub fn value_as_block(&self) -> Option<ExprBlock> {
match self.value.as_ref() {
Some(Expr::Block(expr)) => Some(expr.to_owned()),
_ => None,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum NodeType {
Element,
Attribute,
Text,
Comment,
Doctype,
Fragment,
Block,
}
impl fmt::Display for NodeType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
match self {
Self::Element => "NodeType::Element",
Self::Attribute => "NodeType::Attribute",
Self::Text => "NodeType::Text",
Self::Comment => "NodeType::Comment",
Self::Doctype => "NodeType::Doctype",
Self::Fragment => "NodeType::Fragment",
Self::Block => "NodeType::Block",
}
)
}
}
#[derive(Debug)]
pub enum NodeName {
Path(ExprPath),
Dash(Punctuated<Ident, Dash>),
Colon(Punctuated<Ident, Colon>),
Block(Expr),
}
impl NodeName {
pub fn span(&self) -> Span {
match self {
NodeName::Path(name) => name.span(),
NodeName::Dash(name) => name.span(),
NodeName::Colon(name) => name.span(),
NodeName::Block(name) => name.span(),
}
}
}
impl fmt::Display for NodeName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
match self {
NodeName::Path(expr) => path_to_string(expr),
NodeName::Dash(name) => name
.iter()
.map(|ident| ident.to_string())
.collect::<Vec<String>>()
.join("-"),
NodeName::Colon(name) => name
.iter()
.map(|ident| ident.to_string())
.collect::<Vec<String>>()
.join(":"),
NodeName::Block(_) => String::from("{}"),
}
)
}
}
impl PartialEq for NodeName {
fn eq(&self, other: &NodeName) -> bool {
match self {
Self::Path(this) => match other {
Self::Path(other) => this == other,
_ => false,
},
Self::Dash(this) => match other {
Self::Dash(other) => this == other,
_ => false,
},
Self::Colon(this) => match other {
Self::Colon(other) => this == other,
_ => false,
},
Self::Block(this) => match other {
Self::Block(other) => this == other,
_ => false,
},
}
}
}
impl ToTokens for NodeName {
fn to_tokens(&self, tokens: &mut TokenStream) {
match self {
NodeName::Path(name) => name.to_tokens(tokens),
NodeName::Dash(name) => name.to_tokens(tokens),
NodeName::Colon(name) => name.to_tokens(tokens),
NodeName::Block(name) => name.to_tokens(tokens),
}
}
}
fn path_to_string(expr: &ExprPath) -> String {
expr.path
.segments
.iter()
.map(|segment| segment.ident.to_string())
.collect::<Vec<String>>()
.join("::")
}