diff --git a/macros/src/html.rs b/macros/src/html.rs
index a1db35d..cef7ce6 100644
--- a/macros/src/html.rs
+++ b/macros/src/html.rs
@@ -262,7 +262,7 @@ impl Element {
let key = TokenTree::Ident(key.clone());
let value = process_value(value);
body.extend(quote!(
- element.events.#key = Some(typed_html::events::IntoEventHandler::into_event_handler(#value));
+ element.events.#key = Some(#value.into());
));
}
diff --git a/typed-html/src/events.rs b/typed-html/src/events.rs
index aa1ac4f..c4d1dcf 100644
--- a/typed-html/src/events.rs
+++ b/typed-html/src/events.rs
@@ -3,6 +3,7 @@
use crate::OutputType;
use htmlescape::encode_attribute;
use std::fmt::{Display, Error, Formatter};
+use std::iter;
/// Trait for event handlers.
pub trait EventHandler {
@@ -23,58 +24,57 @@ pub trait EventHandler {
fn render(&self) -> Option;
}
-/// Trait for building event handlers from other types.
-pub trait IntoEventHandler {
- /// Construct an event handler from an instance of the source type.
- fn into_event_handler(self) -> Box>;
-}
-
-/// An uninhabited event type for string handlers.
-pub enum StringEvent {}
-
-impl EventHandler for &'static str {
- fn attach(&mut self, _target: &mut ::EventTarget) {
- panic!("Silly wabbit, strings as event handlers are only for printing.");
- }
-
- fn render(&self) -> Option {
- Some(self.to_string())
- }
-}
-
-impl IntoEventHandler for &'static str {
- fn into_event_handler(self) -> Box> {
- Box::new(self)
- }
-}
-
-impl EventHandler for String {
- fn attach(&mut self, _target: &mut ::EventTarget) {
- panic!("Silly wabbit, strings as event handlers are only for printing.");
- }
-
- fn render(&self) -> Option {
- Some(self.clone())
- }
-}
-
-impl IntoEventHandler for String {
- fn into_event_handler(self) -> Box> {
- Box::new(self)
- }
-}
-
-macro_rules! declare_string_events {
+macro_rules! declare_events_struct {
($($name:ident,)*) => {
- pub struct StringEvents {
+ pub struct Events {
$(
- pub $name: Option>>,
+ pub $name: Option,
)*
}
- impl Default for StringEvents {
+ impl Events {
+ pub fn iter(&self) -> impl Iterator- {
+ iter::empty()
+ $(
+ .chain(
+ self.$name.iter()
+ .map(|value| (stringify!($name), value))
+ )
+ )*
+ }
+
+ pub fn iter_mut(&mut self) -> impl Iterator
- {
+ iter::empty()
+ $(
+ .chain(
+ self.$name.iter_mut()
+ .map(|value| (stringify!($name), value))
+ )
+ )*
+ }
+ }
+
+ impl IntoIterator for Events {
+ type Item = (&'static str, T);
+ type IntoIter = Box>;
+
+ fn into_iter(mut self) -> Self::IntoIter {
+ Box::new(
+ iter::empty()
+ $(
+ .chain(
+ iter::once(self.$name.take())
+ .filter(|value| value.is_some())
+ .map(|value| (stringify!($name), value.unwrap()))
+ )
+ )*
+ )
+ }
+ }
+
+ impl Default for Events {
fn default() -> Self {
- StringEvents {
+ Events {
$(
$name: None,
)*
@@ -82,12 +82,12 @@ macro_rules! declare_string_events {
}
}
- impl Display for StringEvents {
+ impl Display for Events {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
$(
if let Some(ref value) = self.$name {
- write!(f, " on{}=\"{}\"", stringify!($name),
- encode_attribute(value.render().unwrap().as_str()))?;
+ let attribute = encode_attribute(&value.to_string());
+ write!(f, " on{}=\"{}\"", stringify!($name), attribute)?;
}
)*
Ok(())
@@ -96,7 +96,7 @@ macro_rules! declare_string_events {
}
}
-declare_string_events! {
+declare_events_struct! {
abort,
autocomplete,
autocompleteerror,
@@ -161,3 +161,52 @@ declare_string_events! {
volumechange,
waiting,
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_empty_events_iter() {
+ let events: Events<&str> = Events::default();
+
+ let mut iter = events.iter();
+ assert_eq!(iter.next(), None);
+ }
+
+ #[test]
+ fn test_events_iter() {
+ let mut events: Events<&str> = Events::default();
+ events.abort = Some("abort");
+ events.waiting = Some("waiting");
+
+ let mut iter = events.iter();
+ assert_eq!(iter.next(), Some(("abort", &"abort")));
+ assert_eq!(iter.next(), Some(("waiting", &"waiting")));
+ assert_eq!(iter.next(), None);
+ }
+
+ #[test]
+ fn test_events_iter_mut() {
+ let mut events: Events<&str> = Events::default();
+ events.abort = Some("abort");
+ events.waiting = Some("waiting");
+
+ let mut iter = events.iter_mut();
+ assert_eq!(iter.next(), Some(("abort", &mut "abort")));
+ assert_eq!(iter.next(), Some(("waiting", &mut "waiting")));
+ assert_eq!(iter.next(), None);
+ }
+
+ #[test]
+ fn test_events_into_iter() {
+ let mut events: Events<&str> = Events::default();
+ events.abort = Some("abort");
+ events.waiting = Some("waiting");
+
+ let mut iter = events.into_iter();
+ assert_eq!(iter.next(), Some(("abort", "abort")));
+ assert_eq!(iter.next(), Some(("waiting", "waiting")));
+ assert_eq!(iter.next(), None);
+ }
+}
diff --git a/typed-html/src/lib.rs b/typed-html/src/lib.rs
index 40b4dc0..0c868ee 100644
--- a/typed-html/src/lib.rs
+++ b/typed-html/src/lib.rs
@@ -1,3 +1,4 @@
+#![recursion_limit = "128"]
//! This crate provides the `html!` macro for building HTML documents inside your
//! Rust code using roughly [JSX] compatible syntax.
//!
@@ -221,7 +222,7 @@ pub trait OutputType {
/// String output
impl OutputType for String {
- type Events = events::StringEvents;
+ type Events = events::Events;
type EventTarget = ();
type EventListenerHandle = ();
}
diff --git a/typed-html/src/output/stdweb.rs b/typed-html/src/output/stdweb.rs
index dc37ed8..ec77c7a 100644
--- a/typed-html/src/output/stdweb.rs
+++ b/typed-html/src/output/stdweb.rs
@@ -6,7 +6,7 @@ use stdweb::web::{self, Element, EventListenerHandle, IElement, IEventTarget, IN
use crate::OutputType;
use crate::dom::VNode;
-use crate::events::{EventHandler, IntoEventHandler};
+use crate::events::EventHandler;
/// DOM output using the stdweb crate
pub struct Stdweb;
@@ -136,23 +136,13 @@ where
}
}
-impl IntoEventHandler for F
+impl From for Box>
where
F: FnMut(E) + 'static,
E: ConcreteEvent + 'static,
{
- fn into_event_handler(self) -> Box> {
- Box::new(EFn::new(self))
- }
-}
-
-impl IntoEventHandler for EFn
-where
- F: FnMut(E) + 'static,
- E: ConcreteEvent + 'static,
-{
- fn into_event_handler(self) -> Box> {
- Box::new(self)
+ fn from(f: F) -> Self {
+ Box::new(EFn::new(f))
}
}