Skip to main content

pax_designtime/orm/
mod.rs

1//! # PaxManifestORM API
2//!
3//! `PaxManifestORM` provides an interface for managing `PaxManifest` objects, allowing for easy management of template nodes, selectors, and handlers.
4//!
5//! ## Main Functions
6//!
7//! - `build_new_node`: Create a new node builder instance. This method initializes a `NodeBuilder` for creating a new template node.
8//! - `get_node`: Retrieve an existing node. This method returns a `NodeBuilder` initialized with an existing node's data.
9//! - `remove_node`: Remove a specified node from the manifest.
10//! - `build_new_selector`: Create a new selector builder instance. This method initializes a `SelectorBuilder` for creating a new selector.
11//! - `get_selector`: Retrieve an existing selector. This method returns a `SelectorBuilder` initialized with an existing selector's data.
12//! - `remove_selector`: Remove a specified selector from the manifest.
13//! - `build_new_handler`: Create a new handler builder instance. This method initializes a `HandlerBuilder` for creating a new handler.
14//! - `get_handler`: Retrieve an existing handler. This method returns a `HandlerBuilder` initialized with an existing handler's data.
15//! - `remove_handler`: Remove a specified handler from the manifest.
16//! - `execute_command`: Execute a command that implements the `Command` trait, allowing for actions like adding, updating, or removing nodes, selectors, and handlers.
17//! - `undo`: Undo the last command. This method rolls back the last change made to the manifest.
18//! - `redo`: Redo the last undone command. This method reapplies the last change that was undone.
19//! - `undo_until`: Undo commands up to a specified command ID. This allows for targeted rollback of multiple changes.
20//!
21//! For usage examples see the tests in `pax-designtime/src/orm/tests.rs`.
22
23use std::collections::{BTreeMap, HashMap, HashSet};
24
25use pax_manifest::code_serialization::{diff_html, press_code_serialization_template};
26use pax_manifest::pax_runtime_api::{Interpolatable, Property};
27use pax_manifest::{
28    ComponentDefinition, ComponentTemplate, NodeLocation, PaxManifest, PropertyDefinition,
29    SettingElement, SettingsBlockElement, TemplateNodeDefinition, TemplateNodeId,
30    TimelineDefinition, TypeId, UniqueTemplateNodeIdentifier, ValueDefinition,
31};
32use serde_derive::{Deserialize, Serialize};
33#[allow(unused_imports)]
34use serde_json;
35
36use self::manifest_modification_data::ManifestModificationData;
37use self::template::class_builder::ClassBuilder;
38use self::template::{
39    node_builder::NodeBuilder, ConvertToComponentRequest, RemoveTemplateNodeRequest,
40};
41use self::template::{GetChildrenRequest, MoveTemplateNodeRequest, PasteSubTreeRequest};
42
43use anyhow::{anyhow, Result};
44pub mod manifest_modification_data;
45pub mod template;
46#[cfg(test)]
47mod tests;
48
49pub trait Request {
50    type Response: Response;
51}
52
53pub trait Response {
54    fn set_id(&mut self, id: usize);
55    fn get_id(&self) -> usize;
56    fn get_reload_type(&self) -> Option<ReloadType> {
57        None
58    }
59}
60
61pub trait Command<R: Request> {
62    fn execute(&mut self, manifest: &mut PaxManifest) -> Result<R::Response, String>;
63    fn as_undo_redo(&mut self) -> Option<UndoRedoCommand> {
64        None
65    }
66}
67
68#[derive(Serialize, Deserialize)]
69pub struct PaxManifestORM {
70    manifest: PaxManifest,
71    /// Stable identity of the Rust/component ABI compiled into this cartridge.
72    /// Server manifests may update templates, but they must not replace this
73    /// manifest with one that requires a different static descriptor registry.
74    #[serde(default)]
75    cartridge_abi_identity: Vec<u8>,
76    undo_stack: Vec<(usize, UndoRedoCommand)>,
77    redo_stack: Vec<(usize, UndoRedoCommand)>,
78    next_command_id: usize,
79    // This counter increase with each command execution/undo/redo (essentially tracks each unique change to the manifest)
80    manifest_version: Property<usize>,
81    manifest_modification_data: ManifestModificationData,
82    next_new_component_id: usize,
83    new_components: Vec<TypeId>,
84    reload_queue: HashSet<ReloadType>,
85    pub manifest_loaded_from_server: Property<bool>,
86    pub llm_messages: HashMap<u64, Vec<String>>,
87    pub new_message: Property<Vec<MessageType>>,
88    pub last_serialized_version: HashMap<TypeId, ComponentDefinition>,
89}
90
91impl PaxManifestORM {
92    pub fn new(manifest: PaxManifest) -> Self {
93        let cartridge_abi_identity = runtime_abi_identity(&manifest)
94            .expect("the embedded cartridge manifest should always be serializable");
95        let mut last_serialized_version = HashMap::new();
96        for component in manifest.components.values() {
97            last_serialized_version.insert(component.type_id.clone(), component.clone());
98        }
99
100        PaxManifestORM {
101            manifest,
102            cartridge_abi_identity,
103            undo_stack: Vec::new(),
104            redo_stack: Vec::new(),
105            next_command_id: 0,
106            manifest_version: Property::new(0),
107            manifest_modification_data: Default::default(),
108            next_new_component_id: 1,
109            new_components: Vec::new(),
110            reload_queue: HashSet::new(),
111            manifest_loaded_from_server: Property::new(false),
112            llm_messages: HashMap::new(),
113            new_message: Property::new(vec![]),
114            last_serialized_version,
115        }
116    }
117
118    pub fn add_new_message(
119        &mut self,
120        request_id: u64,
121        message: String,
122        component: Option<ComponentDefinition>,
123    ) {
124        self.llm_messages
125            .entry(request_id)
126            .or_insert(Vec::new())
127            .push(message);
128        self.new_message.update(|msgs| {
129            msgs.push(match component {
130                Some(component) => MessageType::LLMSuccess(component),
131                None => MessageType::LLMPartial,
132            })
133        });
134    }
135
136    pub fn get_messages(&mut self, request_id: u64) -> Vec<String> {
137        self.llm_messages.remove(&request_id).unwrap_or_default()
138    }
139
140    // Lets a designtime client retrieve granular modification data and dirty
141    // only what changed. This is a consuming read; later reads in the same tick
142    // are empty.
143    pub fn take_manifest_modification_data(&mut self) -> ManifestModificationData {
144        std::mem::take(&mut self.manifest_modification_data)
145    }
146
147    pub fn get_new_components(&mut self) -> Vec<ComponentDefinition> {
148        let mut new_components_to_process = Vec::new();
149
150        for component_type_id in &self.new_components {
151            if let Some(component) = self.manifest.components.get(component_type_id) {
152                new_components_to_process.push(component.clone());
153            }
154        }
155        self.new_components.clear();
156        new_components_to_process
157    }
158
159    pub fn get_manifest(&self) -> &PaxManifest {
160        &self.manifest
161    }
162
163    pub fn set_manifest(&mut self, manifest: PaxManifest) {
164        self.manifest = manifest;
165        self.increment_manifest_version();
166        self.manifest_loaded_from_server.set(true);
167        self.insert_reload(ReloadType::Tree);
168    }
169
170    pub fn set_initial_server_manifest(&mut self, manifest: PaxManifest) -> Result<()> {
171        validate_runtime_manifest(&manifest)?;
172
173        let incoming_abi_identity = runtime_abi_identity(&manifest)?;
174        if self.cartridge_abi_identity.is_empty() {
175            self.cartridge_abi_identity = runtime_abi_identity(&self.manifest)?;
176        }
177        if incoming_abi_identity != self.cartridge_abi_identity {
178            return Err(anyhow!(
179                "design-server manifest requires a different cartridge generation"
180            ));
181        }
182
183        if !self.manifest_loaded_from_server.get()
184            && self.manifest_version.get() == 0
185            && self.reload_queue.is_empty()
186        {
187            let manifest_changed = !manifests_match(&self.manifest, &manifest);
188            self.manifest = manifest;
189            self.manifest_loaded_from_server.set(true);
190            if manifest_changed {
191                self.increment_manifest_version();
192                self.insert_reload(ReloadType::Tree);
193            }
194            return Ok(());
195        }
196
197        self.set_manifest(manifest);
198        Ok(())
199    }
200
201    pub fn get_manifest_version(&self) -> Property<usize> {
202        self.manifest_version.clone()
203    }
204
205    pub fn insert_reload(&mut self, reload_type: ReloadType) {
206        if let ReloadType::Tree = &reload_type {
207            self.reload_queue.clear();
208            self.reload_queue.insert(ReloadType::Tree);
209            return;
210        }
211        if !self.reload_queue.contains(&ReloadType::Tree) {
212            self.reload_queue.insert(reload_type);
213        }
214    }
215
216    pub fn set_userland_root_component_type_id(&mut self, type_id: &TypeId) {
217        self.manifest.main_component_type_id = type_id.clone();
218    }
219
220    pub fn take_reload_queue(&mut self) -> HashSet<ReloadType> {
221        std::mem::take(&mut self.reload_queue)
222    }
223
224    pub fn increment_manifest_version(&mut self) {
225        self.manifest_version.update(|v| *v += 1);
226    }
227
228    pub fn build_new_node(
229        &mut self,
230        containing_component_type_id: TypeId,
231        node_type_id: TypeId,
232    ) -> NodeBuilder<'_> {
233        NodeBuilder::new(self, containing_component_type_id, node_type_id, true)
234    }
235
236    pub fn get_node_location(&self, uni: &UniqueTemplateNodeIdentifier) -> Option<NodeLocation> {
237        let component = self
238            .manifest
239            .components
240            .get(&uni.get_containing_component_type_id())?;
241        let template = component.template.as_ref()?;
242        template.get_location(&uni.get_template_node_id())
243    }
244
245    pub fn get_siblings(
246        &self,
247        uni: &UniqueTemplateNodeIdentifier,
248    ) -> Option<Vec<UniqueTemplateNodeIdentifier>> {
249        let component = self
250            .manifest
251            .components
252            .get(&uni.get_containing_component_type_id())?;
253        let template = component.template.as_ref()?;
254        Some(
255            template
256                .get_siblings(&uni.get_template_node_id())?
257                .into_iter()
258                .map(|tid| {
259                    UniqueTemplateNodeIdentifier::build(uni.get_containing_component_type_id(), tid)
260                })
261                .collect(),
262        )
263    }
264
265    pub fn get_parent(
266        &self,
267        uni: &UniqueTemplateNodeIdentifier,
268    ) -> Option<UniqueTemplateNodeIdentifier> {
269        let component = self
270            .manifest
271            .components
272            .get(&uni.get_containing_component_type_id())?;
273        let template = component.template.as_ref()?;
274        Some(UniqueTemplateNodeIdentifier::build(
275            uni.get_containing_component_type_id(),
276            template.get_parent(&uni.get_template_node_id())?,
277        ))
278    }
279
280    pub fn get_all_property_definitions(&self) -> Vec<&PropertyDefinition> {
281        self.manifest
282            .type_table
283            .values()
284            .flat_map(|v| v.property_definitions.iter())
285            .collect()
286    }
287
288    pub fn get_class(
289        &self,
290        type_id: &TypeId,
291        name: &str,
292    ) -> Result<Vec<(String, ValueDefinition, Option<TypeId>)>> {
293        if !pax_manifest::utils::valid_class_or_id(name) {
294            return Err(anyhow!("not a valid class name"));
295        }
296        let component = self
297            .manifest
298            .components
299            .get(&type_id)
300            .ok_or_else(|| anyhow!("couldn't find component with type id: {type_id:?}"))?;
301        let Some(settings) = &component.settings else {
302            return Err(anyhow!("component has no settings"));
303        };
304        let class_block = settings
305            .iter()
306            .find_map(|setting| match setting {
307                pax_manifest::SettingsBlockElement::SelectorBlock(token, block) => {
308                    (token.token_value.ends_with(name)).then_some(block)
309                }
310                _ => None,
311            })
312            .ok_or_else(|| anyhow!("no class with name {name}"))?;
313        // TODO PERF: keep track of cached String -> TypeId map, that get's re-generated on
314        // manifest version change.
315        let property_definitions: Vec<_> = self.get_all_property_definitions();
316        let mut res = vec![];
317
318        for element in &class_block.elements {
319            if let SettingElement::Setting(token, value_definition) = element {
320                let name = token.token_value.clone();
321                let mut property_definitions_with_name: Vec<_> = property_definitions
322                    .iter()
323                    .copied()
324                    .filter(|pd| pd.name == name)
325                    .map(|pd| (&pd.name, &pd.type_id))
326                    .collect();
327                property_definitions_with_name.sort();
328                property_definitions_with_name.dedup();
329                let property_type = if property_definitions_with_name.len() != 1 {
330                    log::warn!(
331                        "class property with name {:?} has an abiguous type, possible types: {:#?}",
332                        name,
333                        property_definitions_with_name
334                    );
335                    None
336                } else {
337                    property_definitions_with_name.into_iter().next()
338                }
339                .map(|(_, v)| v);
340                res.push((name, value_definition.clone(), property_type.cloned()));
341            }
342        }
343        Ok(res)
344    }
345
346    pub fn get_classes(&self, type_id: &TypeId) -> Result<Vec<String>> {
347        let component = self
348            .manifest
349            .components
350            .get(&type_id)
351            .ok_or_else(|| anyhow!("couldn't find component with type id: {type_id:?}"))?;
352        let Some(settings) = &component.settings else {
353            return Ok(vec![]);
354        };
355        let classes: Vec<_> = settings
356            .iter()
357            .filter_map(|setting| match setting {
358                pax_manifest::SettingsBlockElement::SelectorBlock(token, _block) => {
359                    Some(token.token_value.to_string())
360                }
361                _ => None,
362            })
363            .collect();
364        Ok(classes)
365    }
366
367    pub fn move_node(
368        &mut self,
369        uni: UniqueTemplateNodeIdentifier,
370        location: NodeLocation,
371    ) -> Result<usize, String> {
372        let res = self.execute_command(MoveTemplateNodeRequest::new(uni, location))?;
373        Ok(res.get_id())
374    }
375
376    pub fn get_node_children(
377        &mut self,
378        uni: UniqueTemplateNodeIdentifier,
379    ) -> Result<Vec<UniqueTemplateNodeIdentifier>, String> {
380        let resp = self
381            .execute_command(GetChildrenRequest { uni: uni.clone() })
382            .unwrap();
383        Ok(resp.children)
384    }
385
386    pub fn swap_main_component(&mut self, component: ComponentDefinition) -> Result<(), String> {
387        let command = template::SwapMainComponentRequest::new(component);
388        self.execute_command(command)?;
389        Ok(())
390    }
391
392    pub fn copy_subtrees(&self, type_id: &TypeId, nodes: &[TemplateNodeId]) -> Option<SubTrees> {
393        let roots: Vec<_> = nodes.iter().cloned().collect();
394        let mut children = HashMap::new();
395        let mut nodes = HashMap::new();
396
397        let component = self.manifest.components.get(type_id)?;
398        let template = component.template.as_ref()?;
399        let mut to_visit: Vec<_> = roots.iter().cloned().collect();
400        while let Some(node) = to_visit.pop() {
401            if let Some(node_def) = template.get_node(&node) {
402                nodes.insert(node.clone(), node_def.clone());
403                let node_children = template.get_children(&node).unwrap_or_default();
404                children.insert(node.clone(), node_children.clone());
405                to_visit.extend(node_children);
406            }
407        }
408
409        Some(SubTrees {
410            roots,
411            children,
412            nodes,
413        })
414    }
415
416    pub fn paste_subtrees(
417        &mut self,
418        location: NodeLocation,
419        subtrees: SubTrees,
420    ) -> Result<Vec<TemplateNodeId>, String> {
421        let res = self.execute_command(PasteSubTreeRequest::new(location, subtrees))?;
422        Ok(res.get_created().to_vec())
423    }
424
425    pub fn get_node_builder(
426        &mut self,
427        uni: UniqueTemplateNodeIdentifier,
428        overwrite_expressions: bool,
429    ) -> Option<NodeBuilder<'_>> {
430        NodeBuilder::retrieve_node(self, uni, overwrite_expressions)
431    }
432
433    pub fn get_class_builder<'a>(
434        &'a mut self,
435        component_type_id: TypeId,
436        class_name: &'a str,
437    ) -> Result<ClassBuilder<'a>> {
438        ClassBuilder::new(self, component_type_id, class_name)
439    }
440
441    pub fn get_main_component(&self) -> &TypeId {
442        &self.manifest.main_component_type_id
443    }
444
445    pub fn get_components(&self) -> Vec<TypeId> {
446        self.manifest.components.keys().cloned().collect()
447    }
448
449    pub fn get_component(&self, type_id: &TypeId) -> anyhow::Result<&ComponentDefinition> {
450        self.manifest
451            .components
452            .get(type_id)
453            .ok_or(anyhow!("couldn't find component"))
454    }
455
456    pub fn get_property(
457        &self,
458        unid: &UniqueTemplateNodeIdentifier,
459        key: &str,
460    ) -> Option<ValueDefinition> {
461        let tnd = self.manifest.get_template_node(unid)?;
462        if let Some(settings) = &tnd.settings {
463            for setting in settings {
464                if let SettingElement::Setting(token, value) = setting {
465                    if token.token_value == key {
466                        return Some(value.clone());
467                    }
468                }
469            }
470        }
471        None
472    }
473
474    pub fn get_property_type(
475        &self,
476        unid: &UniqueTemplateNodeIdentifier,
477        key: &str,
478    ) -> Option<TypeId> {
479        let tnd = self.manifest.get_template_node(unid)?;
480        let property_types = self.manifest.type_table.get(&tnd.type_id)?;
481        property_types
482            .property_definitions
483            .iter()
484            .find(|v| v.name == key)
485            .map(|v| v.type_id.clone())
486    }
487
488    pub fn remove_node(&mut self, uni: UniqueTemplateNodeIdentifier) -> Result<usize, String> {
489        let command = RemoveTemplateNodeRequest::new(uni);
490        let resp = self.execute_command(command)?;
491        Ok(resp.get_id())
492    }
493
494    pub fn move_to_new_component(
495        &mut self,
496        nodes: &[MoveToComponentEntry],
497        x: f64,
498        y: f64,
499        width: f64,
500        height: f64,
501    ) -> Result<(), String> {
502        let new_component_number = self.next_new_component_id;
503        let command = ConvertToComponentRequest::new(
504            nodes.to_vec(),
505            new_component_number,
506            x,
507            y,
508            width,
509            height,
510        );
511        let resp = self.execute_command(command)?;
512        self.new_components.push(resp.new_component_type_id);
513        self.next_new_component_id += 1;
514        Ok(())
515    }
516
517    pub fn send_component_update(&mut self, type_id: &TypeId) {
518        let current_component = self.manifest.components.get(type_id).unwrap().clone();
519        let prev_component: Option<ComponentDefinition> =
520            self.last_serialized_version.get(type_id).cloned();
521        if let Some(prev_component) = prev_component {
522            let prev_serialized =
523                press_code_serialization_template(prev_component.clone()).unwrap_or("".to_string());
524            let post_serialized = press_code_serialization_template(current_component.clone())
525                .unwrap_or("".to_string());
526            let diff = diff_html(&prev_serialized, &post_serialized);
527            let message_type = diff.map(|d| MessageType::Serialization(d));
528            if let Some(message) = message_type {
529                self.new_message.update(|msgs| {
530                    msgs.push(message);
531                });
532            }
533        }
534        self.last_serialized_version
535            .insert(type_id.clone(), current_component);
536    }
537
538    pub fn execute_command<R: Request, C>(&mut self, mut command: C) -> Result<R::Response, String>
539    where
540        C: Command<R>,
541    {
542        let mut response: <R as Request>::Response = command.execute(&mut self.manifest)?;
543        let command_id = self.next_command_id;
544        if let Some(command) = command.as_undo_redo() {
545            self.undo_stack.push((command_id, command));
546            self.redo_stack.clear();
547        }
548
549        response.set_id(command_id);
550        self.next_command_id += 1;
551        if let Some(reload_type) = response.get_reload_type() {
552            match &reload_type {
553                ReloadType::Tree | ReloadType::Subtree(_) => {
554                    self.manifest_modification_data.tree_modified = true
555                }
556                ReloadType::Node(_, props) => self
557                    .manifest_modification_data
558                    .modified_properties
559                    .extend(props.iter().cloned()),
560            }
561            self.insert_reload(reload_type);
562            self.manifest_version.update(|v| *v += 1);
563        }
564
565        Ok(response)
566    }
567
568    pub fn undo(&mut self) -> Result<(), String> {
569        if let Some((id, mut command)) = self.undo_stack.pop() {
570            command.undo(&mut self.manifest)?;
571            self.redo_stack.push((id, command));
572            self.manifest_version.update(|v| *v += 1);
573            self.insert_reload(ReloadType::Tree);
574        }
575        Ok(())
576    }
577
578    pub fn redo(&mut self) -> Result<(), String> {
579        if let Some((id, mut command)) = self.redo_stack.pop() {
580            command.redo(&mut self.manifest)?;
581            self.undo_stack.push((id, command));
582            self.manifest_version.update(|v| *v += 1);
583            self.insert_reload(ReloadType::Tree);
584        }
585        Ok(())
586    }
587
588    pub fn get_last_undo_id(&self) -> Option<usize> {
589        self.undo_stack.last().map(|l| l.0)
590    }
591
592    pub fn undo_until(&mut self, command_id: Option<usize>) -> Result<(), String> {
593        while let Some((id, _)) = self.undo_stack.last() {
594            if command_id.is_some_and(|c_id| c_id == *id) {
595                break;
596            }
597            self.undo()?;
598        }
599        Ok(())
600    }
601
602    pub fn redo_including(&mut self, command_id: usize) -> Result<(), String> {
603        while let Some(&(id, _)) = self.redo_stack.last() {
604            self.redo()?;
605            if id == command_id {
606                break;
607            }
608        }
609        Ok(())
610    }
611
612    pub fn replace_template(
613        &mut self,
614        component_type_id: TypeId,
615        template: ComponentTemplate,
616        settings_block: Vec<SettingsBlockElement>,
617        timelines: Vec<TimelineDefinition>,
618    ) -> Result<usize, String> {
619        if !self.manifest.components.contains_key(&component_type_id) {
620            return Err(format!(
621                "design-server template update targets missing component {}",
622                component_type_id
623            ));
624        }
625        validate_template_references(&self.manifest, &component_type_id, &template)
626            .map_err(|err| err.to_string())?;
627
628        // Event-handler descriptors are generated statically from both the
629        // component settings block and inline template bindings. Validate the
630        // prospective manifest before executing the command so a template-only
631        // update cannot require a handler that this cartridge did not compile.
632        let mut prospective_manifest = self.manifest.clone();
633        let component = prospective_manifest
634            .components
635            .get_mut(&component_type_id)
636            .expect("target component presence was checked above");
637        component.template = Some(template.clone());
638        component.settings = Some(settings_block.clone());
639        component.timelines.clone_from(&timelines);
640        let prospective_identity =
641            runtime_abi_identity(&prospective_manifest).map_err(|err| err.to_string())?;
642        if prospective_identity != self.cartridge_abi_identity {
643            return Err(
644                "design-server template update requires different runtime descriptors".to_string(),
645            );
646        }
647
648        let command = template::ReplaceTemplateRequest::new(
649            component_type_id,
650            template,
651            settings_block,
652            timelines,
653        );
654        let resp = self.execute_command(command)?;
655        Ok(resp.get_id())
656    }
657
658    pub fn replace_node_subtree(
659        &mut self,
660        uni: UniqueTemplateNodeIdentifier,
661        new_subtree: Option<template::SerializedTemplateNodeSubtree>,
662        reload_uni: Option<UniqueTemplateNodeIdentifier>,
663    ) -> Result<usize, String> {
664        let command = template::ReplaceNodeSubtreeRequest::new(uni, new_subtree, reload_uni);
665        let resp = self.execute_command(command)?;
666        Ok(resp.get_id())
667    }
668
669    pub fn component_has_slots(&self, type_id: &TypeId) -> bool {
670        let Some(component) = self.manifest.components.get(type_id) else {
671            return false;
672        };
673        let Some(template) = component.template.as_ref() else {
674            return false;
675        };
676        template.contains_slots()
677    }
678}
679
680fn manifests_match(left: &PaxManifest, right: &PaxManifest) -> bool {
681    if !type_tables_match(left, right) {
682        return false;
683    }
684
685    let left_identity = (
686        &left.components,
687        &left.main_component_type_id,
688        &left.assets_dirs,
689        &left.engine_import_path,
690    );
691    let right_identity = (
692        &right.components,
693        &right.main_component_type_id,
694        &right.assets_dirs,
695        &right.engine_import_path,
696    );
697    match (
698        rmp_serde::to_vec(&left_identity),
699        rmp_serde::to_vec(&right_identity),
700    ) {
701        (Ok(left), Ok(right)) => left == right,
702        _ => false,
703    }
704}
705
706fn type_tables_match(left: &PaxManifest, right: &PaxManifest) -> bool {
707    if left.type_table.len() != right.type_table.len() {
708        return false;
709    }
710
711    left.type_table.iter().all(|(type_id, left_definition)| {
712        let Some(right_definition) = right.type_table.get(type_id) else {
713            return false;
714        };
715        match (
716            rmp_serde::to_vec(left_definition),
717            rmp_serde::to_vec(right_definition),
718        ) {
719            (Ok(left), Ok(right)) => left == right,
720            _ => false,
721        }
722    })
723}
724
725/// Identity of the static runtime descriptors generated for a cartridge.
726/// Handler identity models the runtime's first-match lookup: order between
727/// distinct names and same-signature duplicates after the first match are
728/// irrelevant. Conflicting later signatures remain part of the identity so a
729/// template-only update cannot route an event through an incompatible first
730/// wrapper. This remains designtime metadata and is deliberately not stored in
731/// `PaxManifest` or any release-cartridge representation.
732pub fn runtime_abi_identity(manifest: &PaxManifest) -> Result<Vec<u8>> {
733    let component_identity = manifest
734        .components
735        .values()
736        .map(|component| {
737            (
738                &component.type_id,
739                component.is_primitive,
740                component.is_struct_only_component,
741                &component.primitive_instance_import_path,
742            )
743        })
744        .collect::<Vec<_>>();
745    let mut type_identity = manifest.type_table.iter().collect::<Vec<_>>();
746    type_identity.sort_by(|(left, _), (right, _)| left.cmp(right));
747
748    let handler_identity = generated_handler_identity(manifest)?;
749
750    rmp_serde::to_vec(&(component_identity, type_identity, handler_identity))
751        .map_err(|err| anyhow!("failed to identify cartridge ABI: {err}"))
752}
753
754/// Mirrors the handler descriptors emitted by cartridge generation without
755/// requiring a complete type table. The latter matters for early and test
756/// manifests, while the descriptor identity itself depends only on event names
757/// and their generated argument types.
758fn generated_handler_identity(
759    manifest: &PaxManifest,
760) -> Result<Vec<(TypeId, Vec<(String, Vec<Option<String>>)>)>> {
761    let event_map = manifest.event_to_args_map();
762    let mut component_handlers = Vec::new();
763
764    for component in manifest.components.values() {
765        if component.is_struct_only_component {
766            continue;
767        }
768
769        let mut handlers = BTreeMap::new();
770        if let Some(settings) = &component.settings {
771            for setting in settings {
772                if let SettingsBlockElement::Handler(event, targets) = setting {
773                    let args_type = event_map
774                        .get(event.token_value.as_str())
775                        .ok_or_else(|| {
776                            anyhow!(
777                                "custom handler event {} is not supported in settings blocks",
778                                event.token_value
779                            )
780                        })?
781                        .as_ref()
782                        .map(|args| format!("Event<{args}>"));
783                    for target in targets {
784                        record_handler_identity(
785                            &mut handlers,
786                            clean_handler_name(&target.token_value),
787                            args_type.clone(),
788                        );
789                    }
790                }
791            }
792        }
793
794        if let Some(template) = &component.template {
795            for node in template.get_nodes() {
796                let Some(settings) = &node.settings else {
797                    continue;
798                };
799                for setting in settings {
800                    if let SettingElement::Setting(
801                        event,
802                        ValueDefinition::EventBindingTarget(target),
803                    ) = setting
804                    {
805                        let args_type = event_map
806                            .get(event.token_value.as_str())
807                            .and_then(|args| args.as_ref())
808                            .map(|args| format!("Event<{args}>"));
809                        record_handler_identity(
810                            &mut handlers,
811                            clean_handler_name(&target.name),
812                            args_type,
813                        );
814                    }
815                }
816            }
817        }
818
819        component_handlers.push((component.type_id.clone(), handlers.into_iter().collect()));
820    }
821
822    Ok(component_handlers)
823}
824
825fn record_handler_identity(
826    handlers: &mut BTreeMap<String, Vec<Option<String>>>,
827    name: String,
828    args_type: Option<String>,
829) {
830    let signatures = handlers.entry(name).or_default();
831    let differs_from_first = signatures
832        .first()
833        .map(|first| first != &args_type)
834        .unwrap_or(true);
835
836    // Runtime lookup always selects the first descriptor for a name. Repeated
837    // descriptors with that same signature are therefore irrelevant, but a
838    // later conflicting wrapper must prevent template-only activation.
839    if differs_from_first {
840        signatures.push(args_type);
841    }
842}
843
844fn clean_handler_name(name: &str) -> String {
845    name.replace("self.", "").replace("this.", "")
846}
847
848fn validate_runtime_manifest(manifest: &PaxManifest) -> Result<()> {
849    if !manifest
850        .components
851        .contains_key(&manifest.main_component_type_id)
852    {
853        return Err(anyhow!(
854            "design-server manifest is missing its main component {}",
855            manifest.main_component_type_id
856        ));
857    }
858
859    for (containing_type_id, component) in &manifest.components {
860        let Some(template) = &component.template else {
861            continue;
862        };
863        validate_template_references(manifest, containing_type_id, template)?;
864    }
865
866    Ok(())
867}
868
869fn validate_template_references(
870    manifest: &PaxManifest,
871    containing_type_id: &TypeId,
872    template: &ComponentTemplate,
873) -> Result<()> {
874    for node in template.get_nodes() {
875        if matches!(
876            node.type_id.get_pax_type(),
877            pax_manifest::PaxType::If
878                | pax_manifest::PaxType::Router
879                | pax_manifest::PaxType::Slot
880                | pax_manifest::PaxType::Repeat
881                | pax_manifest::PaxType::Comment
882        ) {
883            continue;
884        }
885        if !manifest.components.contains_key(&node.type_id) {
886            return Err(anyhow!(
887                "design-server manifest component {} references missing component {}",
888                containing_type_id,
889                node.type_id
890            ));
891        }
892    }
893
894    Ok(())
895}
896
897pub trait Undo {
898    fn undo(&mut self, manifest: &mut PaxManifest) -> Result<(), String>;
899}
900
901#[derive(Serialize, Deserialize)]
902pub enum UndoRedoCommand {
903    AddTemplateNodeRequest(Box<template::AddTemplateNodeRequest>),
904    RemoveTemplateNodeRequest(Box<template::RemoveTemplateNodeRequest>),
905    MoveTemplateNodeRequest(Box<template::MoveTemplateNodeRequest>),
906    UpdateTemplateNodeRequest(Box<template::UpdateTemplateNodeRequest>),
907    UpdateClassRequest(Box<template::UpdateClassRequest>),
908    PasteSubTreeRequest(Box<template::PasteSubTreeRequest>),
909    ReplaceTemplateRequest(Box<template::ReplaceTemplateRequest>),
910    ReplaceNodeSubtreeRequest(Box<template::ReplaceNodeSubtreeRequest>),
911    ConvertToComponentRequest(Box<template::ConvertToComponentRequest>),
912    SwapMainComponentRequest(Box<template::SwapMainComponentRequest>),
913}
914
915impl UndoRedoCommand {
916    fn undo(&mut self, manifest: &mut PaxManifest) -> Result<(), String> {
917        match self {
918            UndoRedoCommand::MoveTemplateNodeRequest(command) => command.undo(manifest),
919            UndoRedoCommand::AddTemplateNodeRequest(command) => command.undo(manifest),
920            UndoRedoCommand::RemoveTemplateNodeRequest(command) => command.undo(manifest),
921            UndoRedoCommand::UpdateTemplateNodeRequest(command) => command.undo(manifest),
922            UndoRedoCommand::UpdateClassRequest(command) => command.undo(manifest),
923            UndoRedoCommand::PasteSubTreeRequest(command) => command.undo(manifest),
924            UndoRedoCommand::ReplaceTemplateRequest(command) => command.undo(manifest),
925            UndoRedoCommand::ReplaceNodeSubtreeRequest(command) => command.undo(manifest),
926            UndoRedoCommand::ConvertToComponentRequest(command) => command.undo(manifest),
927            UndoRedoCommand::SwapMainComponentRequest(command) => command.undo(manifest),
928        }
929    }
930
931    fn redo(&mut self, manifest: &mut PaxManifest) -> Result<(), String> {
932        match self {
933            UndoRedoCommand::AddTemplateNodeRequest(command) => {
934                let _ = command.execute(manifest);
935            }
936            UndoRedoCommand::RemoveTemplateNodeRequest(command) => {
937                let _ = command.execute(manifest);
938            }
939            UndoRedoCommand::MoveTemplateNodeRequest(command) => {
940                let _ = command.execute(manifest);
941            }
942            UndoRedoCommand::UpdateTemplateNodeRequest(command) => {
943                let _ = command.execute(manifest);
944            }
945            UndoRedoCommand::UpdateClassRequest(command) => {
946                let _ = command.execute(manifest);
947            }
948            UndoRedoCommand::PasteSubTreeRequest(command) => {
949                let _ = command.execute(manifest);
950            }
951            UndoRedoCommand::ReplaceTemplateRequest(command) => {
952                let _ = command.execute(manifest);
953            }
954            UndoRedoCommand::ReplaceNodeSubtreeRequest(command) => {
955                let _ = command.execute(manifest);
956            }
957            UndoRedoCommand::ConvertToComponentRequest(command) => {
958                let _ = command.execute(manifest);
959            }
960            UndoRedoCommand::SwapMainComponentRequest(command) => {
961                let _ = command.execute(manifest);
962            }
963        }
964        Ok(())
965    }
966}
967
968#[derive(Serialize, Deserialize, Clone, Debug)]
969pub struct MoveToComponentEntry {
970    pub x: f64,
971    pub y: f64,
972    pub width: f64,
973    pub height: f64,
974    pub id: UniqueTemplateNodeIdentifier,
975}
976
977#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Hash, Eq)]
978pub enum ReloadType {
979    Tree,
980    Subtree(UniqueTemplateNodeIdentifier),
981    Node(UniqueTemplateNodeIdentifier, Vec<String>),
982    // might have both node and class updates in the same transaction, make struct instead?
983    // Class(TypeId, String, Vec<String>),
984}
985
986impl Interpolatable for SubTrees {}
987
988#[derive(Serialize, Deserialize, Clone, Debug, Default)]
989pub struct SubTrees {
990    roots: Vec<TemplateNodeId>,
991    children: HashMap<TemplateNodeId, Vec<TemplateNodeId>>,
992    nodes: HashMap<TemplateNodeId, TemplateNodeDefinition>,
993}
994
995#[derive(Serialize, Deserialize, Clone)]
996pub enum MessageType {
997    Serialization(String),
998    LLMPartial,
999    LLMSuccess(ComponentDefinition),
1000}
1001
1002impl Interpolatable for MessageType {}