use std::{convert::TryFrom, fmt, ops::Deref};
use proc_macro2::{Punct, TokenStream};
use quote::ToTokens;
use syn::{
punctuated::{Pair, Punctuated},
Expr, ExprBlock, ExprLit, ExprPath, Ident, Lit,
};
use crate::Error;
#[derive(Debug, PartialEq, Eq)]
pub enum NodeType {
Element,
Attribute,
Text,
Comment,
Doctype,
Block,
Fragment,
}
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::Block => "NodeType::Block",
Self::Fragment => "NodeType::Fragment",
}
)
}
}
#[derive(Debug)]
pub enum Node {
Element(NodeElement),
Attribute(NodeAttribute),
Text(NodeText),
Comment(NodeComment),
Doctype(NodeDoctype),
Block(NodeBlock),
Fragment(NodeFragment),
}
impl Node {
pub fn r#type(&self) -> NodeType {
match &self {
Self::Element(_) => NodeType::Element,
Self::Attribute(_) => NodeType::Attribute,
Self::Text(_) => NodeType::Text,
Self::Comment(_) => NodeType::Comment,
Self::Doctype(_) => NodeType::Element,
Self::Block(_) => NodeType::Block,
Self::Fragment(_) => NodeType::Fragment,
}
}
pub fn children(&self) -> Option<&Vec<Node>> {
match self {
Self::Fragment(NodeFragment { children })
| Self::Element(NodeElement { children, .. }) => Some(children),
_ => None,
}
}
pub fn children_mut(&mut self) -> Option<&mut Vec<Node>> {
match self {
Self::Fragment(NodeFragment { children })
| Self::Element(NodeElement { children, .. }) => Some(children),
_ => None,
}
}
}
impl fmt::Display for Node {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
match self {
Self::Element(_) => "Node::Element",
Self::Attribute(_) => "Node::Attribute",
Self::Text(_) => "Node::Text",
Self::Comment(_) => "Node::Comment",
Self::Doctype(_) => "Node::Doctype",
Self::Block(_) => "Node::Block",
Self::Fragment(_) => "Node::Fragment",
}
)
}
}
#[derive(Debug)]
pub struct NodeElement {
pub name: NodeName,
pub attributes: Vec<Node>,
pub children: Vec<Node>,
}
impl fmt::Display for NodeElement {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "NodeElement")
}
}
#[derive(Debug)]
pub struct NodeAttribute {
pub key: NodeName,
pub value: Option<NodeValueExpr>,
}
impl fmt::Display for NodeAttribute {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "NodeAttribute")
}
}
#[derive(Debug)]
pub struct NodeText {
pub value: NodeValueExpr,
}
impl fmt::Display for NodeText {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "NodeText")
}
}
#[derive(Debug)]
pub struct NodeComment {
pub value: NodeValueExpr,
}
impl fmt::Display for NodeComment {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "NodeComment")
}
}
#[derive(Debug)]
pub struct NodeDoctype {
pub value: NodeValueExpr,
}
impl fmt::Display for NodeDoctype {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "NodeDoctype")
}
}
#[derive(Debug)]
pub struct NodeFragment {
pub children: Vec<Node>,
}
impl fmt::Display for NodeFragment {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "NodeFragment")
}
}
#[derive(Debug)]
pub struct NodeBlock {
pub value: NodeValueExpr,
}
impl fmt::Display for NodeBlock {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "NodeBlock")
}
}
#[derive(Debug)]
pub enum NodeName {
Path(ExprPath),
Punctuated(Punctuated<Ident, Punct>),
Block(Expr),
}
impl TryFrom<&NodeName> for ExprBlock {
type Error = Error;
fn try_from(node: &NodeName) -> Result<Self, Self::Error> {
match node {
NodeName::Block(Expr::Block(expr)) => Ok(expr.to_owned()),
_ => Err(Error::TryFrom(
"NodeName does not match NodeName::Block(Expr::Block(_))".into(),
)),
}
}
}
impl PartialEq for NodeName {
fn eq(&self, other: &NodeName) -> bool {
match self {
Self::Path(this) => match other {
Self::Path(other) => this == other,
_ => false,
},
Self::Punctuated(this) => match other {
Self::Punctuated(other) => {
this.pairs()
.zip(other.pairs())
.all(|(this, other)| match (this, other) {
(
Pair::Punctuated(this_ident, this_punct),
Pair::Punctuated(other_ident, other_punct),
) => {
this_ident == other_ident
&& this_punct.as_char() == other_punct.as_char()
}
(Pair::End(this), Pair::End(other)) => this == other,
_ => false,
})
}
_ => 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::Punctuated(name) => name.to_tokens(tokens),
NodeName::Block(name) => name.to_tokens(tokens),
}
}
}
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::Punctuated(name) => {
name.pairs()
.flat_map(|pair| match pair {
Pair::Punctuated(ident, punct) => {
[ident.to_string(), punct.to_string()]
}
Pair::End(ident) => [ident.to_string(), "".to_string()],
})
.collect::<String>()
}
NodeName::Block(_) => String::from("{}"),
}
)
}
}
#[derive(Debug)]
pub struct NodeValueExpr {
expr: Expr,
}
impl NodeValueExpr {
pub fn new(expr: Expr) -> Self {
Self { expr }
}
}
impl AsRef<Expr> for NodeValueExpr {
fn as_ref(&self) -> &Expr {
&self.expr
}
}
impl Deref for NodeValueExpr {
type Target = Expr;
fn deref(&self) -> &Self::Target {
&self.expr
}
}
impl From<Expr> for NodeValueExpr {
fn from(expr: Expr) -> Self {
Self { expr }
}
}
impl From<ExprLit> for NodeValueExpr {
fn from(expr: ExprLit) -> Self {
Self { expr: expr.into() }
}
}
impl From<ExprBlock> for NodeValueExpr {
fn from(expr: ExprBlock) -> Self {
Self { expr: expr.into() }
}
}
impl From<NodeValueExpr> for Expr {
fn from(value: NodeValueExpr) -> Self {
value.expr
}
}
impl<'a> From<&'a NodeValueExpr> for &'a Expr {
fn from(value: &'a NodeValueExpr) -> Self {
&value.expr
}
}
impl TryFrom<NodeValueExpr> for ExprBlock {
type Error = Error;
fn try_from(value: NodeValueExpr) -> Result<Self, Self::Error> {
if let Expr::Block(block) = value.expr {
Ok(block)
} else {
Err(Error::TryFrom(
"NodeValueExpr does not match Expr::Block(_)".into(),
))
}
}
}
impl TryFrom<NodeValueExpr> for ExprLit {
type Error = Error;
fn try_from(value: NodeValueExpr) -> Result<Self, Self::Error> {
if let Expr::Lit(lit) = value.expr {
Ok(lit)
} else {
Err(Error::TryFrom(
"NodeValueExpr does not match Expr::Lit(_)".into(),
))
}
}
}
impl TryFrom<&NodeValueExpr> for String {
type Error = Error;
fn try_from(value: &NodeValueExpr) -> Result<Self, Self::Error> {
match &value.expr {
Expr::Lit(expr) => match &expr.lit {
Lit::Str(lit_str) => Some(lit_str.value()),
_ => None,
},
Expr::Path(expr) => Some(path_to_string(&expr)),
_ => None,
}
.ok_or_else(|| {
Error::TryFrom(
"NodeValueExpr does not match Expr::Lit(Lit::Str(_)) or Expr::Path(_)".into(),
)
})
}
}
fn path_to_string(expr: &ExprPath) -> String {
expr.path
.segments
.iter()
.map(|segment| segment.ident.to_string())
.collect::<Vec<String>>()
.join("::")
}