tachys/view/
either.rs

1use super::{
2    add_attr::AddAnyAttr, MarkBranch, Mountable, Position, PositionState,
3    Render, RenderHtml,
4};
5use crate::{
6    html::attribute::{Attribute, NextAttribute},
7    hydration::Cursor,
8    ssr::StreamBuilder,
9};
10use either_of::*;
11use futures::future::join;
12
13impl<A, B> Render for Either<A, B>
14where
15    A: Render,
16    B: Render,
17{
18    type State = Either<A::State, B::State>;
19
20    fn build(self) -> Self::State {
21        match self {
22            Either::Left(left) => Either::Left(left.build()),
23            Either::Right(right) => Either::Right(right.build()),
24        }
25    }
26
27    fn rebuild(self, state: &mut Self::State) {
28        match (self, &mut *state) {
29            (Either::Left(new), Either::Left(old)) => {
30                new.rebuild(old);
31            }
32            (Either::Right(new), Either::Right(old)) => {
33                new.rebuild(old);
34            }
35            (Either::Right(new), Either::Left(old)) => {
36                let mut new_state = new.build();
37                old.insert_before_this(&mut new_state);
38                old.unmount();
39                *state = Either::Right(new_state);
40            }
41            (Either::Left(new), Either::Right(old)) => {
42                let mut new_state = new.build();
43                old.insert_before_this(&mut new_state);
44                old.unmount();
45                *state = Either::Left(new_state);
46            }
47        }
48    }
49}
50
51impl<A, B> Mountable for Either<A, B>
52where
53    A: Mountable,
54    B: Mountable,
55{
56    fn unmount(&mut self) {
57        match self {
58            Either::Left(left) => left.unmount(),
59            Either::Right(right) => right.unmount(),
60        }
61    }
62
63    fn mount(
64        &mut self,
65        parent: &crate::renderer::types::Element,
66        marker: Option<&crate::renderer::types::Node>,
67    ) {
68        match self {
69            Either::Left(left) => left.mount(parent, marker),
70            Either::Right(right) => right.mount(parent, marker),
71        }
72    }
73
74    fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {
75        match &self {
76            Either::Left(left) => left.insert_before_this(child),
77            Either::Right(right) => right.insert_before_this(child),
78        }
79    }
80}
81
82impl<A, B> AddAnyAttr for Either<A, B>
83where
84    A: RenderHtml,
85    B: RenderHtml,
86{
87    type Output<SomeNewAttr: Attribute> = Either<
88        <A as AddAnyAttr>::Output<SomeNewAttr>,
89        <B as AddAnyAttr>::Output<SomeNewAttr>,
90    >;
91
92    fn add_any_attr<NewAttr: Attribute>(
93        self,
94        attr: NewAttr,
95    ) -> Self::Output<NewAttr>
96    where
97        Self::Output<NewAttr>: RenderHtml,
98    {
99        match self {
100            Either::Left(i) => Either::Left(i.add_any_attr(attr)),
101            Either::Right(i) => Either::Right(i.add_any_attr(attr)),
102        }
103    }
104}
105
106const fn max_usize(vals: &[usize]) -> usize {
107    let mut max = 0;
108    let len = vals.len();
109    let mut i = 0;
110    while i < len {
111        if vals[i] > max {
112            max = vals[i];
113        }
114        i += 1;
115    }
116    max
117}
118
119#[cfg(not(erase_components))]
120impl<A, B> NextAttribute for Either<A, B>
121where
122    B: NextAttribute,
123    A: NextAttribute,
124{
125    type Output<NewAttr: Attribute> = Either<
126        <A as NextAttribute>::Output<NewAttr>,
127        <B as NextAttribute>::Output<NewAttr>,
128    >;
129
130    fn add_any_attr<NewAttr: Attribute>(
131        self,
132        new_attr: NewAttr,
133    ) -> Self::Output<NewAttr> {
134        match self {
135            Either::Left(left) => Either::Left(left.add_any_attr(new_attr)),
136            Either::Right(right) => Either::Right(right.add_any_attr(new_attr)),
137        }
138    }
139}
140
141#[cfg(erase_components)]
142use crate::html::attribute::any_attribute::{AnyAttribute, IntoAnyAttribute};
143
144#[cfg(erase_components)]
145impl<A, B> NextAttribute for Either<A, B>
146where
147    B: IntoAnyAttribute,
148    A: IntoAnyAttribute,
149{
150    type Output<NewAttr: Attribute> = Vec<AnyAttribute>;
151
152    fn add_any_attr<NewAttr: Attribute>(
153        self,
154        new_attr: NewAttr,
155    ) -> Self::Output<NewAttr> {
156        vec![
157            match self {
158                Either::Left(left) => left.into_any_attr(),
159                Either::Right(right) => right.into_any_attr(),
160            },
161            new_attr.into_any_attr(),
162        ]
163    }
164}
165
166impl<A, B> Attribute for Either<A, B>
167where
168    B: Attribute,
169    A: Attribute,
170{
171    const MIN_LENGTH: usize = max_usize(&[A::MIN_LENGTH, B::MIN_LENGTH]);
172
173    type AsyncOutput = Either<A::AsyncOutput, B::AsyncOutput>;
174    type State = Either<A::State, B::State>;
175    type Cloneable = Either<A::Cloneable, B::Cloneable>;
176    type CloneableOwned = Either<A::CloneableOwned, B::CloneableOwned>;
177
178    fn html_len(&self) -> usize {
179        match self {
180            Either::Left(left) => left.html_len(),
181            Either::Right(right) => right.html_len(),
182        }
183    }
184
185    fn to_html(
186        self,
187        buf: &mut String,
188        class: &mut String,
189        style: &mut String,
190        inner_html: &mut String,
191    ) {
192        match self {
193            Either::Left(left) => left.to_html(buf, class, style, inner_html),
194            Either::Right(right) => {
195                right.to_html(buf, class, style, inner_html)
196            }
197        }
198    }
199
200    fn hydrate<const FROM_SERVER: bool>(
201        self,
202        el: &crate::renderer::types::Element,
203    ) -> Self::State {
204        match self {
205            Either::Left(left) => Either::Left(left.hydrate::<FROM_SERVER>(el)),
206            Either::Right(right) => {
207                Either::Right(right.hydrate::<FROM_SERVER>(el))
208            }
209        }
210    }
211
212    fn build(self, el: &crate::renderer::types::Element) -> Self::State {
213        match self {
214            Either::Left(left) => Either::Left(left.build(el)),
215            Either::Right(right) => Either::Right(right.build(el)),
216        }
217    }
218
219    fn rebuild(self, state: &mut Self::State) {
220        match self {
221            Either::Left(left) => {
222                if let Some(state) = state.as_left_mut() {
223                    left.rebuild(state)
224                }
225            }
226            Either::Right(right) => {
227                if let Some(state) = state.as_right_mut() {
228                    right.rebuild(state)
229                }
230            }
231        }
232    }
233
234    fn into_cloneable(self) -> Self::Cloneable {
235        match self {
236            Either::Left(left) => Either::Left(left.into_cloneable()),
237            Either::Right(right) => Either::Right(right.into_cloneable()),
238        }
239    }
240
241    fn into_cloneable_owned(self) -> Self::CloneableOwned {
242        match self {
243            Either::Left(left) => Either::Left(left.into_cloneable_owned()),
244            Either::Right(right) => Either::Right(right.into_cloneable_owned()),
245        }
246    }
247
248    fn dry_resolve(&mut self) {
249        match self {
250            Either::Left(left) => left.dry_resolve(),
251            Either::Right(right) => right.dry_resolve(),
252        }
253    }
254
255    async fn resolve(self) -> Self::AsyncOutput {
256        match self {
257            Either::Left(left) => Either::Left(left.resolve().await),
258            Either::Right(right) => Either::Right(right.resolve().await),
259        }
260    }
261}
262
263impl<A, B> RenderHtml for Either<A, B>
264where
265    A: RenderHtml,
266    B: RenderHtml,
267{
268    type AsyncOutput = Either<A::AsyncOutput, B::AsyncOutput>;
269
270    fn dry_resolve(&mut self) {
271        match self {
272            Either::Left(left) => left.dry_resolve(),
273            Either::Right(right) => right.dry_resolve(),
274        }
275    }
276
277    async fn resolve(self) -> Self::AsyncOutput {
278        match self {
279            Either::Left(left) => Either::Left(left.resolve().await),
280            Either::Right(right) => Either::Right(right.resolve().await),
281        }
282    }
283
284    const MIN_LENGTH: usize = max_usize(&[A::MIN_LENGTH, B::MIN_LENGTH]);
285
286    #[inline(always)]
287    fn html_len(&self) -> usize {
288        match self {
289            Either::Left(i) => i.html_len(),
290            Either::Right(i) => i.html_len(),
291        }
292    }
293
294    fn to_html_with_buf(
295        self,
296        buf: &mut String,
297        position: &mut Position,
298        escape: bool,
299        mark_branches: bool,
300    ) {
301        match self {
302            Either::Left(left) => {
303                if mark_branches {
304                    buf.open_branch("0");
305                }
306                left.to_html_with_buf(buf, position, escape, mark_branches);
307                if mark_branches {
308                    buf.close_branch("0");
309                }
310            }
311            Either::Right(right) => {
312                if mark_branches {
313                    buf.open_branch("1");
314                }
315                right.to_html_with_buf(buf, position, escape, mark_branches);
316                if mark_branches {
317                    buf.close_branch("1");
318                }
319            }
320        }
321    }
322
323    fn to_html_async_with_buf<const OUT_OF_ORDER: bool>(
324        self,
325        buf: &mut StreamBuilder,
326        position: &mut Position,
327        escape: bool,
328        mark_branches: bool,
329    ) where
330        Self: Sized,
331    {
332        match self {
333            Either::Left(left) => {
334                if mark_branches {
335                    buf.open_branch("0");
336                }
337                left.to_html_async_with_buf::<OUT_OF_ORDER>(
338                    buf,
339                    position,
340                    escape,
341                    mark_branches,
342                );
343                if mark_branches {
344                    buf.close_branch("0");
345                }
346            }
347            Either::Right(right) => {
348                if mark_branches {
349                    buf.open_branch("1");
350                }
351                right.to_html_async_with_buf::<OUT_OF_ORDER>(
352                    buf,
353                    position,
354                    escape,
355                    mark_branches,
356                );
357                if mark_branches {
358                    buf.close_branch("1");
359                }
360            }
361        }
362    }
363
364    fn hydrate<const FROM_SERVER: bool>(
365        self,
366        cursor: &Cursor,
367        position: &PositionState,
368    ) -> Self::State {
369        match self {
370            Either::Left(left) => {
371                Either::Left(left.hydrate::<FROM_SERVER>(cursor, position))
372            }
373            Either::Right(right) => {
374                Either::Right(right.hydrate::<FROM_SERVER>(cursor, position))
375            }
376        }
377    }
378}
379
380/// Stores each value in the view state, overwriting it only if `Some(_)` is provided.
381pub struct EitherKeepAlive<A, B> {
382    /// The first possibility.
383    pub a: Option<A>,
384    /// The second possibility.
385    pub b: Option<B>,
386    /// If `true`, then `b` will be shown.
387    pub show_b: bool,
388}
389
390/// Retained view state for [`EitherKeepAlive`].
391pub struct EitherKeepAliveState<A, B> {
392    a: Option<A>,
393    b: Option<B>,
394    showing_b: bool,
395}
396
397impl<A, B> Render for EitherKeepAlive<A, B>
398where
399    A: Render,
400    B: Render,
401{
402    type State = EitherKeepAliveState<A::State, B::State>;
403
404    fn build(self) -> Self::State {
405        let showing_b = self.show_b;
406        let a = self.a.map(Render::build);
407        let b = self.b.map(Render::build);
408        EitherKeepAliveState { a, b, showing_b }
409    }
410
411    fn rebuild(self, state: &mut Self::State) {
412        // set or update A -- `None` just means "no change"
413        match (self.a, &mut state.a) {
414            (Some(new), Some(old)) => new.rebuild(old),
415            (Some(new), None) => state.a = Some(new.build()),
416            _ => {}
417        }
418
419        // set or update B
420        match (self.b, &mut state.b) {
421            (Some(new), Some(old)) => new.rebuild(old),
422            (Some(new), None) => state.b = Some(new.build()),
423            _ => {}
424        }
425
426        match (self.show_b, state.showing_b) {
427            // transition from A to B
428            (true, false) => match (&mut state.a, &mut state.b) {
429                (Some(a), Some(b)) => {
430                    a.insert_before_this(b);
431                    a.unmount();
432                }
433                _ => unreachable!(),
434            },
435            // transition from B to A
436            (false, true) => match (&mut state.a, &mut state.b) {
437                (Some(a), Some(b)) => {
438                    b.insert_before_this(a);
439                    b.unmount();
440                }
441                _ => unreachable!(),
442            },
443            _ => {}
444        }
445        state.showing_b = self.show_b;
446    }
447}
448
449impl<A, B> AddAnyAttr for EitherKeepAlive<A, B>
450where
451    A: RenderHtml,
452    B: RenderHtml,
453{
454    type Output<SomeNewAttr: Attribute> = EitherKeepAlive<
455        <A as AddAnyAttr>::Output<SomeNewAttr::Cloneable>,
456        <B as AddAnyAttr>::Output<SomeNewAttr::Cloneable>,
457    >;
458
459    fn add_any_attr<NewAttr: Attribute>(
460        self,
461        attr: NewAttr,
462    ) -> Self::Output<NewAttr>
463    where
464        Self::Output<NewAttr>: RenderHtml,
465    {
466        let EitherKeepAlive { a, b, show_b } = self;
467        let attr = attr.into_cloneable();
468        EitherKeepAlive {
469            a: a.map(|a| a.add_any_attr(attr.clone())),
470            b: b.map(|b| b.add_any_attr(attr.clone())),
471            show_b,
472        }
473    }
474}
475
476impl<A, B> RenderHtml for EitherKeepAlive<A, B>
477where
478    A: RenderHtml,
479    B: RenderHtml,
480{
481    type AsyncOutput = EitherKeepAlive<A::AsyncOutput, B::AsyncOutput>;
482
483    const MIN_LENGTH: usize = 0;
484
485    fn dry_resolve(&mut self) {
486        if let Some(inner) = &mut self.a {
487            inner.dry_resolve();
488        }
489        if let Some(inner) = &mut self.b {
490            inner.dry_resolve();
491        }
492    }
493
494    async fn resolve(self) -> Self::AsyncOutput {
495        let EitherKeepAlive { a, b, show_b } = self;
496        let (a, b) = join(
497            async move {
498                match a {
499                    Some(a) => Some(a.resolve().await),
500                    None => None,
501                }
502            },
503            async move {
504                match b {
505                    Some(b) => Some(b.resolve().await),
506                    None => None,
507                }
508            },
509        )
510        .await;
511        EitherKeepAlive { a, b, show_b }
512    }
513
514    fn to_html_with_buf(
515        self,
516        buf: &mut String,
517        position: &mut Position,
518        escape: bool,
519        mark_branches: bool,
520    ) {
521        if self.show_b {
522            self.b
523                .expect("rendering B to HTML without filling it")
524                .to_html_with_buf(buf, position, escape, mark_branches);
525        } else {
526            self.a
527                .expect("rendering A to HTML without filling it")
528                .to_html_with_buf(buf, position, escape, mark_branches);
529        }
530    }
531
532    fn to_html_async_with_buf<const OUT_OF_ORDER: bool>(
533        self,
534        buf: &mut StreamBuilder,
535        position: &mut Position,
536        escape: bool,
537        mark_branches: bool,
538    ) where
539        Self: Sized,
540    {
541        if self.show_b {
542            self.b
543                .expect("rendering B to HTML without filling it")
544                .to_html_async_with_buf::<OUT_OF_ORDER>(
545                    buf,
546                    position,
547                    escape,
548                    mark_branches,
549                );
550        } else {
551            self.a
552                .expect("rendering A to HTML without filling it")
553                .to_html_async_with_buf::<OUT_OF_ORDER>(
554                    buf,
555                    position,
556                    escape,
557                    mark_branches,
558                );
559        }
560    }
561
562    fn hydrate<const FROM_SERVER: bool>(
563        self,
564        cursor: &Cursor,
565        position: &PositionState,
566    ) -> Self::State {
567        let showing_b = self.show_b;
568        let a = self.a.map(|a| {
569            if showing_b {
570                a.build()
571            } else {
572                a.hydrate::<FROM_SERVER>(cursor, position)
573            }
574        });
575        let b = self.b.map(|b| {
576            if showing_b {
577                b.hydrate::<FROM_SERVER>(cursor, position)
578            } else {
579                b.build()
580            }
581        });
582
583        EitherKeepAliveState { showing_b, a, b }
584    }
585}
586
587impl<A, B> Mountable for EitherKeepAliveState<A, B>
588where
589    A: Mountable,
590    B: Mountable,
591{
592    fn unmount(&mut self) {
593        if self.showing_b {
594            self.b.as_mut().expect("B was not present").unmount();
595        } else {
596            self.a.as_mut().expect("A was not present").unmount();
597        }
598    }
599
600    fn mount(
601        &mut self,
602        parent: &crate::renderer::types::Element,
603        marker: Option<&crate::renderer::types::Node>,
604    ) {
605        if self.showing_b {
606            self.b
607                .as_mut()
608                .expect("B was not present")
609                .mount(parent, marker);
610        } else {
611            self.a
612                .as_mut()
613                .expect("A was not present")
614                .mount(parent, marker);
615        }
616    }
617
618    fn insert_before_this(&self, child: &mut dyn Mountable) -> bool {
619        if self.showing_b {
620            self.b
621                .as_ref()
622                .expect("B was not present")
623                .insert_before_this(child)
624        } else {
625            self.a
626                .as_ref()
627                .expect("A was not present")
628                .insert_before_this(child)
629        }
630    }
631}
632
633macro_rules! tuples {
634    ($num:literal => $($ty:ident),*) => {
635        paste::paste! {
636            #[doc = concat!("Retained view state for ", stringify!([<EitherOf $num>]), ".")]
637            pub struct [<EitherOf $num State>]<$($ty,)*>
638            where
639                $($ty: Render,)*
640
641            {
642                /// Which child view state is being displayed.
643                pub state: [<EitherOf $num>]<$($ty::State,)*>,
644            }
645
646            impl<$($ty,)*> Mountable for [<EitherOf $num State>]<$($ty,)*>
647            where
648                $($ty: Render,)*
649
650            {
651                fn unmount(&mut self) {
652                    match &mut self.state {
653                        $([<EitherOf $num>]::$ty(this) => [<EitherOf $num>]::$ty(this.unmount()),)*
654                    };
655                }
656
657                fn mount(
658                    &mut self,
659                    parent: &crate::renderer::types::Element,
660                    marker: Option<&crate::renderer::types::Node>,
661                ) {
662                    match &mut self.state {
663                        $([<EitherOf $num>]::$ty(this) => [<EitherOf $num>]::$ty(this.mount(parent, marker)),)*
664                    };
665                }
666
667                fn insert_before_this(&self,
668                    child: &mut dyn Mountable,
669                ) -> bool {
670                    match &self.state {
671                        $([<EitherOf $num>]::$ty(this) =>this.insert_before_this(child),)*
672                    }
673                }
674            }
675
676            impl<$($ty,)*> Render for [<EitherOf $num>]<$($ty,)*>
677            where
678                $($ty: Render,)*
679
680            {
681                type State = [<EitherOf $num State>]<$($ty,)*>;
682
683
684                fn build(self) -> Self::State {
685                    let state = match self {
686                        $([<EitherOf $num>]::$ty(this) => [<EitherOf $num>]::$ty(this.build()),)*
687                    };
688                    Self::State { state }
689                }
690
691                fn rebuild(self, state: &mut Self::State) {
692                    let new_state = match (self, &mut state.state) {
693                        // rebuild same state and return early
694                        $(([<EitherOf $num>]::$ty(new), [<EitherOf $num>]::$ty(old)) => { return new.rebuild(old); },)*
695                        // or mount new state
696                        $(([<EitherOf $num>]::$ty(new), _) => {
697                            let mut new = new.build();
698                            state.insert_before_this(&mut new);
699                            [<EitherOf $num>]::$ty(new)
700                        },)*
701                    };
702
703                    // and then unmount old state
704                    match &mut state.state {
705                        $([<EitherOf $num>]::$ty(this) => this.unmount(),)*
706                    };
707
708                    // and store the new state
709                    state.state = new_state;
710                }
711            }
712
713            impl<$($ty,)*> AddAnyAttr for [<EitherOf $num>]<$($ty,)*>
714            where
715                $($ty: RenderHtml,)*
716
717            {
718                type Output<SomeNewAttr: Attribute> = [<EitherOf $num>]<
719                    $(<$ty as AddAnyAttr>::Output<SomeNewAttr>,)*
720                >;
721
722                fn add_any_attr<NewAttr: Attribute>(
723                    self,
724                    attr: NewAttr,
725                ) -> Self::Output<NewAttr>
726                where
727                    Self::Output<NewAttr>: RenderHtml,
728                {
729                    match self {
730                        $([<EitherOf $num>]::$ty(this) => [<EitherOf $num>]::$ty(this.add_any_attr(attr)),)*
731                    }
732                }
733            }
734
735            impl<$($ty,)*> RenderHtml for [<EitherOf $num>]<$($ty,)*>
736            where
737                $($ty: RenderHtml,)*
738
739            {
740                type AsyncOutput = [<EitherOf $num>]<$($ty::AsyncOutput,)*>;
741
742                const MIN_LENGTH: usize = max_usize(&[$($ty ::MIN_LENGTH,)*]);
743
744
745                fn dry_resolve(&mut self) {
746                    match self {
747                        $([<EitherOf $num>]::$ty(this) => {
748                            this.dry_resolve();
749                        })*
750                    }
751                }
752
753                async fn resolve(self) -> Self::AsyncOutput {
754                    match self {
755                        $([<EitherOf $num>]::$ty(this) => [<EitherOf $num>]::$ty(this.resolve().await),)*
756                    }
757                }
758
759                #[inline(always)]
760                fn html_len(&self) -> usize {
761                    match self {
762                        $([<EitherOf $num>]::$ty(i) => i.html_len(),)*
763                    }
764                }
765
766                fn to_html_with_buf(self, buf: &mut String, position: &mut Position, escape: bool, mark_branches: bool) {
767                    match self {
768                        $([<EitherOf $num>]::$ty(this) => {
769                            if mark_branches {
770                                buf.open_branch(stringify!($ty));
771                            }
772                            this.to_html_with_buf(buf, position, escape, mark_branches);
773                            if mark_branches {
774                                buf.close_branch(stringify!($ty));
775                            }
776                        })*
777                    }
778                }
779
780                fn to_html_async_with_buf<const OUT_OF_ORDER: bool>(
781                    self,
782                    buf: &mut StreamBuilder, position: &mut Position, escape: bool, mark_branches: bool) where
783                    Self: Sized,
784                {
785                    match self {
786                        $([<EitherOf $num>]::$ty(this) => {
787                            if mark_branches {
788                                buf.open_branch(stringify!($ty));
789                            }
790                            this.to_html_async_with_buf::<OUT_OF_ORDER>(buf, position, escape, mark_branches);
791                            if mark_branches {
792                                buf.close_branch(stringify!($ty));
793                            }
794                        })*
795                    }
796                }
797
798                fn hydrate<const FROM_SERVER: bool>(
799                    self,
800                    cursor: &Cursor,
801                    position: &PositionState,
802                ) -> Self::State {
803                    let state = match self {
804                        $([<EitherOf $num>]::$ty(this) => {
805                            [<EitherOf $num>]::$ty(this.hydrate::<FROM_SERVER>(cursor, position))
806                        })*
807                    };
808
809                    Self::State { state }
810                }
811            }
812        }
813    }
814}
815
816tuples!(3 => A, B, C);
817tuples!(4 => A, B, C, D);
818tuples!(5 => A, B, C, D, E);
819tuples!(6 => A, B, C, D, E, F);
820tuples!(7 => A, B, C, D, E, F, G);
821tuples!(8 => A, B, C, D, E, F, G, H);
822tuples!(9 => A, B, C, D, E, F, G, H, I);
823tuples!(10 => A, B, C, D, E, F, G, H, I, J);
824tuples!(11 => A, B, C, D, E, F, G, H, I, J, K);
825tuples!(12 => A, B, C, D, E, F, G, H, I, J, K, L);
826tuples!(13 => A, B, C, D, E, F, G, H, I, J, K, L, M);
827tuples!(14 => A, B, C, D, E, F, G, H, I, J, K, L, M, N);
828tuples!(15 => A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
829tuples!(16 => A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);