pub mod any_attribute;
pub mod aria;
pub mod custom;
pub mod global;
mod key;
pub(crate) mod maybe_next_attr_erasure_macros;
mod value;
use crate::view::{Position, ToTemplate};
pub use key::*;
use maybe_next_attr_erasure_macros::{
next_attr_combine, next_attr_output_type,
};
use std::{borrow::Cow, fmt::Debug, future::Future};
pub use value::*;
pub trait Attribute: NextAttribute + Send {
const MIN_LENGTH: usize;
type State;
type AsyncOutput: Attribute;
type Cloneable: Attribute + Clone;
type CloneableOwned: Attribute + Clone + 'static;
fn html_len(&self) -> usize;
fn to_html(
self,
buf: &mut String,
class: &mut String,
style: &mut String,
inner_html: &mut String,
);
fn hydrate<const FROM_SERVER: bool>(
self,
el: &crate::renderer::types::Element,
) -> Self::State;
fn build(self, el: &crate::renderer::types::Element) -> Self::State;
fn rebuild(self, 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;
fn keys(&self) -> Vec<NamedAttributeKey> {
vec![]
}
}
pub enum NamedAttributeKey {
Attribute(Cow<'static, str>),
Property(Cow<'static, str>),
InnerHtml,
}
pub trait NextAttribute {
type Output<NewAttr: Attribute>: Attribute;
fn add_any_attr<NewAttr: Attribute>(
self,
new_attr: NewAttr,
) -> Self::Output<NewAttr>;
}
impl Attribute for () {
const MIN_LENGTH: usize = 0;
type State = ();
type AsyncOutput = ();
type Cloneable = ();
type CloneableOwned = ();
fn html_len(&self) -> usize {
0
}
fn to_html(
self,
_buf: &mut String,
_class: &mut String,
_style: &mut String,
_inner_html: &mut String,
) {
}
fn hydrate<const FROM_SERVER: bool>(
self,
_el: &crate::renderer::types::Element,
) -> Self::State {
}
fn build(self, _el: &crate::renderer::types::Element) -> Self::State {}
fn rebuild(self, _state: &mut Self::State) {}
fn into_cloneable(self) -> Self::Cloneable {
self
}
fn into_cloneable_owned(self) -> Self::Cloneable {
self
}
fn dry_resolve(&mut self) {}
async fn resolve(self) -> Self::AsyncOutput {}
fn keys(&self) -> Vec<NamedAttributeKey> {
vec![]
}
}
impl NextAttribute for () {
#[cfg(not(erase_components))]
type Output<NewAttr: Attribute> = (NewAttr,);
#[cfg(erase_components)]
type Output<NewAttr: Attribute> =
Vec<crate::html::attribute::any_attribute::AnyAttribute>;
fn add_any_attr<NewAttr: Attribute>(
self,
new_attr: NewAttr,
) -> Self::Output<NewAttr> {
#[cfg(not(erase_components))]
{
(new_attr,)
}
#[cfg(erase_components)]
{
use crate::html::attribute::any_attribute::IntoAnyAttribute;
vec![new_attr.into_any_attr()]
}
}
}
#[derive(Debug)]
pub struct Attr<K, V>(pub K, pub V)
where
K: AttributeKey,
V: AttributeValue;
impl<K, V> Clone for Attr<K, V>
where
K: AttributeKey,
V: AttributeValue + Clone,
{
fn clone(&self) -> Self {
Self(self.0.clone(), self.1.clone())
}
}
impl<K, V> ToTemplate for Attr<K, V>
where
K: AttributeKey,
V: AttributeValue,
{
fn to_template(
buf: &mut String,
_class: &mut String,
_style: &mut String,
_inner_html: &mut String,
_position: &mut Position,
) {
V::to_template(K::KEY, buf);
}
}
impl<K, V> Attribute for Attr<K, V>
where
K: AttributeKey + Send,
V: AttributeValue + Send,
{
const MIN_LENGTH: usize = 0;
type State = V::State;
type AsyncOutput = Attr<K, V::AsyncOutput>;
type Cloneable = Attr<K, V::Cloneable>;
type CloneableOwned = Attr<K, V::CloneableOwned>;
fn html_len(&self) -> usize {
K::KEY.len() + 3 + self.1.html_len()
}
fn to_html(
self,
buf: &mut String,
_class: &mut String,
_style: &mut String,
_inner_html: &mut String,
) {
self.1.to_html(K::KEY, buf);
}
fn hydrate<const FROM_SERVER: bool>(
self,
el: &crate::renderer::types::Element,
) -> Self::State {
self.1.hydrate::<FROM_SERVER>(K::KEY, el)
}
fn build(self, el: &crate::renderer::types::Element) -> Self::State {
V::build(self.1, el, K::KEY)
}
fn rebuild(self, state: &mut Self::State) {
V::rebuild(self.1, K::KEY, state);
}
fn into_cloneable(self) -> Self::Cloneable {
Attr(self.0, self.1.into_cloneable())
}
fn into_cloneable_owned(self) -> Self::CloneableOwned {
Attr(self.0, self.1.into_cloneable_owned())
}
fn dry_resolve(&mut self) {
self.1.dry_resolve();
}
async fn resolve(self) -> Self::AsyncOutput {
Attr(self.0, self.1.resolve().await)
}
fn keys(&self) -> Vec<NamedAttributeKey> {
vec![NamedAttributeKey::Attribute(K::KEY.into())]
}
}
impl<K, V> NextAttribute for Attr<K, V>
where
K: AttributeKey,
V: AttributeValue,
{
next_attr_output_type!(Self, NewAttr);
fn add_any_attr<NewAttr: Attribute>(
self,
new_attr: NewAttr,
) -> Self::Output<NewAttr> {
next_attr_combine!(self, new_attr)
}
}
macro_rules! impl_attr_for_tuples {
($first:ident, $($ty:ident),* $(,)?) => {
impl<$first, $($ty),*> Attribute for ($first, $($ty,)*)
where
$first: Attribute,
$($ty: Attribute),*,
{
const MIN_LENGTH: usize = $first::MIN_LENGTH $(+ $ty::MIN_LENGTH)*;
type AsyncOutput = ($first::AsyncOutput, $($ty::AsyncOutput,)*);
type State = ($first::State, $($ty::State,)*);
type Cloneable = ($first::Cloneable, $($ty::Cloneable,)*);
type CloneableOwned = ($first::CloneableOwned, $($ty::CloneableOwned,)*);
fn html_len(&self) -> usize {
#[allow(non_snake_case)]
let ($first, $($ty,)*) = self;
$first.html_len() $(+ $ty.html_len())*
}
fn to_html(self, buf: &mut String, class: &mut String, style: &mut String, inner_html: &mut String,) {
#[allow(non_snake_case)]
let ($first, $($ty,)* ) = self;
$first.to_html(buf, class, style, inner_html);
$($ty.to_html(buf, class, style, inner_html));*
}
fn hydrate<const FROM_SERVER: bool>(self, el: &crate::renderer::types::Element) -> Self::State {
#[allow(non_snake_case)]
let ($first, $($ty,)* ) = self;
(
$first.hydrate::<FROM_SERVER>(el),
$($ty.hydrate::<FROM_SERVER>(el)),*
)
}
fn build(self, el: &crate::renderer::types::Element) -> Self::State {
#[allow(non_snake_case)]
let ($first, $($ty,)*) = self;
(
$first.build(el),
$($ty.build(el)),*
)
}
fn rebuild(self, state: &mut Self::State) {
paste::paste! {
let ([<$first:lower>], $([<$ty:lower>],)*) = self;
let ([<view_ $first:lower>], $([<view_ $ty:lower>],)*) = state;
[<$first:lower>].rebuild([<view_ $first:lower>]);
$([<$ty:lower>].rebuild([<view_ $ty:lower>]));*
}
}
fn into_cloneable(self) -> Self::Cloneable {
#[allow(non_snake_case)]
let ($first, $($ty,)*) = self;
(
$first.into_cloneable(),
$($ty.into_cloneable()),*
)
}
fn into_cloneable_owned(self) -> Self::CloneableOwned {
#[allow(non_snake_case)]
let ($first, $($ty,)*) = self;
(
$first.into_cloneable_owned(),
$($ty.into_cloneable_owned()),*
)
}
fn dry_resolve(&mut self) {
#[allow(non_snake_case)]
let ($first, $($ty,)*) = self;
$first.dry_resolve();
$($ty.dry_resolve());*
}
async fn resolve(self) -> Self::AsyncOutput {
#[allow(non_snake_case)]
let ($first, $($ty,)*) = self;
futures::join!(
$first.resolve(),
$($ty.resolve()),*
)
}
fn keys(&self) -> Vec<NamedAttributeKey> {
#[allow(non_snake_case)]
let ($first, $($ty,)*) = &self;
let mut buf = $first.keys();
$(buf.extend($ty.keys());)*
buf
}
}
impl<$first, $($ty),*> NextAttribute for ($first, $($ty,)*)
where
$first: Attribute,
$($ty: Attribute),*,
{
type Output<NewAttr: Attribute> = ($first, $($ty,)* NewAttr);
fn add_any_attr<NewAttr: Attribute>(
self,
new_attr: NewAttr,
) -> Self::Output<NewAttr> {
#[allow(non_snake_case)]
let ($first, $($ty,)*) = self;
($first, $($ty,)* new_attr)
}
}
};
}
macro_rules! impl_attr_for_tuples_truncate_additional {
($first:ident, $($ty:ident),* $(,)?) => {
impl<$first, $($ty),*> Attribute for ($first, $($ty,)*)
where
$first: Attribute,
$($ty: Attribute),*,
{
const MIN_LENGTH: usize = $first::MIN_LENGTH $(+ $ty::MIN_LENGTH)*;
type AsyncOutput = ($first::AsyncOutput, $($ty::AsyncOutput,)*);
type State = ($first::State, $($ty::State,)*);
type Cloneable = ($first::Cloneable, $($ty::Cloneable,)*);
type CloneableOwned = ($first::CloneableOwned, $($ty::CloneableOwned,)*);
fn html_len(&self) -> usize {
#[allow(non_snake_case)]
let ($first, $($ty,)*) = self;
$first.html_len() $(+ $ty.html_len())*
}
fn to_html(self, buf: &mut String, class: &mut String, style: &mut String, inner_html: &mut String,) {
#[allow(non_snake_case)]
let ($first, $($ty,)* ) = self;
$first.to_html(buf, class, style, inner_html);
$($ty.to_html(buf, class, style, inner_html));*
}
fn hydrate<const FROM_SERVER: bool>(self, el: &crate::renderer::types::Element) -> Self::State {
#[allow(non_snake_case)]
let ($first, $($ty,)* ) = self;
(
$first.hydrate::<FROM_SERVER>(el),
$($ty.hydrate::<FROM_SERVER>(el)),*
)
}
fn build(self, el: &crate::renderer::types::Element) -> Self::State {
#[allow(non_snake_case)]
let ($first, $($ty,)*) = self;
(
$first.build(el),
$($ty.build(el)),*
)
}
fn rebuild(self, state: &mut Self::State) {
paste::paste! {
let ([<$first:lower>], $([<$ty:lower>],)*) = self;
let ([<view_ $first:lower>], $([<view_ $ty:lower>],)*) = state;
[<$first:lower>].rebuild([<view_ $first:lower>]);
$([<$ty:lower>].rebuild([<view_ $ty:lower>]));*
}
}
fn into_cloneable(self) -> Self::Cloneable {
#[allow(non_snake_case)]
let ($first, $($ty,)*) = self;
(
$first.into_cloneable(),
$($ty.into_cloneable()),*
)
}
fn into_cloneable_owned(self) -> Self::CloneableOwned {
#[allow(non_snake_case)]
let ($first, $($ty,)*) = self;
(
$first.into_cloneable_owned(),
$($ty.into_cloneable_owned()),*
)
}
fn dry_resolve(&mut self) {
#[allow(non_snake_case)]
let ($first, $($ty,)*) = self;
$first.dry_resolve();
$($ty.dry_resolve());*
}
async fn resolve(self) -> Self::AsyncOutput {
#[allow(non_snake_case)]
let ($first, $($ty,)*) = self;
futures::join!(
$first.resolve(),
$($ty.resolve()),*
)
}
fn keys(&self) -> Vec<NamedAttributeKey> {
#[allow(non_snake_case)]
let ($first, $($ty,)*) = &self;
let mut buf = $first.keys();
$(buf.extend($ty.keys());)*
buf
}
}
impl<$first, $($ty),*> NextAttribute for ($first, $($ty,)*)
where
$first: Attribute,
$($ty: Attribute),*,
{
type Output<NewAttr: Attribute> = ($first, $($ty,)*);
fn add_any_attr<NewAttr: Attribute>(
self,
_new_attr: NewAttr,
) -> Self::Output<NewAttr> {
todo!("adding more than 26 attributes is not supported");
}
}
};
}
impl<A> Attribute for (A,)
where
A: Attribute,
{
const MIN_LENGTH: usize = A::MIN_LENGTH;
type AsyncOutput = (A::AsyncOutput,);
type State = A::State;
type Cloneable = (A::Cloneable,);
type CloneableOwned = (A::CloneableOwned,);
fn html_len(&self) -> usize {
self.0.html_len()
}
fn to_html(
self,
buf: &mut String,
class: &mut String,
style: &mut String,
inner_html: &mut String,
) {
self.0.to_html(buf, class, style, inner_html);
}
fn hydrate<const FROM_SERVER: bool>(
self,
el: &crate::renderer::types::Element,
) -> Self::State {
self.0.hydrate::<FROM_SERVER>(el)
}
fn build(self, el: &crate::renderer::types::Element) -> Self::State {
self.0.build(el)
}
fn rebuild(self, state: &mut Self::State) {
self.0.rebuild(state);
}
fn into_cloneable(self) -> Self::Cloneable {
(self.0.into_cloneable(),)
}
fn into_cloneable_owned(self) -> Self::CloneableOwned {
(self.0.into_cloneable_owned(),)
}
fn dry_resolve(&mut self) {
self.0.dry_resolve();
}
async fn resolve(self) -> Self::AsyncOutput {
(self.0.resolve().await,)
}
fn keys(&self) -> Vec<NamedAttributeKey> {
self.0.keys()
}
}
impl<A> NextAttribute for (A,)
where
A: Attribute,
{
next_attr_output_type!(A, NewAttr);
fn add_any_attr<NewAttr: Attribute>(
self,
new_attr: NewAttr,
) -> Self::Output<NewAttr> {
next_attr_combine!(self.0, new_attr)
}
}
impl_attr_for_tuples!(A, B);
impl_attr_for_tuples!(A, B, C);
impl_attr_for_tuples!(A, B, C, D);
impl_attr_for_tuples!(A, B, C, D, E);
impl_attr_for_tuples!(A, B, C, D, E, F);
impl_attr_for_tuples!(A, B, C, D, E, F, G);
impl_attr_for_tuples!(A, B, C, D, E, F, G, H);
impl_attr_for_tuples!(A, B, C, D, E, F, G, H, I);
impl_attr_for_tuples!(A, B, C, D, E, F, G, H, I, J);
impl_attr_for_tuples!(A, B, C, D, E, F, G, H, I, J, K);
impl_attr_for_tuples!(A, B, C, D, E, F, G, H, I, J, K, L);
impl_attr_for_tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M);
impl_attr_for_tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N);
impl_attr_for_tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
impl_attr_for_tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
impl_attr_for_tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q);
impl_attr_for_tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R);
impl_attr_for_tuples!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S);
impl_attr_for_tuples!(
A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T
);
impl_attr_for_tuples!(
A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U
);
impl_attr_for_tuples!(
A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V
);
impl_attr_for_tuples!(
A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W
);
impl_attr_for_tuples!(
A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X
);
impl_attr_for_tuples!(
A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y
);
impl_attr_for_tuples_truncate_additional!(
A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y,
Z
);