extern crate proc_macro;
use syn::{
parse::{ParseStream, Parser as _},
Result,
};
mod node;
mod parser;
pub mod punctuation {
use syn::custom_punctuation;
custom_punctuation!(Dash, -);
}
pub use node::{Node, NodeName, NodeType};
pub use parser::{Parser, ParserConfig};
pub fn parse(tokens: proc_macro::TokenStream, config: Option<ParserConfig>) -> Result<Vec<Node>> {
let parser = move |input: ParseStream| {
let config = config.unwrap_or_else(ParserConfig::default);
Parser::new(config).parse(input)
};
parser.parse(tokens)
}
pub fn parse2(tokens: proc_macro2::TokenStream, config: Option<ParserConfig>) -> Result<Vec<Node>> {
let parser = move |input: ParseStream| {
let config = config.unwrap_or_else(ParserConfig::default);
Parser::new(config).parse(input)
};
parser.parse2(tokens)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_single_empty_element() {
let tokens = quote::quote! {
<foo></foo>
};
let nodes = parse2(tokens, None).unwrap();
assert_eq!(nodes[0].name_as_string().unwrap(), "foo");
}
#[test]
fn test_single_element_with_attributes() {
let tokens = quote::quote! {
<foo bar="moo" baz="42"></foo>
};
let nodes = parse2(tokens, None).unwrap();
let attribute = &nodes[0].attributes[0];
assert_eq!(attribute.name_as_string().unwrap(), "bar");
assert_eq!(attribute.value_as_string().unwrap(), "moo");
}
#[test]
fn test_single_element_with_text() {
let tokens = quote::quote! {
<foo>"bar"</foo>
};
let nodes = parse2(tokens, None).unwrap();
assert_eq!(nodes[0].children[0].value_as_string().unwrap(), "bar");
}
#[test]
fn test_reserved_keyword_attributes() {
let tokens = quote::quote! {
<input type="foo" />
};
let nodes = parse2(tokens, None).unwrap();
assert_eq!(nodes[0].name_as_string().unwrap(), "input");
assert_eq!(nodes[0].attributes[0].name_as_string().unwrap(), "type");
}
#[test]
fn test_block_node() {
let tokens = quote::quote! {
<div>{hello}</div>
};
let nodes = parse2(tokens, None).unwrap();
assert_eq!(nodes[0].children.len(), 1);
}
#[test]
fn test_flat_tree() {
let config = ParserConfig { flatten: true };
let tokens = quote::quote! {
<div>
<div>
<div>{hello}</div>
<div>"world"</div>
</div>
</div>
<div />
};
let nodes = parse2(tokens, Some(config)).unwrap();
assert_eq!(nodes.len(), 7);
}
#[test]
fn test_path_as_tag_name() {
let tokens = quote::quote! {
<some::path />
};
let nodes = parse2(tokens, None).unwrap();
assert_eq!(nodes[0].name_as_string().unwrap(), "some::path");
}
#[test]
fn test_dashed_attribute_name() {
let tokens = quote::quote! {
<div data-foo="bar" />
};
let nodes = parse2(tokens, None).unwrap();
assert_eq!(nodes[0].attributes[0].name_as_string().unwrap(), "data-foo");
}
#[test]
fn test_coloned_attribute_name() {
let tokens = quote::quote! {
<div on:click={foo} />
};
let nodes = parse2(tokens, None).unwrap();
assert_eq!(nodes[0].attributes[0].name_as_string().unwrap(), "on:click");
}
}