Skip to content

Commit

Permalink
refactor!: switch Node to enum-style (#23)
Browse files Browse the repository at this point in the history
  • Loading branch information
stoically authored Oct 21, 2022
1 parent 0f13493 commit 900e464
Show file tree
Hide file tree
Showing 8 changed files with 605 additions and 319 deletions.
11 changes: 7 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,15 @@ license = "MIT"
bench = false

[dependencies]
proc-macro2 = "1.0.40"
quote = "1.0.20"
syn = { version = "1.0.98", features = ["full", "parsing", "extra-traits"] }
proc-macro2 = "1.0.47"
quote = "1.0.21"
syn = { version = "1.0.102", features = ["full", "parsing", "extra-traits"] }
thiserror = "1.0.37"

[dev-dependencies]
criterion = "0.3.5"
criterion = "0.4.0"
extrude = "0.1.0"
eyre = "0.6.8"

[[bench]]
name = "bench"
Expand Down
8 changes: 4 additions & 4 deletions examples/html-to-string-macro/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ license = "MIT"
proc-macro = true

[dependencies]
proc-macro2 = "1.0.36"
quote = "1.0.15"
syn = "1.0.86"
syn-rsx = "0.8.1"
proc-macro2 = "1.0.47"
quote = "1.0.21"
syn = "1.0.102"
syn-rsx = { path = "../../" }
65 changes: 32 additions & 33 deletions examples/html-to-string-macro/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,28 @@
use std::convert::TryFrom;

use proc_macro::TokenStream;
use quote::quote;
use syn::Expr;
use syn_rsx::{parse, Node, NodeType};

fn walk_nodes(nodes: Vec<Node>, nodes_context: Option<NodeType>) -> (String, Vec<Expr>) {
fn walk_nodes<'a>(nodes: &'a Vec<Node>, context: Option<NodeType>) -> (String, Vec<&'a Expr>) {
let mut out = String::new();
let mut values = vec![];

for node in nodes {
match node.node_type {
NodeType::Element => {
let name = node.name_as_string().expect("unexpected missing node name");
match node {
Node::Doctype(doctype) => {
let value = String::try_from(&doctype.value)
.expect("could not convert node value to string");
out.push_str(&format!("<!DOCTYPE {}>", value));
}
Node::Element(element) => {
let name = element.name.to_string();
out.push_str(&format!("<{}", name));

// attributes
let (html_string, attribute_values) =
walk_nodes(node.attributes, Some(NodeType::Attribute));
walk_nodes(&element.attributes, Some(NodeType::Attribute));
out.push_str(&html_string);
values.extend(attribute_values);
out.push('>');
Expand All @@ -28,49 +35,41 @@ fn walk_nodes(nodes: Vec<Node>, nodes_context: Option<NodeType>) -> (String, Vec
}

// children
let (html_string, children_values) =
walk_nodes(node.children, Some(NodeType::Element));
let (html_string, children_values) = walk_nodes(&element.children, None);
out.push_str(&html_string);
values.extend(children_values);

out.push_str(&format!("</{}>", name));
}
NodeType::Attribute => {
out.push_str(&format!(
" {}",
node.name_as_string().expect("unexpected missing node name")
));
if node.value.is_some() {
Node::Attribute(attribute) => {
out.push_str(&format!(" {}", attribute.key.to_string()));
if let Some(value) = &attribute.value {
out.push_str(r#"="{}""#);
values.push(node.value.expect("unexpected missing node value"));
values.push(value);
}
}
NodeType::Text | NodeType::Block => {
if let Some(nodes_context) = &nodes_context {
// If the nodes context is attribute we prefix with whitespace
if nodes_context == &NodeType::Attribute {
out.push(' ');
}
}

Node::Text(text) => {
out.push_str("{}");
values.push(node.value.expect("unexpected missing node value"));
values.push(&text.value);
}
NodeType::Fragment => {
Node::Fragment(fragment) => {
let (html_string, children_values) =
walk_nodes(node.children, Some(NodeType::Fragment));
walk_nodes(&fragment.children, Some(NodeType::Fragment));
out.push_str(&html_string);
values.extend(children_values);
}
NodeType::Comment => {
Node::Comment(comment) => {
out.push_str("<!-- {} -->");
values.push(node.value.expect("unexpected missing node value"));
values.push(&comment.value);
}
NodeType::Doctype => {
let value = node
.value_as_string()
.expect("unexpected missing node value");
out.push_str(&format!("<!DOCTYPE {}>", value));
Node::Block(block) => {
// If the nodes parent is an attribute we prefix with whitespace
if matches!(context, Some(NodeType::Attribute)) {
out.push(' ');
}

out.push_str("{}");
values.push(&block.value);
}
}
}
Expand All @@ -92,7 +91,7 @@ fn walk_nodes(nodes: Vec<Node>, nodes_context: Option<NodeType>) -> (String, Vec
pub fn html(tokens: TokenStream) -> TokenStream {
match parse(tokens) {
Ok(nodes) => {
let (html_string, values) = walk_nodes(nodes, None);
let (html_string, values) = walk_nodes(&nodes, None);
quote! { format!(#html_string, #(#values),*) }
}
Err(error) => error.to_compile_error(),
Expand Down
5 changes: 5 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("TryFrom failed: {0}")]
TryFrom(String),
}
24 changes: 18 additions & 6 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,25 @@
//! value are syn expressions to support building proc macros.
//!
//! ```rust
//! # fn main() -> eyre::Result<()> {
//! use std::convert::TryFrom;
//!
//! use extrude::extrude;
//! use quote::quote;
//! use syn_rsx::parse2;
//! use syn_rsx::{parse2, Node, NodeAttribute, NodeElement, NodeText};
//!
//! let tokens = quote! { <hello world>"hi"</hello> };
//! let nodes = parse2(tokens)?;
//!
//! let element = extrude!(&nodes[0], Node::Element(element)).unwrap();
//! let attribute = extrude!(&element.attributes[0], Node::Attribute(attribute)).unwrap();
//! let text = extrude!(&element.children[0], Node::Text(text)).unwrap();
//!
//! let nodes = parse2(tokens).unwrap();
//! assert_eq!(nodes[0].name_as_string().unwrap(), "hello");
//! assert_eq!(nodes[0].attributes[0].name_as_string().unwrap(), "world");
//! assert_eq!(nodes[0].children[0].value_as_string().unwrap(), "hi");
//! assert_eq!(element.name.to_string(), "hello");
//! assert_eq!(attribute.key.to_string(), "world");
//! assert_eq!(String::try_from(&text.value)?, "hi");
//! # Ok(())
//! # }
//! ```
//!
//! ## Features
Expand Down Expand Up @@ -137,6 +147,7 @@ use syn::{
};

mod config;
mod error;
mod node;
mod parser;

Expand All @@ -148,7 +159,8 @@ pub mod punctuation {
}

pub use config::ParserConfig;
pub use node::{Node, NodeName, NodeType};
pub use error::Error;
pub use node::*;
pub use parser::Parser;

/// Parse the given [`proc-macro::TokenStream`] into a [`Node`] tree
Expand Down
Loading

0 comments on commit 900e464

Please sign in to comment.