#![cfg_attr(test, feature(conservative_impl_trait))]
#![cfg_attr(not(any(feature = "use_std", test)), no_std)]
#![allow(unused_unsafe)]
#![deny(missing_docs)]
pub mod dom_node;
pub use dom_node::{DomNode, DomValue};
#[cfg(any(feature = "use_std", test))]
pub mod html_writer;
mod keys;
pub use keys::KeyIter;
pub mod listener;
pub use listener::{Listener, Event, on};
pub mod processors;
pub use processors::{DomNodes, Listeners};
pub mod tags;
#[cfg(all(feature = "web_render", target_os = "emscripten"))]
pub mod web_render;
pub type KeyValue = (&'static str, AttributeValue);
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub enum AttributeValue {
Str(&'static str),
#[cfg(any(feature = "use_std", test))]
OwnedStr(String),
Bool(bool),
}
impl AttributeValue {
pub fn as_str(&self) -> &str {
match *self {
AttributeValue::Str(value) => value,
#[cfg(any(feature = "use_std", test))]
AttributeValue::OwnedStr(ref value) => value,
AttributeValue::Bool(true) => "true",
AttributeValue::Bool(false) => "false",
}
}
}
#[cfg(any(feature = "use_std", test))]
impl std::fmt::Display for AttributeValue {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
mod opt_std {
#[cfg(not(any(feature = "use_std", test)))]
pub extern crate core as std;
#[cfg(not(any(feature = "use_std", test)))]
pub use self::std::*;
#[cfg(any(feature = "use_std", test))]
pub use std::*;
}
#[cfg(test)]
mod tests {
use super::{DomNode, DomNodes, DomValue, KeyValue};
use super::AttributeValue::Str;
use super::tags::*;
use super::processors::{DomNodeProcessor, EmptyListeners};
#[cfg(feature = "use_either_n")]
extern crate either_n;
#[cfg(feature = "use_either_n")]
use self::either_n::*;
use std::marker::PhantomData;
static EMPTY_NODES_REF: &'static () = &();
static EMPTY_LISTN_REF: &'static EmptyListeners = &EmptyListeners;
struct BogusOne;
impl<M> DomNodes<M> for BogusOne {
fn process_all<'a, P: DomNodeProcessor<'a, M>>(&'a self, acc: &mut P::Acc) -> Result<(), P::Error> {
P::get_processor()(acc, self)
}
}
impl<M> DomNode<M> for BogusOne {
type Children = ();
type Listeners = EmptyListeners;
type WithoutListeners = BogusOne;
fn children(&self) -> &Self::Children { EMPTY_NODES_REF }
fn listeners(&self) -> &Self::Listeners { EMPTY_LISTN_REF }
fn children_and_listeners(&self) -> (&Self::Children, &Self::Listeners) {
(EMPTY_NODES_REF, EMPTY_LISTN_REF)
}
fn split_listeners(self) -> (Self::WithoutListeners, Self::Listeners) {
(BogusOne, EmptyListeners)
}
fn key(&self) -> Option<u32> { None }
fn get_attribute(&self, _index: usize) -> Option<&KeyValue> { None }
fn value(&self) -> DomValue {
DomValue::Element { tag: "bogus_tag_one" }
}
}
struct BogusTwo;
impl<M> DomNodes<M> for BogusTwo {
fn process_all<'a, P: DomNodeProcessor<'a, M>>(&'a self, acc: &mut P::Acc) -> Result<(), P::Error> {
P::get_processor()(acc, self)
}
}
impl<M> DomNode<M> for BogusTwo {
type Children = ();
type Listeners = EmptyListeners;
type WithoutListeners = BogusTwo;
fn key(&self) -> Option<u32> { None }
fn get_attribute(&self, _index: usize) -> Option<&KeyValue> { None }
fn children(&self) -> &Self::Children { EMPTY_NODES_REF }
fn listeners(&self) -> &Self::Listeners { EMPTY_LISTN_REF }
fn children_and_listeners(&self) -> (&Self::Children, &Self::Listeners) {
(EMPTY_NODES_REF, EMPTY_LISTN_REF)
}
fn split_listeners(self) -> (Self::WithoutListeners, Self::Listeners) {
(BogusTwo, EmptyListeners)
}
fn value(&self) -> DomValue {
DomValue::Element { tag: "bogus_tag_two" }
}
}
struct ChildCounter<M=Never>(PhantomData<M>);
impl<'a, M> DomNodeProcessor<'a, M> for ChildCounter<M> {
type Acc = usize;
type Error = Never;
fn get_processor<T: DomNode<M>>() -> fn(&mut Self::Acc, &'a T) -> Result<(), Never> {
fn incr<'a, M, T: DomNode<M>>(count: &mut usize, _node: &'a T) -> Result<(), Never> {
*count += 1;
Ok(())
}
incr
}
}
#[derive(Copy, Clone, Debug, Hash, PartialOrd, PartialEq)]
enum Never {}
fn html_sample() -> impl DomNode<Never> + 'static {
div ((
attributes([("attr", Str("value"))]),
(
BogusOne,
BogusOne,
BogusTwo,
table ((
"something&",
th (()),
tr (()),
tr (()),
)),
)
))
}
#[cfg(feature = "use_either_n")]
fn html_either(include_rows: bool) -> impl DomNode<Never> + 'static {
div((
table((
if include_rows {
Either2::One((
tr("a"),
tr("b"),
))
} else {
Either2::Two("sumthin else")
}
))
))
}
#[cfg(feature = "use_either_n")]
fn builds_an_either_string(arg: bool, expected: &'static str) {
assert_eq!(
without_whitespace(expected.to_string()),
without_whitespace(html_either(arg).displayable().to_string())
);
}
#[cfg(feature = "use_either_n")]
#[test]
fn builds_either_string() {
builds_an_either_string(true, r#"
<div>
<table>
<tr>a</tr>
<tr>b</tr>
</table>
</div>
"#);
builds_an_either_string(false, r#"
<div>
<table>
sumthin else
</table>
</div>
"#);
}
#[test]
fn nonstatic_nodes() {
fn str_div<'a>(str_val: &'a str) -> impl DomNode<Never> + 'a {
div (
str_val
)
}
let hello_string = "hello".to_string();
let hello_div = str_div(&hello_string);
assert_eq!("<div>hello</div>".to_string(), hello_div.displayable().to_string());
}
#[test]
fn counts_children() {
let mut count = 0;
(BogusOne, BogusOne, BogusTwo).process_all::<ChildCounter>(&mut count).unwrap();
assert_eq!(3, count);
count = 0;
(BogusOne, (BogusOne,), BogusTwo).process_all::<ChildCounter>(&mut count).unwrap();
assert_eq!(3, count);
count = 0;
[BogusOne, BogusOne, BogusOne].process_all::<ChildCounter>(&mut count).unwrap();
assert_eq!(3, count);
count = 0;
(BogusOne, BogusOne,
[BogusOne, BogusOne, BogusOne],
[(BogusOne)],
vec![(), (), ()],
[BogusTwo, BogusTwo, BogusTwo],
).process_all::<ChildCounter>(&mut count).unwrap();
assert_eq!(9, count);
let sample = html_sample();
count = 0;
sample.process_all::<ChildCounter>(&mut count).unwrap();
assert_eq!(1, count);
count = 0;
sample.children().process_all::<ChildCounter>(&mut count).unwrap();
assert_eq!(4, count);
}
fn without_whitespace(string: String) -> String {
string.chars().filter(|c| !c.is_whitespace()).collect()
}
#[test]
fn builds_string() {
let string = html_sample().displayable().to_string();
assert_eq!(
without_whitespace(r#"
<div attr="value">
<bogus_tag_one></bogus_tag_one>
<bogus_tag_one></bogus_tag_one>
<bogus_tag_two></bogus_tag_two>
<table>
something&
<th></th>
<tr></tr>
<tr></tr>
</table>
</div>
"#.to_string()),
without_whitespace(string)
);
}
fn check_attribute_list<M, T: DomNode<M>>(div: T) {
assert_eq!(div.get_attribute(0), Some(&("attr1", Str("val1"))));
assert_eq!(div.get_attribute(1), Some(&("attr2", Str("val2"))));
assert_eq!(div.get_attribute(2), Some(&("attr3", Str("val3"))));
assert_eq!(div.get_attribute(3), None);
let mut attr_iter = div.attributes();
assert_eq!(attr_iter.next(), Some(&("attr1", Str("val1"))));
assert_eq!(attr_iter.next(), Some(&("attr2", Str("val2"))));
assert_eq!(attr_iter.next(), Some(&("attr3", Str("val3"))));
assert_eq!(attr_iter.next(), None);
}
#[test]
fn builds_attribute_list() {
let div1 = div(PhantomData::<Never>)
.with_attributes([("attr2", Str("val2")), ("attr3", Str("val3"))])
.with_attributes([("attr1", Str("val1"))]);
check_attribute_list(div1);
let div2 = div((
attributes([("attr2", Str("val2")), ("attr3", Str("val3"))]),
div(PhantomData::<Never>)
)).with_attributes([("attr1", Str("val1"))]);
check_attribute_list(div2);
}
}