tachys/html/attribute/
custom.rs

1use super::{
2    maybe_next_attr_erasure_macros::next_attr_output_type, NextAttribute,
3};
4use crate::{
5    html::attribute::{
6        maybe_next_attr_erasure_macros::next_attr_combine, Attribute,
7        AttributeValue, NamedAttributeKey,
8    },
9    view::{add_attr::AddAnyAttr, Position, ToTemplate},
10};
11use std::{borrow::Cow, sync::Arc};
12
13/// Adds a custom attribute with any key-value combination.
14#[inline(always)]
15pub fn custom_attribute<K, V>(key: K, value: V) -> CustomAttr<K, V>
16where
17    K: CustomAttributeKey,
18    V: AttributeValue,
19{
20    CustomAttr { key, value }
21}
22
23/// A custom attribute with any key-value combination.
24#[derive(Debug)]
25pub struct CustomAttr<K, V>
26where
27    K: CustomAttributeKey,
28    V: AttributeValue,
29{
30    key: K,
31    value: V,
32}
33
34impl<K, V> Clone for CustomAttr<K, V>
35where
36    K: CustomAttributeKey,
37    V: AttributeValue + Clone,
38{
39    fn clone(&self) -> Self {
40        Self {
41            key: self.key.clone(),
42            value: self.value.clone(),
43        }
44    }
45}
46
47impl<K, V> Attribute for CustomAttr<K, V>
48where
49    K: CustomAttributeKey,
50    V: AttributeValue,
51{
52    const MIN_LENGTH: usize = 0;
53    type AsyncOutput = CustomAttr<K, V::AsyncOutput>;
54    type State = V::State;
55    type Cloneable = CustomAttr<K, V::Cloneable>;
56    type CloneableOwned = CustomAttr<K, V::CloneableOwned>;
57
58    fn html_len(&self) -> usize {
59        self.key.as_ref().len() + 3 + self.value.html_len()
60    }
61
62    fn to_html(
63        self,
64        buf: &mut String,
65        _class: &mut String,
66        _style: &mut String,
67        _inner_html: &mut String,
68    ) {
69        self.value.to_html(self.key.as_ref(), buf);
70    }
71
72    fn hydrate<const FROM_SERVER: bool>(
73        self,
74        el: &crate::renderer::types::Element,
75    ) -> Self::State {
76        if !K::KEY.is_empty() {
77            self.value.hydrate::<FROM_SERVER>(self.key.as_ref(), el)
78        } else {
79            self.value.build(el, self.key.as_ref())
80        }
81    }
82
83    fn build(self, el: &crate::renderer::types::Element) -> Self::State {
84        self.value.build(el, self.key.as_ref())
85    }
86
87    fn rebuild(self, state: &mut Self::State) {
88        self.value.rebuild(self.key.as_ref(), state);
89    }
90
91    fn into_cloneable(self) -> Self::Cloneable {
92        CustomAttr {
93            key: self.key,
94            value: self.value.into_cloneable(),
95        }
96    }
97
98    fn into_cloneable_owned(self) -> Self::CloneableOwned {
99        CustomAttr {
100            key: self.key,
101            value: self.value.into_cloneable_owned(),
102        }
103    }
104
105    fn dry_resolve(&mut self) {
106        self.value.dry_resolve();
107    }
108
109    async fn resolve(self) -> Self::AsyncOutput {
110        CustomAttr {
111            key: self.key,
112            value: self.value.resolve().await,
113        }
114    }
115
116    fn keys(&self) -> Vec<NamedAttributeKey> {
117        vec![NamedAttributeKey::Attribute(
118            self.key.as_ref().to_string().into(),
119        )]
120    }
121}
122
123impl<K, V> NextAttribute for CustomAttr<K, V>
124where
125    K: CustomAttributeKey,
126    V: AttributeValue,
127{
128    next_attr_output_type!(Self, NewAttr);
129
130    fn add_any_attr<NewAttr: Attribute>(
131        self,
132        new_attr: NewAttr,
133    ) -> Self::Output<NewAttr> {
134        next_attr_combine!(self, new_attr)
135    }
136}
137
138impl<K, V> ToTemplate for CustomAttr<K, V>
139where
140    K: CustomAttributeKey,
141    V: AttributeValue,
142{
143    fn to_template(
144        buf: &mut String,
145        _class: &mut String,
146        _style: &mut String,
147        _inner_html: &mut String,
148        _position: &mut Position,
149    ) {
150        if !K::KEY.is_empty() {
151            V::to_template(K::KEY, buf);
152        }
153    }
154}
155
156// TODO this needs to be a method, not a const
157/// Defines a custom attribute key.
158pub trait CustomAttributeKey: Clone + AsRef<str> + Send + 'static {
159    /// The attribute name.
160    const KEY: &'static str;
161}
162
163impl CustomAttributeKey for &'static str {
164    const KEY: &'static str = "";
165}
166
167impl CustomAttributeKey for Cow<'static, str> {
168    const KEY: &'static str = "";
169}
170
171impl CustomAttributeKey for String {
172    const KEY: &'static str = "";
173}
174
175impl CustomAttributeKey for Arc<str> {
176    const KEY: &'static str = "";
177}
178
179#[cfg(all(feature = "nightly", rustc_nightly))]
180impl<const K: &'static str> CustomAttributeKey
181    for crate::view::static_types::Static<K>
182{
183    const KEY: &'static str = K;
184}
185
186/// Adds a custom attribute to an element.
187pub trait CustomAttribute<K, V>
188where
189    K: CustomAttributeKey,
190    V: AttributeValue,
191
192    Self: Sized + AddAnyAttr,
193{
194    /// Adds an HTML attribute by key and value.
195    fn attr(
196        self,
197        key: K,
198        value: V,
199    ) -> <Self as AddAnyAttr>::Output<CustomAttr<K, V>> {
200        self.add_any_attr(custom_attribute(key, value))
201    }
202}
203
204impl<T, K, V> CustomAttribute<K, V> for T
205where
206    T: AddAnyAttr,
207    K: CustomAttributeKey,
208    V: AttributeValue,
209{
210}