Skip to main content

tachys/view/
iterators.rs

1use super::{
2    add_attr::AddAnyAttr, Mountable, Position, PositionState, Render,
3    RenderHtml,
4};
5use crate::{
6    html::attribute::{any_attribute::AnyAttribute, Attribute},
7    hydration::Cursor,
8    renderer::Rndr,
9    ssr::StreamBuilder,
10};
11use either_of::Either;
12use itertools::Itertools;
13
14/// Retained view state for an `Option`.
15pub type OptionState<T> = Either<<T as Render>::State, <() as Render>::State>;
16
17impl<T> Render for Option<T>
18where
19    T: Render,
20{
21    type State = OptionState<T>;
22
23    fn build(self) -> Self::State {
24        match self {
25            Some(value) => Either::Left(value),
26            None => Either::Right(()),
27        }
28        .build()
29    }
30
31    fn rebuild(self, state: &mut Self::State) {
32        match self {
33            Some(value) => Either::Left(value),
34            None => Either::Right(()),
35        }
36        .rebuild(state)
37    }
38}
39
40impl<T> AddAnyAttr for Option<T>
41where
42    T: AddAnyAttr,
43{
44    type Output<SomeNewAttr: Attribute> =
45        Option<<T as AddAnyAttr>::Output<SomeNewAttr>>;
46
47    fn add_any_attr<NewAttr: Attribute>(
48        self,
49        attr: NewAttr,
50    ) -> Self::Output<NewAttr>
51    where
52        Self::Output<NewAttr>: RenderHtml,
53    {
54        self.map(|n| n.add_any_attr(attr))
55    }
56}
57
58impl<T> RenderHtml for Option<T>
59where
60    T: RenderHtml,
61{
62    type AsyncOutput = Option<T::AsyncOutput>;
63    type Owned = Option<T::Owned>;
64
65    const MIN_LENGTH: usize = T::MIN_LENGTH;
66
67    fn dry_resolve(&mut self) {
68        if let Some(inner) = self.as_mut() {
69            inner.dry_resolve();
70        }
71    }
72
73    async fn resolve(self) -> Self::AsyncOutput {
74        match self {
75            None => None,
76            Some(value) => Some(value.resolve().await),
77        }
78    }
79
80    fn html_len(&self) -> usize {
81        match self {
82            Some(i) => i.html_len() + 3,
83            None => 3,
84        }
85    }
86
87    fn to_html_with_buf(
88        self,
89        buf: &mut String,
90        position: &mut Position,
91        escape: bool,
92        mark_branches: bool,
93        extra_attrs: Vec<AnyAttribute>,
94    ) {
95        match self {
96            Some(value) => Either::Left(value),
97            None => Either::Right(()),
98        }
99        .to_html_with_buf(
100            buf,
101            position,
102            escape,
103            mark_branches,
104            extra_attrs,
105        )
106    }
107
108    fn to_html_async_with_buf<const OUT_OF_ORDER: bool>(
109        self,
110        buf: &mut StreamBuilder,
111        position: &mut Position,
112        escape: bool,
113        mark_branches: bool,
114        extra_attrs: Vec<AnyAttribute>,
115    ) where
116        Self: Sized,
117    {
118        match self {
119            Some(value) => Either::Left(value),
120            None => Either::Right(()),
121        }
122        .to_html_async_with_buf::<OUT_OF_ORDER>(
123            buf,
124            position,
125            escape,
126            mark_branches,
127            extra_attrs,
128        )
129    }
130
131    #[track_caller]
132    fn hydrate<const FROM_SERVER: bool>(
133        self,
134        cursor: &Cursor,
135        position: &PositionState,
136    ) -> Self::State {
137        match self {
138            Some(value) => Either::Left(value),
139            None => Either::Right(()),
140        }
141        .hydrate::<FROM_SERVER>(cursor, position)
142    }
143
144    async fn hydrate_async(
145        self,
146        cursor: &Cursor,
147        position: &PositionState,
148    ) -> Self::State {
149        match self {
150            Some(value) => Either::Left(value),
151            None => Either::Right(()),
152        }
153        .hydrate_async(cursor, position)
154        .await
155    }
156
157    fn into_owned(self) -> Self::Owned {
158        self.map(RenderHtml::into_owned)
159    }
160}
161
162impl<T> Render for Vec<T>
163where
164    T: Render,
165{
166    type State = VecState<T::State>;
167
168    fn build(self) -> Self::State {
169        let marker = Rndr::create_placeholder();
170        VecState {
171            states: self.into_iter().map(T::build).collect(),
172            marker,
173        }
174    }
175
176    fn rebuild(self, state: &mut Self::State) {
177        let VecState { states, marker } = state;
178        let old = states;
179        // this is an unkeyed diff
180        if old.is_empty() {
181            let mut new = self.build().states;
182            for item in new.iter_mut() {
183                Rndr::try_mount_before(item, marker.as_ref());
184            }
185            *old = new;
186        } else if self.is_empty() {
187            // TODO fast path for clearing
188            for item in old.iter_mut() {
189                item.unmount();
190            }
191            old.clear();
192        } else {
193            let mut adds = vec![];
194            let mut removes_at_end = 0;
195            for item in self.into_iter().zip_longest(old.iter_mut()) {
196                match item {
197                    itertools::EitherOrBoth::Both(new, old) => {
198                        T::rebuild(new, old)
199                    }
200                    itertools::EitherOrBoth::Left(new) => {
201                        let mut new_state = new.build();
202                        Rndr::try_mount_before(&mut new_state, marker.as_ref());
203                        adds.push(new_state);
204                    }
205                    itertools::EitherOrBoth::Right(old) => {
206                        removes_at_end += 1;
207                        old.unmount()
208                    }
209                }
210            }
211            old.truncate(old.len() - removes_at_end);
212            old.append(&mut adds);
213        }
214    }
215}
216
217/// Retained view state for a `Vec<_>`.
218pub struct VecState<T>
219where
220    T: Mountable,
221{
222    states: Vec<T>,
223    // Vecs keep a placeholder because they have the potential to add additional items,
224    // after their own items but before the next neighbor. It is much easier to add an
225    // item before a known placeholder than to add it after the last known item, so we
226    // just leave a placeholder here unlike zero-or-one iterators (Option, Result, etc.)
227    marker: crate::renderer::types::Placeholder,
228}
229
230impl<T> Mountable for VecState<T>
231where
232    T: Mountable,
233{
234    fn unmount(&mut self) {
235        for state in self.states.iter_mut() {
236            state.unmount();
237        }
238        self.marker.unmount();
239    }
240
241    fn mount(
242        &mut self,
243        parent: &crate::renderer::types::Element,
244        marker: Option<&crate::renderer::types::Node>,
245    ) {
246        for state in self.states.iter_mut() {
247            state.mount(parent, marker);
248        }
249        self.marker.mount(parent, marker);
250    }
251
252    fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {
253        for state in &self.states {
254            if state.insert_before_this(child) {
255                return true;
256            }
257        }
258        self.marker.insert_before_this(child)
259    }
260
261    fn elements(&self) -> Vec<crate::renderer::types::Element> {
262        self.states
263            .iter()
264            .flat_map(|item| item.elements())
265            .collect()
266    }
267}
268
269impl<T> AddAnyAttr for Vec<T>
270where
271    T: AddAnyAttr,
272{
273    type Output<SomeNewAttr: Attribute> =
274        Vec<<T as AddAnyAttr>::Output<SomeNewAttr::Cloneable>>;
275
276    fn add_any_attr<NewAttr: Attribute>(
277        self,
278        attr: NewAttr,
279    ) -> Self::Output<NewAttr>
280    where
281        Self::Output<NewAttr>: RenderHtml,
282    {
283        let attr = attr.into_cloneable();
284        self.into_iter()
285            .map(|n| n.add_any_attr(attr.clone()))
286            .collect()
287    }
288}
289
290impl<T> RenderHtml for Vec<T>
291where
292    T: RenderHtml,
293{
294    type AsyncOutput = Vec<T::AsyncOutput>;
295    type Owned = Vec<T::Owned>;
296
297    const MIN_LENGTH: usize = 0;
298
299    fn dry_resolve(&mut self) {
300        for inner in self.iter_mut() {
301            inner.dry_resolve();
302        }
303    }
304
305    async fn resolve(self) -> Self::AsyncOutput {
306        futures::future::join_all(self.into_iter().map(T::resolve))
307            .await
308            .into_iter()
309            .collect()
310    }
311
312    fn html_len(&self) -> usize {
313        self.iter().map(|n| n.html_len()).sum::<usize>() + 3
314    }
315
316    fn to_html_with_buf(
317        self,
318        buf: &mut String,
319        position: &mut Position,
320        escape: bool,
321        mark_branches: bool,
322        extra_attrs: Vec<AnyAttribute>,
323    ) {
324        let mut children = self.into_iter();
325        if let Some(first) = children.next() {
326            first.to_html_with_buf(
327                buf,
328                position,
329                escape,
330                mark_branches,
331                extra_attrs.clone(),
332            );
333        }
334        for child in children {
335            child.to_html_with_buf(
336                buf,
337                position,
338                escape,
339                mark_branches,
340                // each child will have the extra attributes applied
341                extra_attrs.clone(),
342            );
343        }
344        if escape {
345            buf.push_str("<!>");
346            *position = Position::NextChild;
347        }
348    }
349
350    fn to_html_async_with_buf<const OUT_OF_ORDER: bool>(
351        self,
352        buf: &mut StreamBuilder,
353        position: &mut Position,
354        escape: bool,
355        mark_branches: bool,
356        extra_attrs: Vec<AnyAttribute>,
357    ) where
358        Self: Sized,
359    {
360        let mut children = self.into_iter();
361        if let Some(first) = children.next() {
362            first.to_html_async_with_buf::<OUT_OF_ORDER>(
363                buf,
364                position,
365                escape,
366                mark_branches,
367                extra_attrs.clone(),
368            );
369        }
370        for child in children {
371            child.to_html_async_with_buf::<OUT_OF_ORDER>(
372                buf,
373                position,
374                escape,
375                mark_branches,
376                extra_attrs.clone(),
377            );
378        }
379        if escape {
380            buf.push_sync("<!>");
381            *position = Position::NextChild;
382        }
383    }
384
385    fn hydrate<const FROM_SERVER: bool>(
386        self,
387        cursor: &Cursor,
388        position: &PositionState,
389    ) -> Self::State {
390        let states = self
391            .into_iter()
392            .map(|child| child.hydrate::<FROM_SERVER>(cursor, position))
393            .collect();
394
395        let marker = cursor.next_placeholder(position);
396        position.set(Position::NextChild);
397
398        VecState { states, marker }
399    }
400
401    async fn hydrate_async(
402        self,
403        cursor: &Cursor,
404        position: &PositionState,
405    ) -> Self::State {
406        let mut states = Vec::with_capacity(self.len());
407        for child in self {
408            states.push(child.hydrate_async(cursor, position).await);
409        }
410
411        let marker = cursor.next_placeholder(position);
412        position.set(Position::NextChild);
413
414        VecState { states, marker }
415    }
416
417    fn into_owned(self) -> Self::Owned {
418        self.into_iter()
419            .map(RenderHtml::into_owned)
420            .collect::<Vec<_>>()
421    }
422}
423
424/// A container used for ErasedMode. It's slightly better than a raw Vec<> because the rendering traits don't have to worry about the length of the Vec changing, therefore no marker traits etc.
425pub struct StaticVec<T>(pub(crate) Vec<T>);
426
427impl<T: Clone> Clone for StaticVec<T> {
428    fn clone(&self) -> Self {
429        Self(self.0.clone())
430    }
431}
432
433impl<T> IntoIterator for StaticVec<T> {
434    type Item = T;
435    type IntoIter = std::vec::IntoIter<T>;
436
437    fn into_iter(self) -> Self::IntoIter {
438        self.0.into_iter()
439    }
440}
441
442impl<T> StaticVec<T> {
443    /// Iterates over the items.
444    pub fn iter(&self) -> std::slice::Iter<'_, T> {
445        self.0.iter()
446    }
447}
448
449impl<T> From<Vec<T>> for StaticVec<T> {
450    fn from(vec: Vec<T>) -> Self {
451        Self(vec)
452    }
453}
454
455impl<T> From<StaticVec<T>> for Vec<T> {
456    fn from(static_vec: StaticVec<T>) -> Self {
457        static_vec.0
458    }
459}
460
461/// Retained view state for a `StaticVec<Vec<_>>`.
462pub struct StaticVecState<T>
463where
464    T: Mountable,
465{
466    states: Vec<T>,
467    marker: crate::renderer::types::Placeholder,
468}
469
470impl<T> Mountable for StaticVecState<T>
471where
472    T: Mountable,
473{
474    fn unmount(&mut self) {
475        for state in self.states.iter_mut() {
476            state.unmount();
477        }
478        self.marker.unmount();
479    }
480
481    fn mount(
482        &mut self,
483        parent: &crate::renderer::types::Element,
484        marker: Option<&crate::renderer::types::Node>,
485    ) {
486        for state in self.states.iter_mut() {
487            state.mount(parent, marker);
488        }
489        self.marker.mount(parent, marker);
490    }
491
492    fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {
493        for state in &self.states {
494            if state.insert_before_this(child) {
495                return true;
496            }
497        }
498        self.marker.insert_before_this(child)
499    }
500
501    fn elements(&self) -> Vec<crate::renderer::types::Element> {
502        self.states
503            .iter()
504            .flat_map(|item| item.elements())
505            .collect()
506    }
507}
508
509impl<T> Render for StaticVec<T>
510where
511    T: Render,
512{
513    type State = StaticVecState<T::State>;
514
515    fn build(self) -> Self::State {
516        let marker = Rndr::create_placeholder();
517        Self::State {
518            states: self.0.into_iter().map(T::build).collect(),
519            marker,
520        }
521    }
522
523    fn rebuild(self, state: &mut Self::State) {
524        let StaticVecState { states, marker } = state;
525        let old = states;
526
527        // reuses the Vec impl
528        if old.is_empty() {
529            let mut new = self.build().states;
530            for item in new.iter_mut() {
531                Rndr::mount_before(item, marker.as_ref());
532            }
533            *old = new;
534        } else if self.0.is_empty() {
535            // TODO fast path for clearing
536            for item in old.iter_mut() {
537                item.unmount();
538            }
539            old.clear();
540        } else {
541            let mut adds = vec![];
542            let mut removes_at_end = 0;
543            for item in self.0.into_iter().zip_longest(old.iter_mut()) {
544                match item {
545                    itertools::EitherOrBoth::Both(new, old) => {
546                        T::rebuild(new, old)
547                    }
548                    itertools::EitherOrBoth::Left(new) => {
549                        let mut new_state = new.build();
550                        Rndr::mount_before(&mut new_state, marker.as_ref());
551                        adds.push(new_state);
552                    }
553                    itertools::EitherOrBoth::Right(old) => {
554                        removes_at_end += 1;
555                        old.unmount()
556                    }
557                }
558            }
559            old.truncate(old.len() - removes_at_end);
560            old.append(&mut adds);
561        }
562    }
563}
564
565impl<T> AddAnyAttr for StaticVec<T>
566where
567    T: AddAnyAttr,
568{
569    type Output<SomeNewAttr: Attribute> =
570        StaticVec<<T as AddAnyAttr>::Output<SomeNewAttr::Cloneable>>;
571
572    fn add_any_attr<NewAttr: Attribute>(
573        self,
574        attr: NewAttr,
575    ) -> Self::Output<NewAttr>
576    where
577        Self::Output<NewAttr>: RenderHtml,
578    {
579        let attr = attr.into_cloneable();
580        self.0
581            .into_iter()
582            .map(|n| n.add_any_attr(attr.clone()))
583            .collect::<Vec<_>>()
584            .into()
585    }
586}
587
588impl<T> RenderHtml for StaticVec<T>
589where
590    T: RenderHtml,
591{
592    type AsyncOutput = StaticVec<T::AsyncOutput>;
593    type Owned = StaticVec<T::Owned>;
594
595    const MIN_LENGTH: usize = 0;
596
597    fn dry_resolve(&mut self) {
598        for inner in self.0.iter_mut() {
599            inner.dry_resolve();
600        }
601    }
602
603    async fn resolve(self) -> Self::AsyncOutput {
604        futures::future::join_all(self.0.into_iter().map(T::resolve))
605            .await
606            .into_iter()
607            .collect::<Vec<_>>()
608            .into()
609    }
610
611    fn html_len(&self) -> usize {
612        self.0.iter().map(RenderHtml::html_len).sum::<usize>() + 3
613    }
614
615    fn to_html_with_buf(
616        self,
617        buf: &mut String,
618        position: &mut Position,
619        escape: bool,
620        mark_branches: bool,
621        extra_attrs: Vec<AnyAttribute>,
622    ) {
623        for child in self.0.into_iter() {
624            child.to_html_with_buf(
625                buf,
626                position,
627                escape,
628                mark_branches,
629                extra_attrs.clone(),
630            );
631        }
632        if escape {
633            buf.push_str("<!>");
634            *position = Position::NextChild;
635        }
636    }
637
638    fn to_html_async_with_buf<const OUT_OF_ORDER: bool>(
639        self,
640        buf: &mut StreamBuilder,
641        position: &mut Position,
642        escape: bool,
643        mark_branches: bool,
644        extra_attrs: Vec<AnyAttribute>,
645    ) where
646        Self: Sized,
647    {
648        for child in self.0.into_iter() {
649            child.to_html_async_with_buf::<OUT_OF_ORDER>(
650                buf,
651                position,
652                escape,
653                mark_branches,
654                extra_attrs.clone(),
655            );
656        }
657        if escape {
658            buf.push_sync("<!>");
659            *position = Position::NextChild;
660        }
661    }
662
663    fn hydrate<const FROM_SERVER: bool>(
664        self,
665        cursor: &Cursor,
666        position: &PositionState,
667    ) -> Self::State {
668        let states = self
669            .0
670            .into_iter()
671            .map(|child| child.hydrate::<FROM_SERVER>(cursor, position))
672            .collect();
673
674        let marker = cursor.next_placeholder(position);
675        position.set(Position::NextChild);
676
677        Self::State { states, marker }
678    }
679
680    async fn hydrate_async(
681        self,
682        cursor: &Cursor,
683        position: &PositionState,
684    ) -> Self::State {
685        let mut states = Vec::with_capacity(self.0.len());
686        for child in self.0 {
687            states.push(child.hydrate_async(cursor, position).await);
688        }
689
690        let marker = cursor.next_placeholder(position);
691        position.set(Position::NextChild);
692
693        Self::State { states, marker }
694    }
695
696    fn into_owned(self) -> Self::Owned {
697        self.0
698            .into_iter()
699            .map(RenderHtml::into_owned)
700            .collect::<Vec<_>>()
701            .into()
702    }
703}
704
705impl<T, const N: usize> Render for [T; N]
706where
707    T: Render,
708{
709    type State = ArrayState<T::State, N>;
710
711    fn build(self) -> Self::State {
712        Self::State {
713            states: self.map(T::build),
714        }
715    }
716
717    fn rebuild(self, state: &mut Self::State) {
718        let Self::State { states } = state;
719        let old = states;
720        // this is an unkeyed diff
721        self.into_iter()
722            .zip(old.iter_mut())
723            .for_each(|(new, old)| T::rebuild(new, old));
724    }
725}
726
727/// Retained view state for a `Vec<_>`.
728pub struct ArrayState<T, const N: usize>
729where
730    T: Mountable,
731{
732    states: [T; N],
733}
734
735impl<T, const N: usize> Mountable for ArrayState<T, N>
736where
737    T: Mountable,
738{
739    fn unmount(&mut self) {
740        self.states.iter_mut().for_each(Mountable::unmount);
741    }
742
743    fn mount(
744        &mut self,
745        parent: &crate::renderer::types::Element,
746        marker: Option<&crate::renderer::types::Node>,
747    ) {
748        for state in self.states.iter_mut() {
749            state.mount(parent, marker);
750        }
751    }
752
753    fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {
754        for state in &self.states {
755            if state.insert_before_this(child) {
756                return true;
757            }
758        }
759        false
760    }
761
762    fn elements(&self) -> Vec<crate::renderer::types::Element> {
763        self.states
764            .iter()
765            .flat_map(|item| item.elements())
766            .collect()
767    }
768}
769impl<T, const N: usize> AddAnyAttr for [T; N]
770where
771    T: AddAnyAttr,
772{
773    type Output<SomeNewAttr: Attribute> =
774        [<T as AddAnyAttr>::Output<SomeNewAttr::Cloneable>; N];
775
776    fn add_any_attr<NewAttr: Attribute>(
777        self,
778        attr: NewAttr,
779    ) -> Self::Output<NewAttr>
780    where
781        Self::Output<NewAttr>: RenderHtml,
782    {
783        let attr = attr.into_cloneable();
784        self.map(|n| n.add_any_attr(attr.clone()))
785    }
786}
787
788impl<T, const N: usize> RenderHtml for [T; N]
789where
790    T: RenderHtml,
791{
792    type AsyncOutput = [T::AsyncOutput; N];
793    type Owned = [T::Owned; N];
794
795    const MIN_LENGTH: usize = 0;
796
797    fn dry_resolve(&mut self) {
798        for inner in self.iter_mut() {
799            inner.dry_resolve();
800        }
801    }
802
803    async fn resolve(self) -> Self::AsyncOutput {
804        futures::future::join_all(self.into_iter().map(T::resolve))
805            .await
806            .into_iter()
807            .collect::<Vec<_>>()
808            .try_into()
809            .unwrap_or_else(|_| unreachable!())
810    }
811
812    fn html_len(&self) -> usize {
813        self.iter().map(RenderHtml::html_len).sum::<usize>()
814    }
815
816    fn to_html_with_buf(
817        self,
818        buf: &mut String,
819        position: &mut Position,
820        escape: bool,
821        mark_branches: bool,
822        extra_attrs: Vec<AnyAttribute>,
823    ) {
824        for child in self.into_iter() {
825            child.to_html_with_buf(
826                buf,
827                position,
828                escape,
829                mark_branches,
830                extra_attrs.clone(),
831            );
832        }
833    }
834
835    fn to_html_async_with_buf<const OUT_OF_ORDER: bool>(
836        self,
837        buf: &mut StreamBuilder,
838        position: &mut Position,
839        escape: bool,
840        mark_branches: bool,
841        extra_attrs: Vec<AnyAttribute>,
842    ) where
843        Self: Sized,
844    {
845        for child in self.into_iter() {
846            child.to_html_async_with_buf::<OUT_OF_ORDER>(
847                buf,
848                position,
849                escape,
850                mark_branches,
851                extra_attrs.clone(),
852            );
853        }
854    }
855
856    fn hydrate<const FROM_SERVER: bool>(
857        self,
858        cursor: &Cursor,
859        position: &PositionState,
860    ) -> Self::State {
861        let states =
862            self.map(|child| child.hydrate::<FROM_SERVER>(cursor, position));
863        ArrayState { states }
864    }
865
866    async fn hydrate_async(
867        self,
868        cursor: &Cursor,
869        position: &PositionState,
870    ) -> Self::State {
871        let mut states = Vec::with_capacity(self.len());
872        for child in self {
873            states.push(child.hydrate_async(cursor, position).await);
874        }
875        let Ok(states) = <[<T as Render>::State; N]>::try_from(states) else {
876            unreachable!()
877        };
878        ArrayState { states }
879    }
880
881    fn into_owned(self) -> Self::Owned {
882        self.into_iter()
883            .map(RenderHtml::into_owned)
884            .collect::<Vec<_>>()
885            .try_into()
886            .unwrap_or_else(|_| unreachable!())
887    }
888}