Skip to main content

cotis_modules_debug/layout_module_debugger/
isolated_replay.rs

1//! Isolated layout-module replay without a full [`CotisApp`](cotis::cotis_app::CotisApp).
2//!
3//! [`LayoutModuleReplay`] runs a stored [`LeafedInterfaceDeclaration`] through a layout
4//! manager in a single frame, returning layout output directly. For replay inside a normal
5//! app loop, prefer [`UiFn`](cotis::cotis_app::UiFn) on [`&LeafedInterfaceDeclaration`](crate::serialize_targets::LeafedInterfaceDeclaration).
6
7use std::iter::Peekable;
8
9use cotis::element_configuring::{ConfigureElements, ConfiguredParentElement, OpenElement};
10use cotis::layout::LayoutFrameManager;
11use cotis::layout::{LayoutManager, LayoutManagerCompatible};
12
13use crate::serialize_targets::leaf_serialization::LeafConfigListCompatible;
14use crate::serialize_targets::{
15    LeafedInterfaceDeclaration, LeafedInterfaceInstructions, SerializableInterfaceInstructions,
16};
17
18/// Replays a stored UI declaration through a layout manager in one isolated frame.
19///
20/// Unlike the [`UiFn`](cotis::cotis_app::UiFn) path (stage 2 of the debug pipeline), this type
21/// does not require [`CotisApp`](cotis::cotis_app::CotisApp). It calls `init`, `prepare`, and
22/// a single `begin_frame` on the wrapped layout module, then interprets the declaration
23/// instructions to rebuild the element tree.
24pub struct LayoutModuleReplay<Module, ConfigType, List: Copy> {
25    internal: Module,
26    declaration: LeafedInterfaceDeclaration<ConfigType, List>,
27}
28
29impl<Module, ConfigType, List: Copy> LayoutModuleReplay<Module, ConfigType, List> {
30    /// Creates a replay runner from a layout module and a stored declaration.
31    pub fn new(
32        internal: Module,
33        declaration: LeafedInterfaceDeclaration<ConfigType, List>,
34    ) -> Self {
35        Self {
36            internal,
37            declaration,
38        }
39    }
40
41    /// Runs one isolated layout frame and returns the layout module's output.
42    ///
43    /// This initializes the layout module, replays all declaration instructions against the
44    /// frame root, closes the root element, and collects the frame's layout output.
45    ///
46    /// # Panics
47    ///
48    /// Panics if the stored instruction stream is invalid for replay:
49    ///
50    /// - `"Bad replay: cannot configure ConfiguredParentElement"` when a `Configure`
51    ///   instruction appears while configuring a parent scope.
52    /// - `"Bad replay: cannot configure ConfiguredParentElement with leaf config"` when a
53    ///   `LeafConfig` instruction appears while configuring a parent scope.
54    pub fn replay<'frame, RenderCommandType, Renderer>(
55        &mut self,
56        renderer: &mut Renderer,
57    ) -> Vec<RenderCommandType>
58    where
59        ConfigType: 'frame + Clone,
60        RenderCommandType: 'frame,
61        Module: LayoutManager<'frame, ConfigType, RenderCommandType>
62            + LayoutManagerCompatible<Renderer>,
63        for<'layout> List: LeafConfigListCompatible<Module::ElementConfigurer<'layout>, ConfigType>,
64    {
65        self.internal.init(renderer);
66        self.internal.prepare(renderer);
67        let mut frame = self.internal.begin_frame();
68        let mut root = frame.begin();
69        let mut iter = self.declaration.iter_over();
70
71        replay_instructions(&mut iter, &mut root);
72
73        root.close_element();
74        frame.end().collect()
75    }
76}
77
78/// Replays a declaration instruction stream into an existing layout root.
79pub(crate) fn replay_instructions<'a, ConfigType, Configurer, List>(
80    inst_iter: &mut impl Iterator<
81        Item = LeafedInterfaceInstructions<&'a SerializableInterfaceInstructions<ConfigType>, List>,
82    >,
83    layout: &mut ConfiguredParentElement<'_, Configurer, ConfigType>,
84) where
85    ConfigType: Clone + 'a,
86    Configurer: ConfigureElements<ConfigType>,
87    List: Copy + LeafConfigListCompatible<Configurer, ConfigType>,
88{
89    replay_instructions_recursive_parent(&mut inst_iter.peekable(), layout);
90}
91
92fn replay_instructions_recursive_parent<'a, ConfigType, Configurer, List>(
93    inst_iter: &mut Peekable<
94        impl Iterator<
95            Item = LeafedInterfaceInstructions<
96                &'a SerializableInterfaceInstructions<ConfigType>,
97                List,
98            >,
99        >,
100    >,
101    layout: &mut ConfiguredParentElement<'_, Configurer, ConfigType>,
102) where
103    ConfigType: Clone + 'a,
104    Configurer: ConfigureElements<ConfigType>,
105    List: Copy + LeafConfigListCompatible<Configurer, ConfigType>,
106{
107    while let Some(leafed) = inst_iter.next() {
108        match leafed.instruction {
109            SerializableInterfaceInstructions::Open => {
110                let element = layout.new_element();
111                replay_instructions_recursive_open(inst_iter, element);
112            }
113            SerializableInterfaceInstructions::Close => {
114                break;
115            }
116            SerializableInterfaceInstructions::Configure(_) => {
117                panic!("Bad replay: cannot configure ConfiguredParentElement");
118            }
119            SerializableInterfaceInstructions::LeafConfig(_) => {
120                panic!("Bad replay: cannot configure ConfiguredParentElement with leaf config");
121            }
122            _ => {}
123        }
124    }
125}
126
127fn replay_instructions_recursive_open<'a, ConfigType, Configurer, List>(
128    inst_iter: &mut Peekable<
129        impl Iterator<
130            Item = LeafedInterfaceInstructions<
131                &'a SerializableInterfaceInstructions<ConfigType>,
132                List,
133            >,
134        >,
135    >,
136    layout: OpenElement<'_, Configurer, ConfigType>,
137) where
138    ConfigType: Clone + 'a,
139    Configurer: ConfigureElements<ConfigType>,
140    List: Copy + LeafConfigListCompatible<Configurer, ConfigType>,
141{
142    let mut layout_opt = Some(layout);
143    let mut parent_opt: Option<ConfiguredParentElement<'_, Configurer, ConfigType>> = None;
144
145    while let Some(leafed) = inst_iter.next() {
146        match leafed.instruction {
147            SerializableInterfaceInstructions::Configure(c) => {
148                if let Some(ref mut open) = layout_opt {
149                    open.set_config(c.clone());
150                }
151            }
152            SerializableInterfaceInstructions::LeafConfig(data) => {
153                if let Some(open) = layout_opt.take() {
154                    List::configure_open(data, open);
155                }
156            }
157            SerializableInterfaceInstructions::Open => {
158                if parent_opt.is_none()
159                    && let Some(open) = layout_opt.take()
160                {
161                    parent_opt = Some(open.make_parent());
162                }
163                if let Some(ref mut parent) = parent_opt {
164                    let child_element = parent.new_element();
165                    replay_instructions_recursive_open(inst_iter, child_element);
166                }
167            }
168            SerializableInterfaceInstructions::Close => {
169                if let Some(parent) = parent_opt.take() {
170                    parent.close_element();
171                } else if let Some(open) = layout_opt.take() {
172                    open.close_element();
173                }
174                break;
175            }
176            _ => {}
177        }
178    }
179}