diff --git a/render/Cargo.toml b/render/Cargo.toml index a53b74d..ffb32d0 100644 --- a/render/Cargo.toml +++ b/render/Cargo.toml @@ -16,4 +16,4 @@ license = "MIT" render_macros = { path = "../render_macros", version = "0.3.1" } [dev-dependencies] -pretty_assertions = "0.6" +pretty_assertions = "1.4" diff --git a/render/src/lib.rs b/render/src/lib.rs index d2d9fa3..bd314b0 100644 --- a/render/src/lib.rs +++ b/render/src/lib.rs @@ -176,4 +176,5 @@ pub use self::render::Render; pub use fragment::Fragment; pub use render_macros::{component, html, rsx}; pub use simple_element::SimpleElement; +pub use simple_element::ToAttribute; pub use text_element::Raw; diff --git a/render/src/simple_element.rs b/render/src/simple_element.rs index 4cc0e22..bdde562 100644 --- a/render/src/simple_element.rs +++ b/render/src/simple_element.rs @@ -4,7 +4,68 @@ use std::borrow::Cow; use std::collections::HashMap; use std::fmt::{Result, Write}; -type Attributes<'a> = Option>>; +type AV<'a> = Option>; + +pub trait ToAttribute<'a> { + fn from_value(self) -> AV<'a>; +} + +impl<'a> ToAttribute<'a> for Option> { + fn from_value(self) -> AV<'a> { + self + } +} + +impl<'a> ToAttribute<'a> for () { + fn from_value(self) -> AV<'a> { + None + } +} + +impl<'a> ToAttribute<'a> for String { + fn from_value(self) -> AV<'a> { + Some(Cow::Owned(self)) + } +} + +impl<'a> ToAttribute<'a> for &'a str { + fn from_value(self) -> AV<'a> { + Some(Cow::Borrowed(self)) + } +} + +impl<'a> ToAttribute<'a> for Option<&'a str> { + fn from_value(self) -> AV<'a> { + self.map(|v| Cow::Borrowed(v)) + } +} + +impl<'a> ToAttribute<'a> for Option { + fn from_value(self) -> AV<'a> { + self.map(|v| Cow::Owned(v)) + } +} + +macro_rules! impl_primitive { + [$($num: ty),+] => { + $( + impl<'a> ToAttribute<'a> for $num { + fn from_value(self) -> AV<'a> { + Some(Cow::Owned(self.to_string())) + } + } + )+ + }; +} +impl_primitive![u8, u16, u32, u64, i8, i16, i32, i64, f32, f64, bool]; + +impl<'a> ToAttribute<'a> for Cow<'a, str> { + fn from_value(self) -> AV<'a> { + Some(self) + } +} + +type Attributes<'a> = Option>>; /// Simple HTML element tag #[derive(Debug)] @@ -15,14 +76,16 @@ pub struct SimpleElement<'a, T: Render> { pub contents: Option, } -fn write_attributes<'a, W: Write>(maybe_attributes: Attributes<'a>, writer: &mut W) -> Result { - match maybe_attributes { +fn write_attributes<'a, W: Write>(attributes: Attributes<'a>, writer: &mut W) -> Result { + match attributes { None => Ok(()), Some(mut attributes) => { - for (key, value) in attributes.drain() { - write!(writer, " {}=\"", key)?; - escape_html(&value, writer)?; - write!(writer, "\"")?; + for (key, maybe_value) in attributes.drain() { + if let Some(value) = maybe_value { + write!(writer, " {}=\"", key)?; + escape_html(&value, writer)?; + write!(writer, "\"")?; + } } Ok(()) } diff --git a/render_macros/Cargo.toml b/render_macros/Cargo.toml index 3412b5a..74bfa6c 100644 --- a/render_macros/Cargo.toml +++ b/render_macros/Cargo.toml @@ -16,11 +16,11 @@ license = "MIT" proc-macro = true [dependencies] -syn = { version = "1.0", features = ["full"] } +syn = { version = "2.0", features = ["full"] } quote = "1.0" proc-macro2 = "1.0" proc-macro-error = "1.0" [dev-dependencies] render = { path = "../render", version = "0.3" } -pretty_assertions = "0.6" +pretty_assertions = "1.4" diff --git a/render_macros/src/element_attribute.rs b/render_macros/src/element_attribute.rs index abf38ad..584200c 100644 --- a/render_macros/src/element_attribute.rs +++ b/render_macros/src/element_attribute.rs @@ -102,6 +102,8 @@ impl Parse for ElementAttribute { } input.parse::()?; + // TODO: Add support for literals + // TODO: Add support for bool expr disable attribute let value = input.parse::()?; Ok(Self::WithValue(name, value)) diff --git a/render_macros/src/element_attributes.rs b/render_macros/src/element_attributes.rs index ace3fb0..c8493b5 100644 --- a/render_macros/src/element_attributes.rs +++ b/render_macros/src/element_attributes.rs @@ -131,13 +131,13 @@ impl<'a> ToTokens for SimpleElementAttributes<'a> { let value = attribute.value_tokens(); quote! { - hm.insert(#ident, ::std::borrow::Cow::from(#value)); + hm.insert(#ident, ::render::ToAttribute::from_value(#value)); } }) .collect(); let hashmap_declaration = quote! {{ - let mut hm = std::collections::HashMap::<&str, ::std::borrow::Cow<'_, str>>::new(); + let mut hm = std::collections::HashMap::<&str, Option<::std::borrow::Cow<'_, str>>>::new(); #(#attrs)* Some(hm) }}; diff --git a/render_macros/src/function_component.rs b/render_macros/src/function_component.rs index 884aad0..5118ebc 100644 --- a/render_macros/src/function_component.rs +++ b/render_macros/src/function_component.rs @@ -42,9 +42,9 @@ pub fn create_function_component(f: syn::ItemFn) -> TokenStream { TokenStream::from(quote! { #[derive(Debug)] - #vis struct #struct_name#impl_generics #inputs_block + #vis struct #struct_name #impl_generics #inputs_block - impl#impl_generics ::render::Render for #struct_name #ty_generics #where_clause { + impl #impl_generics ::render::Render for #struct_name #ty_generics #where_clause { fn render_into(self, w: &mut W) -> std::fmt::Result { let result = { #inputs_reading diff --git a/render_tests/Cargo.toml b/render_tests/Cargo.toml index ec642ce..288db44 100644 --- a/render_tests/Cargo.toml +++ b/render_tests/Cargo.toml @@ -12,5 +12,5 @@ repository = "https://github.com/Schniz/render.rs" render = { path = "../render" } [dev-dependencies] -pretty_assertions = "0.6" +pretty_assertions = "1.4" trybuild = "1.0"