use virtual_dom::{VTag, VNode, Listener};
#[macro_export]
macro_rules! html_impl {
($stack:ident (< $starttag:ident $($tail:tt)*)) => {
let node = $crate::virtual_dom::VTag::new(stringify!($starttag));
$stack.push(node);
html_impl! { $stack ($($tail)*) }
};
($stack:ident (class = ($($class:expr),*), $($tail:tt)*)) => {
$( $crate::macros::attach_class(&mut $stack, $class); )*
html_impl! { $stack ($($tail)*) }
};
($stack:ident (class = $class:expr, $($tail:tt)*)) => {
$crate::macros::attach_class(&mut $stack, $class);
html_impl! { $stack ($($tail)*) }
};
($stack:ident (value = $value:expr, $($tail:tt)*)) => {
$crate::macros::set_value(&mut $stack, $value);
html_impl! { $stack ($($tail)*) }
};
($stack:ident (type = $kind:expr, $($tail:tt)*)) => {
$crate::macros::set_kind(&mut $stack, $kind);
html_impl! { $stack ($($tail)*) }
};
($stack:ident (checked = $kind:expr, $($tail:tt)*)) => {
$crate::macros::set_checked(&mut $stack, $kind);
html_impl! { $stack ($($tail)*) }
};
($stack:ident (disabled = $kind:expr, $($tail:tt)*)) => {
if $kind {
$crate::macros::add_attribute(&mut $stack, "disabled", "true");
}
html_impl! { $stack ($($tail)*) }
};
($stack:ident (onclick = $handler:expr, $($tail:tt)*)) => {
html_impl! { $stack ((onclick) = $handler, $($tail)*) }
};
($stack:ident (ondoubleclick = $handler:expr, $($tail:tt)*)) => {
html_impl! { $stack ((ondoubleclick) = $handler, $($tail)*) }
};
($stack:ident (onkeypress = $handler:expr, $($tail:tt)*)) => {
html_impl! { $stack ((onkeypress) = $handler, $($tail)*) }
};
($stack:ident (oninput = $handler:expr, $($tail:tt)*)) => {
html_impl! { $stack ((oninput) = $handler, $($tail)*) }
};
($stack:ident (($action:ident) = $handler:expr, $($tail:tt)*)) => {
let handler = $handler;
let listener = $crate::html::$action::Wrapper::from(handler);
$crate::macros::attach_listener(&mut $stack, Box::new(listener));
html_impl! { $stack ($($tail)*) }
};
($stack:ident (href = $href:expr, $($tail:tt)*)) => {
let href: $crate::html::Href = $href.into();
$crate::macros::add_attribute(&mut $stack, "href", href);
html_impl! { $stack ($($tail)*) }
};
($stack:ident ($attr:ident = $val:expr, $($tail:tt)*)) => {
$crate::macros::add_attribute(&mut $stack, stringify!($attr), $val);
html_impl! { $stack ($($tail)*) }
};
($stack:ident ({ for $eval:expr } $($tail:tt)*)) => {
let nodes = $eval;
for node in nodes.map($crate::virtual_dom::VNode::from) {
$crate::macros::add_child(&mut $stack, node);
}
html_impl! { $stack ($($tail)*) }
};
($stack:ident ({ $eval:expr } $($tail:tt)*)) => {
let node = $crate::virtual_dom::VNode::from($eval);
$crate::macros::add_child(&mut $stack, node);
html_impl! { $stack ($($tail)*) }
};
($stack:ident (> $($tail:tt)*)) => {
html_impl! { $stack ($($tail)*) }
};
($stack:ident (/ > $($tail:tt)*)) => {
$crate::macros::child_to_parent(&mut $stack, None);
html_impl! { $stack ($($tail)*) }
};
($stack:ident (< / $endtag:ident > $($tail:tt)*)) => {
let endtag = stringify!($endtag);
$crate::macros::child_to_parent(&mut $stack, Some(endtag));
html_impl! { $stack ($($tail)*) }
};
($stack:ident ()) => {
$crate::macros::unpack($stack)
};
}
#[macro_export]
macro_rules! html {
($($tail:tt)*) => {{
let mut stack = Vec::new();
html_impl! { stack ($($tail)*) }
}};
}
type Stack<MSG> = Vec<VTag<MSG>>;
#[doc(hidden)]
pub fn unpack<MSG>(mut stack: Stack<MSG>) -> VTag<MSG> {
if stack.len() != 1 {
panic!("exactly one element have to be in html!");
}
stack.pop().expect("no html elements in the stack")
}
#[doc(hidden)]
pub fn set_value<MSG, T: ToString>(stack: &mut Stack<MSG>, value: T) {
if let Some(node) = stack.last_mut() {
node.set_value(&value);
} else {
panic!("no tag to set value: {}", value.to_string());
}
}
#[doc(hidden)]
pub fn set_kind<MSG, T: ToString>(stack: &mut Stack<MSG>, value: T) {
if let Some(node) = stack.last_mut() {
node.set_kind(value);
} else {
panic!("no tag to set type: {}", value.to_string());
}
}
#[doc(hidden)]
pub fn set_checked<MSG>(stack: &mut Stack<MSG>, value: bool) {
if let Some(node) = stack.last_mut() {
node.set_checked(value);
} else {
panic!("no tag to set checked: {}", value);
}
}
#[doc(hidden)]
pub fn add_attribute<MSG, T: ToString>(stack: &mut Stack<MSG>, name: &'static str, value: T) {
if let Some(node) = stack.last_mut() {
node.add_attribute(name, value);
} else {
panic!("no tag to set attribute: {}", name);
}
}
#[doc(hidden)]
pub fn attach_class<MSG>(stack: &mut Stack<MSG>, class: &'static str) {
if let Some(node) = stack.last_mut() {
node.add_classes(class);
} else {
panic!("no tag to attach class: {}", class);
}
}
#[doc(hidden)]
pub fn attach_listener<MSG>(stack: &mut Stack<MSG>, listener: Box<Listener<MSG>>) {
if let Some(node) = stack.last_mut() {
node.add_listener(listener);
} else {
panic!("no tag to attach listener: {:?}", listener);
}
}
#[doc(hidden)]
pub fn add_child<MSG>(stack: &mut Stack<MSG>, child: VNode<MSG>) {
if let Some(parent) = stack.last_mut() {
parent.add_child(child);
} else {
panic!("no nodes in stack to add child: {:?}", child);
}
}
#[doc(hidden)]
pub fn child_to_parent<MSG>(stack: &mut Stack<MSG>, endtag: Option<&'static str>) {
if let Some(node) = stack.pop() {
if let Some(endtag) = endtag {
let starttag = node.tag();
if starttag != endtag {
panic!("wrong closing tag: <{}> -> </{}>", starttag, endtag);
}
}
if !stack.is_empty() {
stack.last_mut()
.expect("stack lost the last element")
.add_child(VNode::from(node));
} else {
stack.push(node);
}
} else {
panic!("redundant closing tag: {:?}", endtag);
}
}
#[macro_export]
macro_rules! debug {
($($e:expr),*) => {
if cfg!(debug) {
println!($($e,)*);
}
};
}
#[macro_export]
macro_rules! warn {
($($e:expr),*) => {
eprintln!($($e,)*);
};
}