1use std::any::Any;
12
13use crate::widget::Widget;
14
15#[derive(Clone, Copy, PartialEq, Eq, Debug)]
20pub struct ChangeFlags(u8);
21
22impl ChangeFlags {
23 pub const NONE: Self = Self(0);
25 pub const PAINT: Self = Self(0b0000_0001);
27 pub const LAYOUT: Self = Self(0b0000_0010);
29
30 pub const fn contains(self, other: Self) -> bool {
32 (self.0 & other.0) == other.0
33 }
34
35 pub const fn union(self, other: Self) -> Self {
37 Self(self.0 | other.0)
38 }
39
40 pub const fn is_empty(self) -> bool {
42 self.0 == 0
43 }
44
45 pub const fn needs_paint(self) -> bool {
47 !self.is_empty()
48 }
49
50 pub const fn needs_layout(self) -> bool {
52 self.contains(Self::LAYOUT)
53 }
54}
55
56impl core::ops::BitOr for ChangeFlags {
57 type Output = Self;
58 fn bitor(self, rhs: Self) -> Self {
59 self.union(rhs)
60 }
61}
62
63impl core::ops::BitOrAssign for ChangeFlags {
64 fn bitor_assign(&mut self, rhs: Self) {
65 self.0 |= rhs.0;
66 }
67}
68
69impl Default for ChangeFlags {
70 fn default() -> Self {
71 Self::NONE
72 }
73}
74
75#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
77pub struct WidgetId(pub u64);
78
79impl From<WidgetId> for u64 {
80 fn from(id: WidgetId) -> Self {
81 id.0
82 }
83}
84
85pub struct BuildCtx<'a> {
90 next_id: &'a mut u64,
91 has_focus: bool,
92 element_replaced: bool,
93 hints: Option<std::rc::Rc<[crate::shortcuts::ShortcutHint]>>,
95}
96
97impl<'a> BuildCtx<'a> {
98 pub fn new(next_id: &'a mut u64) -> Self {
104 Self {
105 next_id,
106 has_focus: true,
107 element_replaced: false,
108 hints: None,
109 }
110 }
111
112 pub fn with_shortcut_hints(
114 mut self,
115 hints: std::rc::Rc<[crate::shortcuts::ShortcutHint]>,
116 ) -> Self {
117 self.hints = Some(hints);
118 self
119 }
120
121 pub fn alloc_id(&mut self) -> WidgetId {
123 *self.next_id += 1;
124 WidgetId(*self.next_id)
125 }
126
127 pub fn has_focus(&self) -> bool {
136 self.has_focus
137 }
138
139 pub fn shortcut_hints(&self) -> std::rc::Rc<[crate::shortcuts::ShortcutHint]> {
143 self.hints
144 .clone()
145 .unwrap_or_else(|| std::rc::Rc::from(Vec::new()))
146 }
147
148 pub fn set_has_focus(&mut self, has_focus: bool) {
152 self.has_focus = has_focus;
153 }
154
155 pub fn with_focus_link<R>(&mut self, link_focused: bool, f: impl FnOnce(&mut Self) -> R) -> R {
159 let outer = self.has_focus;
160 self.has_focus = outer && link_focused;
161 let result = f(self);
162 self.has_focus = outer;
163 result
164 }
165
166 pub fn mark_element_replaced(&mut self) {
176 self.element_replaced = true;
177 }
178
179 pub fn track_element_replaced<R>(&mut self, f: impl FnOnce(&mut Self) -> R) -> (R, bool) {
183 let outer = std::mem::replace(&mut self.element_replaced, false);
184 let result = f(self);
185 let replaced = std::mem::replace(&mut self.element_replaced, outer);
186 (result, replaced)
187 }
188}
189
190pub trait View<State: 'static>: 'static {
196 type Element: Widget;
198
199 fn build(&self, ctx: &mut BuildCtx<'_>) -> Self::Element;
201
202 fn rebuild(
204 &self,
205 prev: &Self,
206 element: &mut Self::Element,
207 ctx: &mut BuildCtx<'_>,
208 ) -> ChangeFlags;
209
210 fn teardown(&self, _element: &mut Self::Element, _ctx: &mut BuildCtx<'_>) {}
212
213 fn message(&self, _element: &mut Self::Element, _state: &mut State) {}
216}
217
218trait ErasedView<State: 'static>: 'static {
220 fn dyn_build(&self, ctx: &mut BuildCtx<'_>) -> Box<dyn Widget>;
221
222 fn dyn_rebuild(
225 &self,
226 prev: &dyn ErasedView<State>,
227 element: &mut Box<dyn Widget>,
228 ctx: &mut BuildCtx<'_>,
229 ) -> ChangeFlags;
230
231 fn dyn_teardown(&self, element: &mut Box<dyn Widget>, ctx: &mut BuildCtx<'_>);
232
233 fn as_any(&self) -> &dyn Any;
234}
235
236impl<State: 'static, V: View<State>> ErasedView<State> for V {
237 fn dyn_build(&self, ctx: &mut BuildCtx<'_>) -> Box<dyn Widget> {
238 Box::new(self.build(ctx))
239 }
240
241 fn dyn_rebuild(
242 &self,
243 prev: &dyn ErasedView<State>,
244 element: &mut Box<dyn Widget>,
245 ctx: &mut BuildCtx<'_>,
246 ) -> ChangeFlags {
247 if let Some(prev) = prev.as_any().downcast_ref::<V>() {
248 let element = (**element)
249 .downcast_mut::<V::Element>()
250 .expect("erased element type matches its originating view");
251 self.rebuild(prev, element, ctx)
252 } else {
253 prev.dyn_teardown(element, ctx);
254 *element = self.dyn_build(ctx);
255 ChangeFlags::LAYOUT | ChangeFlags::PAINT
256 }
257 }
258
259 fn dyn_teardown(&self, element: &mut Box<dyn Widget>, ctx: &mut BuildCtx<'_>) {
260 if let Some(element) = (**element).downcast_mut::<V::Element>() {
261 self.teardown(element, ctx);
262 }
263 }
264
265 fn as_any(&self) -> &dyn Any {
266 self
267 }
268}
269
270pub struct AnyView<State: 'static> {
281 inner: Box<dyn ErasedView<State>>,
282}
283
284impl<State: 'static> AnyView<State> {
285 pub fn new<V: View<State>>(view: V) -> Self {
288 let mut slot = Some(view);
289 if let Some(erased) =
290 (&mut slot as &mut dyn core::any::Any).downcast_mut::<Option<AnyView<State>>>()
291 {
292 return erased.take().expect("slot is Some");
293 }
294 Self {
295 inner: Box::new(slot.expect("slot is Some")),
296 }
297 }
298}
299
300pub fn any<State: 'static, V: View<State>>(view: V) -> AnyView<State> {
307 AnyView::new(view)
308}
309
310impl<State: 'static> View<State> for AnyView<State> {
311 type Element = Box<dyn Widget>;
312
313 fn build(&self, ctx: &mut BuildCtx<'_>) -> Self::Element {
314 self.inner.dyn_build(ctx)
315 }
316
317 fn rebuild(
318 &self,
319 prev: &Self,
320 element: &mut Self::Element,
321 ctx: &mut BuildCtx<'_>,
322 ) -> ChangeFlags {
323 self.inner.dyn_rebuild(prev.inner.as_ref(), element, ctx)
324 }
325
326 fn teardown(&self, element: &mut Self::Element, ctx: &mut BuildCtx<'_>) {
327 self.inner.dyn_teardown(element, ctx);
328 }
329}
330
331#[diagnostic::on_unimplemented(
343 message = "this is not a child sequence",
344 note = "a child list is a View, a tuple of up to 12 views (nest tuples beyond 12), an array, a Vec, an Option, or views(iter) for an iterator"
345)]
346pub trait ViewSeq<State: 'static, M> {
347 fn extend_views(self, out: &mut Vec<AnyView<State>>);
349}
350
351pub mod seq_marker {
354 use core::marker::PhantomData;
355
356 pub enum One {}
357
358 pub struct VecOf<M>(PhantomData<fn() -> M>);
359
360 pub struct ArrayOf<M>(PhantomData<fn() -> M>);
361
362 pub struct OptionOf<M>(PhantomData<fn() -> M>);
363
364 pub enum Iter {}
365}
366
367impl<State: 'static, V: View<State>> ViewSeq<State, seq_marker::One> for V {
368 fn extend_views(self, out: &mut Vec<AnyView<State>>) {
369 out.push(AnyView::new(self));
370 }
371}
372
373impl<State: 'static, M, S: ViewSeq<State, M>> ViewSeq<State, seq_marker::VecOf<M>> for Vec<S> {
374 fn extend_views(self, out: &mut Vec<AnyView<State>>) {
375 out.reserve(self.len());
376 for seq in self {
377 seq.extend_views(out);
378 }
379 }
380}
381
382impl<State: 'static, M, S: ViewSeq<State, M>, const N: usize> ViewSeq<State, seq_marker::ArrayOf<M>>
383 for [S; N]
384{
385 fn extend_views(self, out: &mut Vec<AnyView<State>>) {
386 out.reserve(N);
387 for seq in self {
388 seq.extend_views(out);
389 }
390 }
391}
392
393impl<State: 'static, M, S: ViewSeq<State, M>> ViewSeq<State, seq_marker::OptionOf<M>>
394 for Option<S>
395{
396 fn extend_views(self, out: &mut Vec<AnyView<State>>) {
397 if let Some(seq) = self {
398 seq.extend_views(out);
399 }
400 }
401}
402
403macro_rules! impl_view_seq_for_tuples {
405 ($( ($($S:ident $M:ident $idx:tt),+) )+) => {$(
406 impl<State: 'static, $($M, $S: ViewSeq<State, $M>),+> ViewSeq<State, ($($M,)+)>
407 for ($($S,)+)
408 {
409 fn extend_views(self, out: &mut Vec<AnyView<State>>) {
410 $(self.$idx.extend_views(out);)+
411 }
412 }
413 )+};
414}
415
416impl_view_seq_for_tuples! {
417 (S0 M0 0)
418 (S0 M0 0, S1 M1 1)
419 (S0 M0 0, S1 M1 1, S2 M2 2)
420 (S0 M0 0, S1 M1 1, S2 M2 2, S3 M3 3)
421 (S0 M0 0, S1 M1 1, S2 M2 2, S3 M3 3, S4 M4 4)
422 (S0 M0 0, S1 M1 1, S2 M2 2, S3 M3 3, S4 M4 4, S5 M5 5)
423 (S0 M0 0, S1 M1 1, S2 M2 2, S3 M3 3, S4 M4 4, S5 M5 5, S6 M6 6)
424 (S0 M0 0, S1 M1 1, S2 M2 2, S3 M3 3, S4 M4 4, S5 M5 5, S6 M6 6, S7 M7 7)
425 (S0 M0 0, S1 M1 1, S2 M2 2, S3 M3 3, S4 M4 4, S5 M5 5, S6 M6 6, S7 M7 7, S8 M8 8)
426 (S0 M0 0, S1 M1 1, S2 M2 2, S3 M3 3, S4 M4 4, S5 M5 5, S6 M6 6, S7 M7 7, S8 M8 8,
427 S9 M9 9)
428 (S0 M0 0, S1 M1 1, S2 M2 2, S3 M3 3, S4 M4 4, S5 M5 5, S6 M6 6, S7 M7 7, S8 M8 8,
429 S9 M9 9, S10 M10 10)
430 (S0 M0 0, S1 M1 1, S2 M2 2, S3 M3 3, S4 M4 4, S5 M5 5, S6 M6 6, S7 M7 7, S8 M8 8,
431 S9 M9 9, S10 M10 10, S11 M11 11)
432}
433
434pub struct Views<I>(I);
436
437impl<State: 'static, I> ViewSeq<State, seq_marker::Iter> for Views<I>
438where
439 I: IntoIterator,
440 I::Item: View<State>,
441{
442 fn extend_views(self, out: &mut Vec<AnyView<State>>) {
443 out.extend(self.0.into_iter().map(AnyView::new));
444 }
445}
446
447pub fn views<State: 'static, I>(iter: I) -> Views<I>
450where
451 I: IntoIterator,
452 I::Item: View<State>,
453{
454 Views(iter)
455}
456
457#[cfg(test)]
458mod tests {
459 use super::*;
460 use crate::layout::BoxConstraints;
461 use crate::widget::{LayoutCtx, PaintCtx, PaintScene, Widget};
462 use kurbo::Size;
463 use std::cell::Cell;
464 use std::rc::Rc;
465
466 struct WidgetA {
469 n: u32,
470 }
471 impl Widget for WidgetA {
472 fn layout(&mut self, _ctx: &mut LayoutCtx, _bc: &BoxConstraints) -> Size {
473 Size::new(self.n as f64, 1.0)
474 }
475 fn paint(&mut self, _ctx: &mut PaintCtx, _scene: &mut dyn PaintScene) {}
476 }
477
478 struct WidgetB;
479 impl Widget for WidgetB {
480 fn layout(&mut self, _ctx: &mut LayoutCtx, _bc: &BoxConstraints) -> Size {
481 Size::new(99.0, 99.0)
482 }
483 fn paint(&mut self, _ctx: &mut PaintCtx, _scene: &mut dyn PaintScene) {}
484 }
485
486 struct ViewA {
487 n: u32,
488 torn: Rc<Cell<u32>>,
489 }
490 impl View<()> for ViewA {
491 type Element = WidgetA;
492 fn build(&self, _ctx: &mut BuildCtx<'_>) -> WidgetA {
493 WidgetA { n: self.n }
494 }
495 fn rebuild(
496 &self,
497 prev: &Self,
498 element: &mut WidgetA,
499 _ctx: &mut BuildCtx<'_>,
500 ) -> ChangeFlags {
501 if prev.n != self.n {
502 element.n = self.n;
503 ChangeFlags::PAINT
504 } else {
505 ChangeFlags::NONE
506 }
507 }
508 fn teardown(&self, _element: &mut WidgetA, _ctx: &mut BuildCtx<'_>) {
509 self.torn.set(self.torn.get() + 1);
510 }
511 }
512
513 struct ViewB;
514 impl View<()> for ViewB {
515 type Element = WidgetB;
516 fn build(&self, _ctx: &mut BuildCtx<'_>) -> WidgetB {
517 WidgetB
518 }
519 fn rebuild(
520 &self,
521 _prev: &Self,
522 _element: &mut WidgetB,
523 _ctx: &mut BuildCtx<'_>,
524 ) -> ChangeFlags {
525 ChangeFlags::NONE
526 }
527 }
528
529 #[test]
530 fn any_view_same_type_rebuilds_in_place() {
531 let torn = Rc::new(Cell::new(0));
532 let mut counter = 0u64;
533 let mut ctx = BuildCtx::new(&mut counter);
534
535 let prev = any(ViewA {
536 n: 1,
537 torn: torn.clone(),
538 });
539 let mut element = prev.build(&mut ctx);
540
541 let next = any(ViewA {
542 n: 2,
543 torn: torn.clone(),
544 });
545 let flags = next.rebuild(&prev, &mut element, &mut ctx);
546
547 assert_eq!(flags, ChangeFlags::PAINT);
549 assert_eq!(torn.get(), 0);
550 let a = (*element)
551 .downcast_mut::<WidgetA>()
552 .expect("still a WidgetA");
553 assert_eq!(a.n, 2);
554 }
555
556 #[test]
557 fn any_view_type_swap_tears_down_and_replaces() {
558 let torn = Rc::new(Cell::new(0));
559 let mut counter = 0u64;
560 let mut ctx = BuildCtx::new(&mut counter);
561
562 let prev = any(ViewA {
563 n: 7,
564 torn: torn.clone(),
565 });
566 let mut element = prev.build(&mut ctx);
567
568 let next = any(ViewB);
569 let flags = next.rebuild(&prev, &mut element, &mut ctx);
570
571 assert_eq!(flags, ChangeFlags::LAYOUT | ChangeFlags::PAINT);
574 assert_eq!(torn.get(), 1, "old view should be torn down exactly once");
575 assert!((*element).downcast_mut::<WidgetB>().is_some());
576 assert!((*element).downcast_mut::<WidgetA>().is_none());
577 }
578
579 #[test]
580 fn any_view_element_works_inside_a_child_pod() {
581 use crate::widget::ChildPod;
584
585 let torn = Rc::new(Cell::new(0));
586 let mut counter = 0u64;
587 let mut ctx = BuildCtx::new(&mut counter);
588
589 let view = any(ViewA { n: 5, torn });
590 let element: Box<dyn Widget> = view.build(&mut ctx); let mut pod = ChildPod::new(Box::new(element));
595 let mut lctx = LayoutCtx::new();
596 let size = pod.layout_child(&mut lctx, &BoxConstraints::loose(Size::new(100.0, 100.0)));
597 assert_eq!(size, Size::new(5.0, 1.0));
598 }
599
600 #[test]
601 fn change_flags_union_and_contains() {
602 let both = ChangeFlags::PAINT | ChangeFlags::LAYOUT;
603 assert!(both.contains(ChangeFlags::PAINT));
604 assert!(both.contains(ChangeFlags::LAYOUT));
605 assert!(both.needs_layout());
606 assert!(both.needs_paint());
607
608 assert!(ChangeFlags::NONE.is_empty());
609 assert!(!ChangeFlags::NONE.needs_paint());
610
611 let paint_only = ChangeFlags::PAINT;
612 assert!(paint_only.needs_paint());
613 assert!(!paint_only.needs_layout());
614 }
615
616 #[test]
617 fn change_flags_bitor_assign() {
618 let mut f = ChangeFlags::NONE;
619 f |= ChangeFlags::PAINT;
620 assert!(f.contains(ChangeFlags::PAINT));
621 assert!(!f.contains(ChangeFlags::LAYOUT));
622 }
623
624 #[test]
625 fn double_erasure_is_idempotent() {
626 let torn = Rc::new(Cell::new(0));
630 let mut counter = 0u64;
631 let mut ctx = BuildCtx::new(&mut counter);
632
633 let double_erased = any(any(ViewA {
634 n: 5,
635 torn: torn.clone(),
636 }));
637 let mut element = double_erased.build(&mut ctx);
638
639 let a = (*element)
642 .downcast_mut::<WidgetA>()
643 .expect("double-erased any(any(ViewA)) should have WidgetA element directly");
644 assert_eq!(a.n, 5);
645
646 let double_erased_2 = any(any(ViewA {
648 n: 10,
649 torn: torn.clone(),
650 }));
651 let flags = double_erased_2.rebuild(&double_erased, &mut element, &mut ctx);
652
653 assert_eq!(flags, ChangeFlags::PAINT);
655 assert_eq!(torn.get(), 0, "no teardown on same-type rebuild");
656 let a = (*element)
657 .downcast_mut::<WidgetA>()
658 .expect("still a WidgetA after rebuild");
659 assert_eq!(a.n, 10);
660 }
661
662 #[test]
663 fn build_ctx_allocates_unique_ids() {
664 let mut counter = 0;
665 let mut ctx = BuildCtx::new(&mut counter);
666 let a = ctx.alloc_id();
667 let b = ctx.alloc_id();
668 assert_ne!(a, b);
669 assert_eq!(u64::from(a), 1);
670 assert_eq!(u64::from(b), 2);
671 }
672
673 #[test]
674 fn build_ctx_focus_chain_only_narrows_and_restores() {
675 let mut counter = 0;
676 let mut ctx = BuildCtx::new(&mut counter);
677 assert!(
678 ctx.has_focus(),
679 "an unseeded context assumes a live chain (see BuildCtx::new)"
680 );
681
682 ctx.with_focus_link(true, |ctx| assert!(ctx.has_focus()));
684 assert!(ctx.has_focus());
686
687 ctx.with_focus_link(false, |ctx| {
691 assert!(!ctx.has_focus());
692 ctx.with_focus_link(true, |ctx| {
693 assert!(
694 !ctx.has_focus(),
695 "no descent may widen the chain a cleared ancestor closed"
696 );
697 });
698 assert!(!ctx.has_focus());
699 });
700 assert!(
701 ctx.has_focus(),
702 "the outer chain is restored, not clobbered"
703 );
704
705 ctx.set_has_focus(false);
707 assert!(!ctx.has_focus());
708 ctx.with_focus_link(true, |ctx| assert!(!ctx.has_focus()));
709 }
710
711 #[test]
712 fn build_ctx_element_replaced_is_consumed_by_the_nearest_scope() {
713 let mut counter = 0;
714 let mut ctx = BuildCtx::new(&mut counter);
715
716 let ((), replaced) = ctx.track_element_replaced(|_| {});
718 assert!(!replaced);
719
720 let (n, replaced) = ctx.track_element_replaced(|ctx| {
722 ctx.mark_element_replaced();
723 7
724 });
725 assert_eq!(n, 7);
726 assert!(replaced);
727
728 let (inner, outer) = ctx.track_element_replaced(|ctx| {
731 let ((), inner) = ctx.track_element_replaced(|ctx| ctx.mark_element_replaced());
732 inner
733 });
734 assert!(inner, "the inner scope consumes its own signal");
735 assert!(!outer, "a consumed signal does not reach the outer scope");
736
737 ctx.mark_element_replaced();
740 let ((), sibling) = ctx.track_element_replaced(|_| {});
741 assert!(!sibling, "a scope sees only signals raised inside it");
742 let ((), outer) = ctx.track_element_replaced(|ctx| {
743 ctx.mark_element_replaced();
744 });
745 assert!(outer);
746 let ((), _) = ctx.track_element_replaced(|_| {});
748 assert!(
749 std::mem::take(&mut ctx.element_replaced),
750 "the caller's pending signal is restored, not clobbered"
751 );
752 }
753
754 fn seq<M>(children: impl ViewSeq<(), M>) -> Vec<AnyView<()>> {
757 let mut out = Vec::new();
758 children.extend_views(&mut out);
759 out
760 }
761
762 fn a(n: u32) -> ViewA {
763 ViewA {
764 n,
765 torn: Rc::new(Cell::new(0)),
766 }
767 }
768
769 fn tags(children: &[AnyView<()>]) -> Vec<u32> {
773 let mut counter = 0u64;
774 let mut ctx = BuildCtx::new(&mut counter);
775 children
776 .iter()
777 .map(|child| {
778 let mut element = child.build(&mut ctx);
779 if let Some(a) = (*element).downcast_mut::<WidgetA>() {
780 a.n
781 } else {
782 (*element)
783 .downcast_mut::<WidgetB>()
784 .expect("element is the concrete widget, not a re-boxed one");
785 99
786 }
787 })
788 .collect()
789 }
790
791 #[test]
792 fn view_seq_single_view_is_one_element() {
793 assert_eq!(tags(&seq(a(4))), vec![4]);
794 }
795
796 #[test]
797 fn view_seq_tuple_of_mixed_views_keeps_order() {
798 let children = seq((a(1), ViewB, a(3)));
799 assert_eq!(children.len(), 3);
800 assert_eq!(tags(&children), vec![1, 99, 3]);
801 }
802
803 #[test]
804 fn view_seq_nested_tuples_flatten_in_order() {
805 let children = seq(((a(1), (ViewB, a(2))), a(3), (a(4),)));
806 assert_eq!(tags(&children), vec![1, 99, 2, 3, 4]);
807 }
808
809 #[test]
810 fn view_seq_vec_array_and_option_counts() {
811 assert_eq!(tags(&seq(vec![a(1), a(2)])), vec![1, 2]);
812 assert!(seq(Vec::<ViewB>::new()).is_empty());
813 assert_eq!(tags(&seq([a(5), a(6), a(7)])), vec![5, 6, 7]);
814 assert_eq!(tags(&seq(Some(a(8)))), vec![8]);
815 assert!(seq(None::<ViewA>).is_empty());
816 assert_eq!(tags(&seq((None::<ViewA>, ViewB))), vec![99]);
818 assert_eq!(
820 tags(&seq(vec![(a(1), ViewB), (a(2), ViewB)])),
821 vec![1, 99, 2, 99]
822 );
823 }
824
825 #[test]
826 fn view_seq_does_not_rebox_an_any_view_element() {
827 let children = seq((any(a(1)), any(any(ViewB)), vec![any(a(2))]));
830 assert_eq!(tags(&children), vec![1, 99, 2]);
831 }
832
833 #[test]
834 fn view_seq_views_adapter_maps_an_iterator() {
835 let children = seq(views((1..=3).map(a)));
836 assert_eq!(tags(&children), vec![1, 2, 3]);
837 let children = seq((ViewB, views([4, 5].into_iter().map(a))));
839 assert_eq!(tags(&children), vec![99, 4, 5]);
840 }
841
842 #[test]
843 fn view_seq_twelve_tuple_compiles_and_nesting_goes_beyond() {
844 let twelve = (
845 a(1),
846 a(2),
847 a(3),
848 a(4),
849 a(5),
850 a(6),
851 a(7),
852 a(8),
853 a(9),
854 a(10),
855 a(11),
856 a(12),
857 );
858 assert_eq!(tags(&seq(twelve)), (1..=12).collect::<Vec<_>>());
859 let thirteen = (
861 (
862 a(1),
863 a(2),
864 a(3),
865 a(4),
866 a(5),
867 a(6),
868 a(7),
869 a(8),
870 a(9),
871 a(10),
872 a(11),
873 a(12),
874 ),
875 a(13),
876 );
877 assert_eq!(tags(&seq(thirteen)), (1..=13).collect::<Vec<_>>());
878 }
879}