use crate::renderer::Rndr;
use std::{
borrow::Cow,
future::Future,
net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6},
num::{
NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8,
NonZeroIsize, NonZeroU128, NonZeroU16, NonZeroU32, NonZeroU64,
NonZeroU8, NonZeroUsize,
},
sync::Arc,
};
pub trait IntoAttributeValue {
type Output;
fn into_attribute_value(self) -> Self::Output;
}
impl<T> IntoAttributeValue for T
where
T: AttributeValue,
{
type Output = Self;
fn into_attribute_value(self) -> Self::Output {
self
}
}
pub trait AttributeValue: Send {
type State;
type AsyncOutput: AttributeValue;
type Cloneable: AttributeValue + Clone;
type CloneableOwned: AttributeValue + Clone + 'static;
fn html_len(&self) -> usize;
fn to_html(self, key: &str, buf: &mut String);
fn to_template(key: &str, buf: &mut String);
fn hydrate<const FROM_SERVER: bool>(
self,
key: &str,
el: &crate::renderer::types::Element,
) -> Self::State;
fn build(
self,
el: &crate::renderer::types::Element,
key: &str,
) -> Self::State;
fn rebuild(self, key: &str, state: &mut Self::State);
fn into_cloneable(self) -> Self::Cloneable;
fn into_cloneable_owned(self) -> Self::CloneableOwned;
fn dry_resolve(&mut self);
fn resolve(self) -> impl Future<Output = Self::AsyncOutput> + Send;
}
impl AttributeValue for () {
type State = ();
type AsyncOutput = ();
type Cloneable = ();
type CloneableOwned = ();
fn html_len(&self) -> usize {
0
}
fn to_html(self, _key: &str, _buf: &mut String) {}
fn to_template(_key: &str, _buf: &mut String) {}
fn hydrate<const FROM_SERVER: bool>(
self,
_key: &str,
_el: &crate::renderer::types::Element,
) {
}
fn build(
self,
_el: &crate::renderer::types::Element,
_key: &str,
) -> Self::State {
}
fn rebuild(self, _key: &str, _state: &mut Self::State) {}
fn into_cloneable(self) -> Self::Cloneable {
self
}
fn into_cloneable_owned(self) -> Self::CloneableOwned {
self
}
fn dry_resolve(&mut self) {}
async fn resolve(self) {}
}
impl<'a> AttributeValue for &'a str {
type State = (crate::renderer::types::Element, &'a str);
type AsyncOutput = &'a str;
type Cloneable = &'a str;
type CloneableOwned = Arc<str>;
fn html_len(&self) -> usize {
self.len()
}
fn to_html(self, key: &str, buf: &mut String) {
buf.push(' ');
buf.push_str(key);
buf.push_str("=\"");
buf.push_str(&escape_attr(self));
buf.push('"');
}
fn to_template(_key: &str, _buf: &mut String) {}
fn hydrate<const FROM_SERVER: bool>(
self,
key: &str,
el: &crate::renderer::types::Element,
) -> Self::State {
if !FROM_SERVER {
Rndr::set_attribute(el, key, self);
}
(el.clone(), self)
}
fn build(
self,
el: &crate::renderer::types::Element,
key: &str,
) -> Self::State {
Rndr::set_attribute(el, key, self);
(el.to_owned(), self)
}
fn rebuild(self, key: &str, state: &mut Self::State) {
let (el, prev_value) = state;
if self != *prev_value {
Rndr::set_attribute(el, key, self);
}
*prev_value = self;
}
fn into_cloneable(self) -> Self::Cloneable {
self
}
fn into_cloneable_owned(self) -> Self::CloneableOwned {
self.into()
}
fn dry_resolve(&mut self) {}
async fn resolve(self) -> Self::AsyncOutput {
self
}
}
#[cfg(feature = "nightly")]
impl<const V: &'static str> AttributeValue
for crate::view::static_types::Static<V>
{
type AsyncOutput = Self;
type State = ();
type Cloneable = Self;
type CloneableOwned = Self;
fn html_len(&self) -> usize {
V.len()
}
fn to_html(self, key: &str, buf: &mut String) {
<&str as AttributeValue>::to_html(V, key, buf);
}
fn to_template(key: &str, buf: &mut String) {
buf.push(' ');
buf.push_str(key);
buf.push_str("=\"");
buf.push_str(V);
buf.push('"');
}
fn hydrate<const FROM_SERVER: bool>(
self,
_key: &str,
_el: &crate::renderer::types::Element,
) -> Self::State {
}
fn build(
self,
el: &crate::renderer::types::Element,
key: &str,
) -> Self::State {
<&str as AttributeValue>::build(V, el, key);
}
fn rebuild(self, _key: &str, _state: &mut Self::State) {}
fn into_cloneable(self) -> Self::Cloneable {
self
}
fn into_cloneable_owned(self) -> Self::CloneableOwned {
self
}
fn dry_resolve(&mut self) {}
async fn resolve(self) -> Self::AsyncOutput {
self
}
}
impl<'a> AttributeValue for &'a String {
type AsyncOutput = Self;
type State = (crate::renderer::types::Element, &'a String);
type Cloneable = Self;
type CloneableOwned = Arc<str>;
fn html_len(&self) -> usize {
self.len()
}
fn to_html(self, key: &str, buf: &mut String) {
<&str as AttributeValue>::to_html(self.as_str(), key, buf);
}
fn to_template(_key: &str, _buf: &mut String) {}
fn hydrate<const FROM_SERVER: bool>(
self,
key: &str,
el: &crate::renderer::types::Element,
) -> Self::State {
let (el, _) = <&str as AttributeValue>::hydrate::<FROM_SERVER>(
self.as_str(),
key,
el,
);
(el, self)
}
fn build(
self,
el: &crate::renderer::types::Element,
key: &str,
) -> Self::State {
Rndr::set_attribute(el, key, self);
(el.clone(), self)
}
fn rebuild(self, key: &str, state: &mut Self::State) {
let (el, prev_value) = state;
if self != *prev_value {
Rndr::set_attribute(el, key, self);
}
*prev_value = self;
}
fn into_cloneable(self) -> Self::Cloneable {
self
}
fn into_cloneable_owned(self) -> Self::CloneableOwned {
self.as_str().into()
}
fn dry_resolve(&mut self) {}
async fn resolve(self) -> Self::AsyncOutput {
self
}
}
impl AttributeValue for String {
type AsyncOutput = Self;
type State = (crate::renderer::types::Element, String);
type Cloneable = Arc<str>;
type CloneableOwned = Arc<str>;
fn html_len(&self) -> usize {
self.len()
}
fn to_html(self, key: &str, buf: &mut String) {
<&str as AttributeValue>::to_html(self.as_str(), key, buf);
}
fn to_template(_key: &str, _buf: &mut String) {}
fn hydrate<const FROM_SERVER: bool>(
self,
key: &str,
el: &crate::renderer::types::Element,
) -> Self::State {
let (el, _) = <&str as AttributeValue>::hydrate::<FROM_SERVER>(
self.as_str(),
key,
el,
);
(el, self)
}
fn build(
self,
el: &crate::renderer::types::Element,
key: &str,
) -> Self::State {
Rndr::set_attribute(el, key, &self);
(el.clone(), self)
}
fn rebuild(self, key: &str, state: &mut Self::State) {
let (el, prev_value) = state;
if self != *prev_value {
Rndr::set_attribute(el, key, &self);
}
*prev_value = self;
}
fn into_cloneable(self) -> Self::Cloneable {
self.into()
}
fn into_cloneable_owned(self) -> Self::CloneableOwned {
self.into()
}
fn dry_resolve(&mut self) {}
async fn resolve(self) -> Self::AsyncOutput {
self
}
}
impl AttributeValue for Arc<str> {
type AsyncOutput = Self;
type State = (crate::renderer::types::Element, Arc<str>);
type Cloneable = Arc<str>;
type CloneableOwned = Arc<str>;
fn html_len(&self) -> usize {
self.len()
}
fn to_html(self, key: &str, buf: &mut String) {
<&str as AttributeValue>::to_html(self.as_ref(), key, buf);
}
fn to_template(_key: &str, _buf: &mut String) {}
fn hydrate<const FROM_SERVER: bool>(
self,
key: &str,
el: &crate::renderer::types::Element,
) -> Self::State {
let (el, _) = <&str as AttributeValue>::hydrate::<FROM_SERVER>(
self.as_ref(),
key,
el,
);
(el, self)
}
fn build(
self,
el: &crate::renderer::types::Element,
key: &str,
) -> Self::State {
Rndr::set_attribute(el, key, &self);
(el.clone(), self)
}
fn rebuild(self, key: &str, state: &mut Self::State) {
let (el, prev_value) = state;
if self != *prev_value {
Rndr::set_attribute(el, key, &self);
}
*prev_value = self;
}
fn into_cloneable(self) -> Self::Cloneable {
self
}
fn into_cloneable_owned(self) -> Self::CloneableOwned {
self
}
fn dry_resolve(&mut self) {}
async fn resolve(self) -> Self::AsyncOutput {
self
}
}
impl AttributeValue for bool {
type AsyncOutput = Self;
type State = (crate::renderer::types::Element, bool);
type Cloneable = Self;
type CloneableOwned = Self;
fn html_len(&self) -> usize {
0
}
fn to_html(self, key: &str, buf: &mut String) {
if self {
buf.push(' ');
buf.push_str(key);
}
}
fn to_template(_key: &str, _buf: &mut String) {}
fn hydrate<const FROM_SERVER: bool>(
self,
key: &str,
el: &crate::renderer::types::Element,
) -> Self::State {
if !FROM_SERVER {
Rndr::set_attribute(el, key, "");
}
(el.clone(), self)
}
fn build(
self,
el: &crate::renderer::types::Element,
key: &str,
) -> Self::State {
if self {
Rndr::set_attribute(el, key, "");
}
(el.clone(), self)
}
fn rebuild(self, key: &str, state: &mut Self::State) {
let (el, prev_value) = state;
if self != *prev_value {
if self {
Rndr::set_attribute(el, key, "");
} else {
Rndr::remove_attribute(el, key);
}
}
*prev_value = self;
}
fn into_cloneable(self) -> Self::Cloneable {
self
}
fn into_cloneable_owned(self) -> Self::CloneableOwned {
self
}
fn dry_resolve(&mut self) {}
async fn resolve(self) -> Self::AsyncOutput {
self
}
}
impl<V> AttributeValue for Option<V>
where
V: AttributeValue,
{
type AsyncOutput = Option<V::AsyncOutput>;
type State = (crate::renderer::types::Element, Option<V::State>);
type Cloneable = Option<V::Cloneable>;
type CloneableOwned = Option<V::CloneableOwned>;
fn html_len(&self) -> usize {
match self {
Some(i) => i.html_len(),
None => 0,
}
}
fn to_html(self, key: &str, buf: &mut String) {
if let Some(v) = self {
v.to_html(key, buf);
}
}
fn to_template(_key: &str, _buf: &mut String) {}
fn hydrate<const FROM_SERVER: bool>(
self,
key: &str,
el: &crate::renderer::types::Element,
) -> Self::State {
let state = self.map(|v| v.hydrate::<FROM_SERVER>(key, el));
(el.clone(), state)
}
fn build(
self,
el: &crate::renderer::types::Element,
key: &str,
) -> Self::State {
let el = el.clone();
let v = self.map(|v| v.build(&el, key));
(el, v)
}
fn rebuild(self, key: &str, state: &mut Self::State) {
let (el, prev) = state;
match (self, prev.as_mut()) {
(None, None) => {}
(None, Some(_)) => {
Rndr::remove_attribute(el, key);
*prev = None;
}
(Some(value), None) => {
*prev = Some(value.build(el, key));
}
(Some(new), Some(old)) => {
new.rebuild(key, old);
}
}
}
fn into_cloneable(self) -> Self::Cloneable {
self.map(|value| value.into_cloneable())
}
fn into_cloneable_owned(self) -> Self::CloneableOwned {
self.map(|value| value.into_cloneable_owned())
}
fn dry_resolve(&mut self) {
if let Some(inner) = self.as_mut() {
inner.dry_resolve();
}
}
async fn resolve(self) -> Self::AsyncOutput {
match self {
None => None,
Some(inner) => Some(inner.resolve().await),
}
}
}
pub(crate) fn escape_attr(value: &str) -> Cow<'_, str> {
html_escape::encode_double_quoted_attribute(value)
}
macro_rules! render_primitive {
($($child_type:ty),* $(,)?) => {
$(
impl AttributeValue for $child_type
where
{
type AsyncOutput = $child_type;
type State = (crate::renderer::types::Element, $child_type);
type Cloneable = Self;
type CloneableOwned = Self;
fn html_len(&self) -> usize {
0
}
fn to_html(self, key: &str, buf: &mut String) {
<String as AttributeValue>::to_html(self.to_string(), key, buf);
}
fn to_template(_key: &str, _buf: &mut String) {}
fn hydrate<const FROM_SERVER: bool>(
self,
key: &str,
el: &crate::renderer::types::Element,
) -> Self::State {
if !FROM_SERVER {
Rndr::set_attribute(el, key, &self.to_string());
}
(el.clone(), self)
}
fn build(self, el: &crate::renderer::types::Element, key: &str) -> Self::State {
Rndr::set_attribute(el, key, &self.to_string());
(el.to_owned(), self)
}
fn rebuild(self, key: &str, state: &mut Self::State) {
let (el, prev_value) = state;
if self != *prev_value {
Rndr::set_attribute(el, key, &self.to_string());
}
*prev_value = self;
}
fn into_cloneable(self) -> Self::Cloneable {
self
}
fn into_cloneable_owned(self) -> Self::CloneableOwned {
self
}
fn dry_resolve(&mut self) {
}
async fn resolve(self) -> Self::AsyncOutput {
self
}
}
)*
}
}
render_primitive![
usize,
u8,
u16,
u32,
u64,
u128,
isize,
i8,
i16,
i32,
i64,
i128,
f32,
f64,
char,
IpAddr,
SocketAddr,
SocketAddrV4,
SocketAddrV6,
Ipv4Addr,
Ipv6Addr,
NonZeroI8,
NonZeroU8,
NonZeroI16,
NonZeroU16,
NonZeroI32,
NonZeroU32,
NonZeroI64,
NonZeroU64,
NonZeroI128,
NonZeroU128,
NonZeroIsize,
NonZeroUsize,
];