1use crate::renderer::Rndr;
2use std::{
3 borrow::Cow,
4 future::Future,
5 net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6},
6 num::{
7 NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8,
8 NonZeroIsize, NonZeroU128, NonZeroU16, NonZeroU32, NonZeroU64,
9 NonZeroU8, NonZeroUsize,
10 },
11 sync::Arc,
12};
13
14pub trait IntoAttributeValue {
16 type Output;
18
19 fn into_attribute_value(self) -> Self::Output;
21}
22
23impl<T> IntoAttributeValue for T
24where
25 T: AttributeValue,
26{
27 type Output = Self;
28
29 fn into_attribute_value(self) -> Self::Output {
30 self
31 }
32}
33
34pub trait AttributeValue: Send {
36 type State;
38
39 type AsyncOutput: AttributeValue;
41
42 type Cloneable: AttributeValue + Clone;
49
50 type CloneableOwned: AttributeValue + Clone + 'static;
54
55 fn html_len(&self) -> usize;
57
58 fn to_html(self, key: &str, buf: &mut String);
60
61 fn to_template(key: &str, buf: &mut String);
63
64 fn hydrate<const FROM_SERVER: bool>(
67 self,
68 key: &str,
69 el: &crate::renderer::types::Element,
70 ) -> Self::State;
71
72 fn build(
74 self,
75 el: &crate::renderer::types::Element,
76 key: &str,
77 ) -> Self::State;
78
79 fn rebuild(self, key: &str, state: &mut Self::State);
81
82 fn into_cloneable(self) -> Self::Cloneable;
84
85 fn into_cloneable_owned(self) -> Self::CloneableOwned;
87
88 fn dry_resolve(&mut self);
92
93 fn resolve(self) -> impl Future<Output = Self::AsyncOutput> + Send;
95}
96
97impl AttributeValue for () {
98 type State = ();
99 type AsyncOutput = ();
100 type Cloneable = ();
101 type CloneableOwned = ();
102
103 fn html_len(&self) -> usize {
104 0
105 }
106
107 fn to_html(self, _key: &str, _buf: &mut String) {}
108
109 fn to_template(_key: &str, _buf: &mut String) {}
110
111 fn hydrate<const FROM_SERVER: bool>(
112 self,
113 _key: &str,
114 _el: &crate::renderer::types::Element,
115 ) {
116 }
117
118 fn build(
119 self,
120 _el: &crate::renderer::types::Element,
121 _key: &str,
122 ) -> Self::State {
123 }
124
125 fn rebuild(self, _key: &str, _state: &mut Self::State) {}
126
127 fn into_cloneable(self) -> Self::Cloneable {
128 self
129 }
130
131 fn into_cloneable_owned(self) -> Self::CloneableOwned {
132 self
133 }
134
135 fn dry_resolve(&mut self) {}
136
137 async fn resolve(self) {}
138}
139
140impl<'a> AttributeValue for &'a str {
141 type State = (crate::renderer::types::Element, &'a str);
142 type AsyncOutput = &'a str;
143 type Cloneable = &'a str;
144 type CloneableOwned = Arc<str>;
145
146 fn html_len(&self) -> usize {
147 self.len()
148 }
149
150 fn to_html(self, key: &str, buf: &mut String) {
151 buf.push(' ');
152 buf.push_str(key);
153 buf.push_str("=\"");
154 buf.push_str(&escape_attr(self));
155 buf.push('"');
156 }
157
158 fn to_template(_key: &str, _buf: &mut String) {}
159
160 fn hydrate<const FROM_SERVER: bool>(
161 self,
162 key: &str,
163 el: &crate::renderer::types::Element,
164 ) -> Self::State {
165 if !FROM_SERVER {
168 Rndr::set_attribute(el, key, self);
169 }
170 (el.clone(), self)
171 }
172
173 fn build(
174 self,
175 el: &crate::renderer::types::Element,
176 key: &str,
177 ) -> Self::State {
178 Rndr::set_attribute(el, key, self);
179 (el.to_owned(), self)
180 }
181
182 fn rebuild(self, key: &str, state: &mut Self::State) {
183 let (el, prev_value) = state;
184 if self != *prev_value {
185 Rndr::set_attribute(el, key, self);
186 }
187 *prev_value = self;
188 }
189
190 fn into_cloneable(self) -> Self::Cloneable {
191 self
192 }
193
194 fn into_cloneable_owned(self) -> Self::CloneableOwned {
195 self.into()
196 }
197
198 fn dry_resolve(&mut self) {}
199
200 async fn resolve(self) -> Self::AsyncOutput {
201 self
202 }
203}
204
205impl<'a> AttributeValue for Cow<'a, str> {
206 type State = (crate::renderer::types::Element, Self);
207 type AsyncOutput = Self;
208 type Cloneable = Arc<str>;
209 type CloneableOwned = Arc<str>;
210
211 fn html_len(&self) -> usize {
212 self.len()
213 }
214
215 fn to_html(self, key: &str, buf: &mut String) {
216 buf.push(' ');
217 buf.push_str(key);
218 buf.push_str("=\"");
219 buf.push_str(&escape_attr(&self));
220 buf.push('"');
221 }
222
223 fn to_template(_key: &str, _buf: &mut String) {}
224
225 fn hydrate<const FROM_SERVER: bool>(
226 self,
227 key: &str,
228 el: &crate::renderer::types::Element,
229 ) -> Self::State {
230 if !FROM_SERVER {
233 Rndr::set_attribute(el, key, &self);
234 }
235 (el.clone(), self)
236 }
237
238 fn build(
239 self,
240 el: &crate::renderer::types::Element,
241 key: &str,
242 ) -> Self::State {
243 Rndr::set_attribute(el, key, &self);
244 (el.to_owned(), self)
245 }
246
247 fn rebuild(self, key: &str, state: &mut Self::State) {
248 let (el, prev_value) = state;
249 if self != *prev_value {
250 Rndr::set_attribute(el, key, &self);
251 }
252 *prev_value = self;
253 }
254
255 fn into_cloneable(self) -> Self::Cloneable {
256 self.into()
257 }
258
259 fn into_cloneable_owned(self) -> Self::CloneableOwned {
260 self.into()
261 }
262
263 fn dry_resolve(&mut self) {}
264
265 async fn resolve(self) -> Self::AsyncOutput {
266 self
267 }
268}
269
270#[cfg(all(feature = "nightly", rustc_nightly))]
271impl<const V: &'static str> AttributeValue
272 for crate::view::static_types::Static<V>
273{
274 type AsyncOutput = Self;
275 type State = ();
276 type Cloneable = Self;
277 type CloneableOwned = Self;
278
279 fn html_len(&self) -> usize {
280 V.len()
281 }
282
283 fn to_html(self, key: &str, buf: &mut String) {
284 <&str as AttributeValue>::to_html(V, key, buf);
285 }
286
287 fn to_template(key: &str, buf: &mut String) {
288 buf.push(' ');
289 buf.push_str(key);
290 buf.push_str("=\"");
291 buf.push_str(V);
292 buf.push('"');
293 }
294
295 fn hydrate<const FROM_SERVER: bool>(
296 self,
297 _key: &str,
298 _el: &crate::renderer::types::Element,
299 ) -> Self::State {
300 }
301
302 fn build(
303 self,
304 el: &crate::renderer::types::Element,
305 key: &str,
306 ) -> Self::State {
307 <&str as AttributeValue>::build(V, el, key);
308 }
309
310 fn rebuild(self, _key: &str, _state: &mut Self::State) {}
311
312 fn into_cloneable(self) -> Self::Cloneable {
313 self
314 }
315
316 fn into_cloneable_owned(self) -> Self::CloneableOwned {
317 self
318 }
319
320 fn dry_resolve(&mut self) {}
321
322 async fn resolve(self) -> Self::AsyncOutput {
323 self
324 }
325}
326
327impl<'a> AttributeValue for &'a String {
328 type AsyncOutput = Self;
329 type State = (crate::renderer::types::Element, &'a String);
330 type Cloneable = Self;
331 type CloneableOwned = Arc<str>;
332
333 fn html_len(&self) -> usize {
334 self.len()
335 }
336
337 fn to_html(self, key: &str, buf: &mut String) {
338 <&str as AttributeValue>::to_html(self.as_str(), key, buf);
339 }
340
341 fn to_template(_key: &str, _buf: &mut String) {}
342
343 fn hydrate<const FROM_SERVER: bool>(
344 self,
345 key: &str,
346 el: &crate::renderer::types::Element,
347 ) -> Self::State {
348 let (el, _) = <&str as AttributeValue>::hydrate::<FROM_SERVER>(
349 self.as_str(),
350 key,
351 el,
352 );
353 (el, self)
354 }
355
356 fn build(
357 self,
358 el: &crate::renderer::types::Element,
359 key: &str,
360 ) -> Self::State {
361 Rndr::set_attribute(el, key, self);
362 (el.clone(), self)
363 }
364
365 fn rebuild(self, key: &str, state: &mut Self::State) {
366 let (el, prev_value) = state;
367 if self != *prev_value {
368 Rndr::set_attribute(el, key, self);
369 }
370 *prev_value = self;
371 }
372
373 fn into_cloneable(self) -> Self::Cloneable {
374 self
375 }
376
377 fn into_cloneable_owned(self) -> Self::CloneableOwned {
378 self.as_str().into()
379 }
380
381 fn dry_resolve(&mut self) {}
382
383 async fn resolve(self) -> Self::AsyncOutput {
384 self
385 }
386}
387
388impl AttributeValue for String {
389 type AsyncOutput = Self;
390 type State = (crate::renderer::types::Element, String);
391 type Cloneable = Arc<str>;
392 type CloneableOwned = Arc<str>;
393
394 fn html_len(&self) -> usize {
395 self.len()
396 }
397
398 fn to_html(self, key: &str, buf: &mut String) {
399 <&str as AttributeValue>::to_html(self.as_str(), key, buf);
400 }
401
402 fn to_template(_key: &str, _buf: &mut String) {}
403
404 fn hydrate<const FROM_SERVER: bool>(
405 self,
406 key: &str,
407 el: &crate::renderer::types::Element,
408 ) -> Self::State {
409 let (el, _) = <&str as AttributeValue>::hydrate::<FROM_SERVER>(
410 self.as_str(),
411 key,
412 el,
413 );
414 (el, self)
415 }
416
417 fn build(
418 self,
419 el: &crate::renderer::types::Element,
420 key: &str,
421 ) -> Self::State {
422 Rndr::set_attribute(el, key, &self);
423 (el.clone(), self)
424 }
425
426 fn rebuild(self, key: &str, state: &mut Self::State) {
427 let (el, prev_value) = state;
428 if self != *prev_value {
429 Rndr::set_attribute(el, key, &self);
430 }
431 *prev_value = self;
432 }
433
434 fn into_cloneable(self) -> Self::Cloneable {
435 self.into()
436 }
437
438 fn into_cloneable_owned(self) -> Self::CloneableOwned {
439 self.into()
440 }
441
442 fn dry_resolve(&mut self) {}
443
444 async fn resolve(self) -> Self::AsyncOutput {
445 self
446 }
447}
448
449impl AttributeValue for Arc<str> {
450 type AsyncOutput = Self;
451 type State = (crate::renderer::types::Element, Arc<str>);
452 type Cloneable = Arc<str>;
453 type CloneableOwned = Arc<str>;
454
455 fn html_len(&self) -> usize {
456 self.len()
457 }
458
459 fn to_html(self, key: &str, buf: &mut String) {
460 <&str as AttributeValue>::to_html(self.as_ref(), key, buf);
461 }
462
463 fn to_template(_key: &str, _buf: &mut String) {}
464
465 fn hydrate<const FROM_SERVER: bool>(
466 self,
467 key: &str,
468 el: &crate::renderer::types::Element,
469 ) -> Self::State {
470 let (el, _) = <&str as AttributeValue>::hydrate::<FROM_SERVER>(
471 self.as_ref(),
472 key,
473 el,
474 );
475 (el, self)
476 }
477
478 fn build(
479 self,
480 el: &crate::renderer::types::Element,
481 key: &str,
482 ) -> Self::State {
483 Rndr::set_attribute(el, key, &self);
484 (el.clone(), self)
485 }
486
487 fn rebuild(self, key: &str, state: &mut Self::State) {
488 let (el, prev_value) = state;
489 if self != *prev_value {
490 Rndr::set_attribute(el, key, &self);
491 }
492 *prev_value = self;
493 }
494
495 fn into_cloneable(self) -> Self::Cloneable {
496 self
497 }
498
499 fn into_cloneable_owned(self) -> Self::CloneableOwned {
500 self
501 }
502
503 fn dry_resolve(&mut self) {}
504
505 async fn resolve(self) -> Self::AsyncOutput {
506 self
507 }
508}
509impl AttributeValue for bool {
512 type AsyncOutput = Self;
513 type State = (crate::renderer::types::Element, bool);
514 type Cloneable = Self;
515 type CloneableOwned = Self;
516
517 fn html_len(&self) -> usize {
518 0
519 }
520
521 fn to_html(self, key: &str, buf: &mut String) {
522 if self {
523 buf.push(' ');
524 buf.push_str(key);
525 }
526 }
527
528 fn to_template(_key: &str, _buf: &mut String) {}
529
530 fn hydrate<const FROM_SERVER: bool>(
531 self,
532 key: &str,
533 el: &crate::renderer::types::Element,
534 ) -> Self::State {
535 if !FROM_SERVER {
538 Rndr::set_attribute(el, key, "");
539 }
540 (el.clone(), self)
541 }
542
543 fn build(
544 self,
545 el: &crate::renderer::types::Element,
546 key: &str,
547 ) -> Self::State {
548 if self {
549 Rndr::set_attribute(el, key, "");
550 }
551 (el.clone(), self)
552 }
553
554 fn rebuild(self, key: &str, state: &mut Self::State) {
555 let (el, prev_value) = state;
556 if self != *prev_value {
557 if self {
558 Rndr::set_attribute(el, key, "");
559 } else {
560 Rndr::remove_attribute(el, key);
561 }
562 }
563 *prev_value = self;
564 }
565
566 fn into_cloneable(self) -> Self::Cloneable {
567 self
568 }
569
570 fn into_cloneable_owned(self) -> Self::CloneableOwned {
571 self
572 }
573
574 fn dry_resolve(&mut self) {}
575
576 async fn resolve(self) -> Self::AsyncOutput {
577 self
578 }
579}
580
581impl<V> AttributeValue for Option<V>
582where
583 V: AttributeValue,
584{
585 type AsyncOutput = Option<V::AsyncOutput>;
586 type State = (crate::renderer::types::Element, Option<V::State>);
587 type Cloneable = Option<V::Cloneable>;
588 type CloneableOwned = Option<V::CloneableOwned>;
589
590 fn html_len(&self) -> usize {
591 match self {
592 Some(i) => i.html_len(),
593 None => 0,
594 }
595 }
596
597 fn to_html(self, key: &str, buf: &mut String) {
598 if let Some(v) = self {
599 v.to_html(key, buf);
600 }
601 }
602
603 fn to_template(_key: &str, _buf: &mut String) {}
604
605 fn hydrate<const FROM_SERVER: bool>(
606 self,
607 key: &str,
608 el: &crate::renderer::types::Element,
609 ) -> Self::State {
610 let state = self.map(|v| v.hydrate::<FROM_SERVER>(key, el));
611 (el.clone(), state)
612 }
613
614 fn build(
615 self,
616 el: &crate::renderer::types::Element,
617 key: &str,
618 ) -> Self::State {
619 let el = el.clone();
620 let v = self.map(|v| v.build(&el, key));
621 (el, v)
622 }
623
624 fn rebuild(self, key: &str, state: &mut Self::State) {
625 let (el, prev) = state;
626 match (self, prev.as_mut()) {
627 (None, None) => {}
628 (None, Some(_)) => {
629 Rndr::remove_attribute(el, key);
630 *prev = None;
631 }
632 (Some(value), None) => {
633 *prev = Some(value.build(el, key));
634 }
635 (Some(new), Some(old)) => {
636 new.rebuild(key, old);
637 }
638 }
639 }
640
641 fn into_cloneable(self) -> Self::Cloneable {
642 self.map(|value| value.into_cloneable())
643 }
644
645 fn into_cloneable_owned(self) -> Self::CloneableOwned {
646 self.map(|value| value.into_cloneable_owned())
647 }
648
649 fn dry_resolve(&mut self) {
650 if let Some(inner) = self.as_mut() {
651 inner.dry_resolve();
652 }
653 }
654
655 async fn resolve(self) -> Self::AsyncOutput {
656 match self {
657 None => None,
658 Some(inner) => Some(inner.resolve().await),
659 }
660 }
661}
662
663pub(crate) fn escape_attr(value: &str) -> Cow<'_, str> {
664 html_escape::encode_double_quoted_attribute(value)
665}
666
667macro_rules! render_primitive {
668 ($($child_type:ty),* $(,)?) => {
669 $(
670 impl AttributeValue for $child_type
671 where
672
673 {
674 type AsyncOutput = $child_type;
675 type State = (crate::renderer::types::Element, $child_type);
676 type Cloneable = Self;
677 type CloneableOwned = Self;
678
679 fn html_len(&self) -> usize {
680 0
681 }
682
683 fn to_html(self, key: &str, buf: &mut String) {
684 <String as AttributeValue>::to_html(self.to_string(), key, buf);
685 }
686
687 fn to_template(_key: &str, _buf: &mut String) {}
688
689 fn hydrate<const FROM_SERVER: bool>(
690 self,
691 key: &str,
692 el: &crate::renderer::types::Element,
693 ) -> Self::State {
694 if !FROM_SERVER {
697 Rndr::set_attribute(el, key, &self.to_string());
698 }
699 (el.clone(), self)
700 }
701
702 fn build(self, el: &crate::renderer::types::Element, key: &str) -> Self::State {
703 Rndr::set_attribute(el, key, &self.to_string());
704 (el.to_owned(), self)
705 }
706
707 fn rebuild(self, key: &str, state: &mut Self::State) {
708 let (el, prev_value) = state;
709 if self != *prev_value {
710 Rndr::set_attribute(el, key, &self.to_string());
711 }
712 *prev_value = self;
713 }
714
715 fn into_cloneable(self) -> Self::Cloneable {
716 self
717 }
718
719 fn into_cloneable_owned(self) -> Self::CloneableOwned {
720 self
721 }
722
723 fn dry_resolve(&mut self) {
724 }
725
726 async fn resolve(self) -> Self::AsyncOutput {
727 self
728 }
729 }
730 )*
731 }
732}
733
734render_primitive![
735 usize,
736 u8,
737 u16,
738 u32,
739 u64,
740 u128,
741 isize,
742 i8,
743 i16,
744 i32,
745 i64,
746 i128,
747 f32,
748 f64,
749 char,
750 IpAddr,
751 SocketAddr,
752 SocketAddrV4,
753 SocketAddrV6,
754 Ipv4Addr,
755 Ipv6Addr,
756 NonZeroI8,
757 NonZeroU8,
758 NonZeroI16,
759 NonZeroU16,
760 NonZeroI32,
761 NonZeroU32,
762 NonZeroI64,
763 NonZeroU64,
764 NonZeroI128,
765 NonZeroU128,
766 NonZeroIsize,
767 NonZeroUsize,
768];