Skip to content

Instantly share code, notes, and snippets.

@emonkak
Last active June 25, 2021 11:10
Show Gist options
  • Save emonkak/2f2f77d86c855315af21da9d06217efe to your computer and use it in GitHub Desktop.
Save emonkak/2f2f77d86c855315af21da9d06217efe to your computer and use it in GitHub Desktop.
use std::array;
#[derive(Debug, PartialEq, Eq)]
pub struct Element<T> {
pub instance: T,
pub children: Box<[Element<T>]>,
}
#[derive(Debug, PartialEq, Eq)]
pub enum Child<T> {
Multiple(Vec<Element<T>>),
Single(Element<T>),
None,
}
#[macro_export]
macro_rules! element {
($expr:expr => { $($content:tt)* }) => {
Element::build($expr, __children!([] $($content)*))
};
($expr:expr) => {
Element::build($expr, [])
};
}
#[macro_export]
macro_rules! __children {
([$($children:expr)*] $expr:expr => { $($content:tt)* } $($rest:tt)*) => {
__children!([$($children)* Element::build($expr, __children!([] $($content)*)).into()] $($rest)*)
};
([$($children:expr)*] $expr:expr; $($rest:tt)*) => {
__children!([$($children)* Child::from($expr)] $($rest)*)
};
([$($children:expr)*] $expr:expr) => {
__children!([$($children)* Child::from($expr)])
};
([$($children:expr)*]) => {
[$($children),*]
};
}
impl<T> Element<T> {
pub fn new<const N: usize>(instance: T, children: [Element<T>; N]) -> Self {
Self {
instance,
children: Box::new(children),
}
}
pub fn build<const N: usize>(instance: T, children: [Child<T>; N]) -> Self {
let mut flatten_children = Vec::with_capacity(N);
for child in array::IntoIter::new(children) {
match child {
Child::Multiple(elements) => {
for element in elements {
flatten_children.push(element)
}
}
Child::Single(element) => {
flatten_children.push(element)
}
_ => {}
}
}
Self {
instance,
children: flatten_children.into_boxed_slice(),
}
}
fn to_string_rec(&self, level: usize) -> String where T: ToString {
let name = self.instance.to_string();
let indent_str = unsafe { String::from_utf8_unchecked(vec![b'\t'; level]) };
if self.children.len() > 0 {
let mut children_str = "".to_string();
for i in 0..self.children.len() {
children_str.push('\n');
children_str.push_str(&self.children[i].to_string_rec(level + 1));
}
format!("{}<{}>{}\n{}</{}>", indent_str, name, children_str, indent_str, name)
} else {
format!("{}<{}></{}>", indent_str, name, name)
}
}
}
impl<T: ToString> ToString for Element<T> {
fn to_string(&self) -> String {
self.to_string_rec(0)
}
}
impl<T> From<Vec<Element<T>>> for Child<T> {
fn from(elements: Vec<Element<T>>) -> Self {
Child::Multiple(elements)
}
}
impl<T> From<Option<Element<T>>> for Child<T> {
fn from(element: Option<Element<T>>) -> Self {
match element {
Some(element) => Child::Single(element),
None => Child::None,
}
}
}
impl<T> From<Element<T>> for Child<T> {
fn from(element: Element<T>) -> Self {
Child::Single(element)
}
}
impl<T> From<T> for Child<T> {
fn from(value: T) -> Self {
Child::Single(Element::new(value, []))
}
}
fn main() {
let el = element!(
"foo" => {
"bar" => {
"baz";
if true { Some(element!("qux")) } else { None };
vec![element!("quux" => { "corge" })];
}
None;
"grault";
}
);
assert_eq!(el, Element::new("foo", [
Element::new("bar", [
Element::new("baz", []),
Element::new("qux", []),
Element::new("quux", [
Element::new("corge", [])
]),
]),
Element::new("grault", []),
]));
println!("{}", el.to_string());
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment