#[macro_export]
macro_rules! tree {
($name:ident { $($children:tt)* }) => {
$crate::Tree::Node(
stringify!($name).to_string(),
$crate::__tree_parse_children!([$($children)*] [])
)
};
($name:literal { $($children:tt)* }) => {
$crate::Tree::Node(
$name.to_string(),
$crate::__tree_parse_children!([$($children)*] [])
)
};
}
#[macro_export]
#[doc(hidden)]
macro_rules! __tree_parse_children {
([$i:ident { $($s:tt)* } , $($rest:tt)*] [$($acc:expr,)*]) => {
$crate::__tree_parse_children!([$($rest)*] [$($acc,)* $crate::tree!($i { $($s)* }),])
};
([$l:literal { $($s:tt)* } , $($rest:tt)*] [$($acc:expr,)*]) => {
$crate::__tree_parse_children!([$($rest)*] [$($acc,)* $crate::tree!($l { $($s)* }),])
};
([$l:literal , $($rest:tt)*] [$($acc:expr,)*]) => {
$crate::__tree_parse_children!([$($rest)*] [$($acc,)* $crate::Tree::Leaf(vec![$l.to_string()]),])
};
([$i:ident , $($rest:tt)*] [$($acc:expr,)*]) => {
$crate::__tree_parse_children!([$($rest)*] [$($acc,)* $crate::Tree::Leaf(vec![stringify!($i).to_string()]),])
};
([$i:ident { $($s:tt)* }] [$($acc:expr,)*]) => {
vec![$($acc,)* $crate::tree!($i { $($s)* })]
};
([$l:literal { $($s:tt)* }] [$($acc:expr,)*]) => {
vec![$($acc,)* $crate::tree!($l { $($s)* })]
};
([$l:literal] [$($acc:expr,)*]) => {
vec![$($acc,)* $crate::Tree::Leaf(vec![$l.to_string()])]
};
([$i:ident] [$($acc:expr,)*]) => {
vec![$($acc,)* $crate::Tree::Leaf(vec![stringify!($i).to_string()])]
};
([] [$($acc:expr,)*]) => {
vec![$($acc,)*]
};
([,] [$($acc:expr,)*]) => {
vec![$($acc,)*]
};
}
#[cfg(test)]
mod tests {
use crate::tree::Tree;
#[test]
fn test_simple_tree() {
let tree = tree! {
root {
"item1",
"item2"
}
};
assert!(tree.is_node());
assert_eq!(tree.label(), Some("root"));
if let Tree::Node(_, children) = &tree {
assert_eq!(children.len(), 2);
assert!(children[0].is_leaf());
assert!(children[1].is_leaf());
}
}
#[test]
fn test_nested_tree() {
let tree = tree! {
root {
"item1",
sub {
"subitem1",
"subitem2"
}
}
};
assert!(tree.is_node());
if let Tree::Node(_, children) = &tree {
assert_eq!(children.len(), 2);
assert!(children[0].is_leaf());
assert!(children[1].is_node());
if let Tree::Node(_, subchildren) = &children[1] {
assert_eq!(subchildren.len(), 2);
}
}
}
#[test]
fn test_string_node_name() {
let tree = tree! {
"root node" {
"item1"
}
};
assert!(tree.is_node());
assert_eq!(tree.label(), Some("root node"));
}
#[test]
fn test_identifier_leaves() {
let tree = tree! {
root {
item1,
item2
}
};
assert!(tree.is_node());
if let Tree::Node(_, children) = &tree {
assert_eq!(children.len(), 2);
if let Tree::Leaf(lines) = &children[0] {
assert_eq!(lines[0], "item1");
}
if let Tree::Leaf(lines) = &children[1] {
assert_eq!(lines[0], "item2");
}
}
}
#[test]
fn test_trailing_comma() {
let tree = tree! {
root {
"item1",
"item2",
}
};
assert!(tree.is_node());
if let Tree::Node(_, children) = &tree {
assert_eq!(children.len(), 2);
}
}
#[test]
fn test_empty_node() {
let tree = tree! {
root {}
};
assert!(tree.is_node());
if let Tree::Node(_, children) = &tree {
assert_eq!(children.len(), 0);
}
}
#[test]
fn test_deeply_nested() {
let tree = tree! {
level1 {
level2 {
level3 {
"leaf"
}
}
}
};
assert!(tree.is_node());
if let Tree::Node(_, children) = &tree {
assert_eq!(children.len(), 1);
if let Tree::Node(_, children) = &children[0] {
assert_eq!(children.len(), 1);
if let Tree::Node(_, children) = &children[0] {
assert_eq!(children.len(), 1);
assert!(children[0].is_leaf());
}
}
}
}
#[test]
fn test_mixed_syntax() {
let tree = tree! {
"root" {
item1,
"item2",
sub {
"subitem1",
subitem2
}
}
};
assert!(tree.is_node());
if let Tree::Node(_, children) = &tree {
assert_eq!(children.len(), 3);
}
}
}