Skip to main content

pax_designtime/orm/template/
node_builder.rs

1use std::collections::HashMap;
2
3use anyhow::{anyhow, Result};
4use pax_manifest::{
5    pax_runtime_api::ToPaxValue, ControlFlowRepeatPredicateDefinition,
6    ControlFlowSettingsDefinition, ExpressionInfo, NodeLocation, NodeType, PaxExpression,
7    PaxIdentifier, PaxPrimary, PropertyDefinition, SettingElement, Token, TypeId,
8    UniqueTemplateNodeIdentifier, ValueDefinition,
9};
10
11use super::{
12    AddTemplateNodeRequest, ControlFlowSettingsDefinitionUpdate, GetTemplateNodeRequest,
13    UpdateTemplateNodeRequest,
14};
15use crate::orm::PaxManifestORM;
16
17/// Builder for creating and modifying template nodes in the PaxManifest.
18pub struct NodeBuilder<'a> {
19    orm: &'a mut PaxManifestORM,
20    containing_component_type_id: TypeId,
21    node_type_id: TypeId,
22    updated_property_map: HashMap<Token, Option<ValueDefinition>>,
23    // true = insert, false = remove
24    updated_classes: HashMap<String, bool>,
25    control_flow_updates: ControlFlowSettingsDefinitionUpdate,
26    unique_node_identifier: Option<UniqueTemplateNodeIdentifier>,
27    location: Option<NodeLocation>,
28    overwrite_expressions: bool,
29}
30
31pub struct SaveData {
32    pub undo_id: Option<usize>,
33    pub unique_id: UniqueTemplateNodeIdentifier,
34}
35
36impl<'a> NodeBuilder<'a> {
37    pub fn new(
38        orm: &'a mut PaxManifestORM,
39        containing_component_type_id: TypeId,
40        node_type_id: TypeId,
41        overwrite_expressions: bool,
42    ) -> Self {
43        NodeBuilder {
44            orm,
45            containing_component_type_id,
46            node_type_id,
47            updated_property_map: HashMap::new(),
48            updated_classes: HashMap::new(),
49            unique_node_identifier: None,
50            location: None,
51            overwrite_expressions,
52            control_flow_updates: ControlFlowSettingsDefinitionUpdate::default(),
53        }
54    }
55
56    pub fn retrieve_node(
57        orm: &'a mut PaxManifestORM,
58        uni: UniqueTemplateNodeIdentifier,
59        overwrite_expressions: bool,
60    ) -> Option<Self> {
61        let resp = orm
62            .execute_command(GetTemplateNodeRequest { uni: uni.clone() })
63            .ok()?;
64        if let Some(node) = resp.node {
65            let location = orm.manifest.get_node_location(&uni);
66            let res = Some(NodeBuilder {
67                orm,
68                containing_component_type_id: uni.get_containing_component_type_id(),
69                node_type_id: node.type_id,
70                updated_property_map: HashMap::new(),
71                updated_classes: HashMap::new(),
72                unique_node_identifier: Some(uni),
73                control_flow_updates: ControlFlowSettingsDefinitionUpdate::default(),
74                location,
75                overwrite_expressions,
76            });
77            res
78        } else {
79            None
80        }
81    }
82
83    pub fn get_unique_identifier(&self) -> Option<UniqueTemplateNodeIdentifier> {
84        self.unique_node_identifier.clone()
85    }
86
87    pub fn set_type_id(&mut self, type_id: &TypeId) {
88        self.node_type_id = type_id.clone();
89    }
90
91    pub fn get_type_id(&self) -> TypeId {
92        self.node_type_id.clone()
93    }
94
95    pub fn get_control_flow_properties(&mut self) -> Option<ControlFlowSettingsDefinition> {
96        if let Some(uni) = &self.unique_node_identifier {
97            let resp = self
98                .orm
99                .execute_command(GetTemplateNodeRequest { uni: uni.clone() })
100                .unwrap();
101            return resp.node?.control_flow_settings.clone();
102        }
103        None
104    }
105
106    pub fn get_all_property_definitions(
107        &mut self,
108    ) -> Vec<(PropertyDefinition, Option<ValueDefinition>)> {
109        let properties = self
110            .orm
111            .manifest
112            .get_all_component_property_definitions(&self.node_type_id);
113
114        let mut full_settings: HashMap<Token, ValueDefinition> = HashMap::new();
115        if let Some(uni) = &self.unique_node_identifier {
116            let resp = self
117                .orm
118                .execute_command(GetTemplateNodeRequest { uni: uni.clone() })
119                .unwrap();
120            if let Some(node) = resp.node {
121                if let Some(settings) = node.settings {
122                    for setting in settings {
123                        if let SettingElement::Setting(token, value) = setting {
124                            full_settings.insert(token, value);
125                        }
126                    }
127                }
128            }
129        }
130
131        let values: Vec<Option<ValueDefinition>> = properties
132            .iter()
133            .map(|prop| {
134                let key = &Token::new_without_location(prop.name.clone());
135                full_settings.get(key).cloned()
136            })
137            .collect();
138
139        properties.into_iter().zip(values).collect()
140    }
141
142    pub fn get_all_applied_classes(&mut self) -> Vec<String> {
143        let mut classes = vec![];
144        if let Some(uni) = &self.unique_node_identifier {
145            let resp = self
146                .orm
147                .execute_command(GetTemplateNodeRequest { uni: uni.clone() })
148                .unwrap();
149            if let Some(node) = resp.node {
150                classes.extend(
151                    node.selector_info
152                        .literal_classes()
153                        .into_iter()
154                        .map(|name| format!(".{name}")),
155                );
156                if let Some(settings) = node.settings {
157                    for setting in settings {
158                        if let SettingElement::Setting(token, value) = setting {
159                            if let ValueDefinition::Identifier(PaxIdentifier { name }) = value {
160                                if token.token_value == "id" {
161                                    classes.push(format!("#{}", name));
162                                }
163                            }
164                        }
165                    }
166                }
167            }
168        }
169        classes
170    }
171
172    pub fn set_repeat_source(&mut self, value: &str) -> Result<()> {
173        let repeat_source_expression = &mut self.control_flow_updates.repeat_source_expression;
174
175        let value = pax_manifest::utils::parse_value(value).map_err(|e| anyhow!(e.to_owned()))?;
176        if let Some(value) = value {
177            let expression = match value {
178                ValueDefinition::LiteralValue(value) => ExpressionInfo {
179                    expression: PaxExpression::Primary(Box::new(PaxPrimary::Literal(value))),
180                    dependencies: vec![],
181                },
182                ValueDefinition::Expression(expression) => expression,
183                ValueDefinition::Identifier(identifier) => ExpressionInfo {
184                    expression: PaxExpression::Primary(Box::new(PaxPrimary::Identifier(
185                        identifier,
186                        vec![],
187                    ))),
188                    dependencies: vec![],
189                },
190                _ => return Err(anyhow!("a control flow source needs to be an expression")),
191            };
192            *repeat_source_expression = Some(Some(expression));
193        } else {
194            *repeat_source_expression = Some(None);
195        }
196        Ok(())
197    }
198
199    pub fn set_repeat_predicate(&mut self, value: &str) -> Result<()> {
200        let control_flow_predicate_definition = self
201            .control_flow_updates
202            .repeat_predicate_definition
203            .get_or_insert_with(|| None);
204        if value.is_empty() {
205            return Ok(());
206        }
207        if let Some((a, b)) = value.split_once(", ") {
208            // assume a tuple
209            let (a, b) = (
210                a.trim_start_matches("(").trim(),
211                b.trim_end_matches(")").trim(),
212            );
213            *control_flow_predicate_definition = Some(
214                ControlFlowRepeatPredicateDefinition::ElemIdIndexId(a.to_string(), b.to_string()),
215            );
216        } else {
217            //assume a single elem
218            *control_flow_predicate_definition = Some(
219                ControlFlowRepeatPredicateDefinition::ElemId(value.to_string()),
220            );
221        }
222        Ok(())
223    }
224
225    pub fn set_conditional_source(&mut self, value: &str) -> Result<()> {
226        let conditional_expression = &mut self.control_flow_updates.conditional_expression;
227        let value = pax_manifest::utils::parse_value(value).map_err(|e| anyhow!(e.to_owned()))?;
228        if let Some(value) = value {
229            let expression = match value {
230                ValueDefinition::LiteralValue(value) => ExpressionInfo {
231                    expression: PaxExpression::Primary(Box::new(PaxPrimary::Literal(value))),
232                    dependencies: vec![],
233                },
234                ValueDefinition::Expression(expression) => expression,
235                ValueDefinition::Identifier(identifier) => ExpressionInfo {
236                    expression: PaxExpression::Primary(Box::new(PaxPrimary::Identifier(
237                        identifier,
238                        vec![],
239                    ))),
240                    dependencies: vec![],
241                },
242                _ => return Err(anyhow!("a control flow source needs to be an expression")),
243            };
244            *conditional_expression = Some(Some(expression));
245        } else {
246            *conditional_expression = Some(None);
247        }
248        Ok(())
249    }
250
251    pub fn set_property_from_value_definition(
252        &mut self,
253        key: &str,
254        value: Option<ValueDefinition>,
255    ) -> Result<()> {
256        if !self.overwrite_expressions && !self.is_literal(key).unwrap_or(true) {
257            return Err(anyhow!("the property {key:?} is bound to an expression"));
258        }
259        if let Some(value) = value {
260            let token = Token::new_without_location(key.to_owned());
261            self.updated_property_map.insert(token, Some(value));
262        } else {
263            self.remove_property(key);
264        }
265        Ok(())
266    }
267
268    pub fn set_property_from_typed<T: ToPaxValue>(
269        &mut self,
270        key: &str,
271        value: Option<T>,
272    ) -> Result<()> {
273        self.set_property_from_value_definition(
274            key,
275            value.map(|v| ValueDefinition::LiteralValue(v.to_pax_value())),
276        )
277    }
278
279    pub fn is_literal(&mut self, key: &str) -> Option<bool> {
280        if let Some(uni) = &self.unique_node_identifier {
281            let resp = self
282                .orm
283                .execute_command(GetTemplateNodeRequest { uni: uni.clone() })
284                .unwrap();
285            if let Some(node) = resp.node {
286                if let Some(settings) = node.settings {
287                    for setting in settings {
288                        if let SettingElement::Setting(token, value) = setting {
289                            if token.token_value == key {
290                                return Some(matches!(value, ValueDefinition::LiteralValue(_)));
291                            }
292                        }
293                    }
294                }
295            }
296        }
297        None
298    }
299
300    #[deprecated = "use typed versions instead"]
301    pub fn set_property(&mut self, key: &str, value: &str) -> Result<()> {
302        self.set_property_from_value_definition(
303            key,
304            pax_manifest::utils::parse_value(value).map_err(|e| anyhow!(e.to_owned()))?,
305        )
306    }
307
308    pub fn remove_property(&mut self, key: &str) {
309        let key = Token::new_without_location(key.to_owned());
310        self.updated_property_map.insert(key, None);
311    }
312
313    pub fn add_class(&mut self, class: &str) -> Result<()> {
314        if !pax_manifest::utils::valid_class_or_id(class) {
315            return Err(anyhow!("not valid class identifier"));
316        }
317        self.updated_classes.insert(class.to_string(), true);
318        Ok(())
319    }
320
321    pub fn remove_class(&mut self, class: &str) -> Result<()> {
322        if !pax_manifest::utils::valid_class_or_id(class) {
323            return Err(anyhow!("not a valid class identifier"));
324        }
325        self.updated_classes.insert(class.to_string(), false);
326        Ok(())
327    }
328
329    pub fn set_location(&mut self, location: NodeLocation) {
330        self.location = Some(location);
331    }
332
333    pub fn save(mut self) -> Result<SaveData, String> {
334        let id = if let Some(uni) = &self.unique_node_identifier {
335            // Node already exists
336            let location = self
337                .location
338                .unwrap_or_else(|| self.orm.manifest.get_node_location(uni).unwrap());
339
340            let resp = self.orm.execute_command(UpdateTemplateNodeRequest::new(
341                uni.clone(),
342                Some(self.node_type_id),
343                self.updated_property_map,
344                self.updated_classes,
345                Some(location),
346                self.control_flow_updates,
347            ))?;
348            resp.command_id
349        } else {
350            let node_data = match self.node_type_id.get_pax_type() {
351                pax_manifest::PaxType::If
352                | pax_manifest::PaxType::Router
353                | pax_manifest::PaxType::Slot
354                | pax_manifest::PaxType::Repeat => {
355                    let control_flow_settings_defintion = ControlFlowSettingsDefinition {
356                        repeat_predicate_definition: self
357                            .control_flow_updates
358                            .repeat_predicate_definition
359                            .flatten(),
360                        repeat_source_expression: self
361                            .control_flow_updates
362                            .repeat_source_expression
363                            .flatten(),
364                        repeat_key_expression: None,
365                        condition_expression: self
366                            .control_flow_updates
367                            .conditional_expression
368                            .flatten(),
369                        slot_index_expression: self
370                            .control_flow_updates
371                            .slot_index_expression
372                            .flatten(),
373                        conditional_branches: vec![],
374                        route_branches: vec![],
375                    };
376
377                    NodeType::ControlFlow(Box::new(control_flow_settings_defintion))
378                }
379                pax_manifest::PaxType::Comment => {
380                    NodeType::Comment("COMMENT BUILDER WRITING NOT IMPLEMENTED".to_string())
381                }
382                pax_manifest::PaxType::BlankComponent { .. }
383                | pax_manifest::PaxType::Singleton { .. } => {
384                    let settings = self
385                        .updated_property_map
386                        .iter()
387                        .filter_map(|(k, v)| {
388                            v.as_ref()
389                                .map(|value| SettingElement::Setting(k.clone(), value.clone()))
390                        })
391                        .chain(self.updated_classes.iter().filter_map(|(k, v)| {
392                            let is_class = k.starts_with('.');
393                            let key = k.trim_start_matches('.').trim_start_matches('#');
394                            v.then_some(SettingElement::Setting(
395                                Token::new_without_location(
396                                    if is_class { "class" } else { "id" }.to_string(),
397                                ),
398                                ValueDefinition::Identifier(PaxIdentifier {
399                                    name: key.to_string(),
400                                }),
401                            ))
402                        }))
403                        .collect::<Vec<SettingElement>>();
404                    NodeType::Template(settings)
405                }
406                _ => return Err("failed to save node - unsupported type".to_string()),
407            };
408            // Node does not exist
409            let resp = self.orm.execute_command(AddTemplateNodeRequest::new(
410                self.containing_component_type_id,
411                self.node_type_id,
412                node_data,
413                self.location,
414            ))?;
415            self.location = self.orm.manifest.get_node_location(&resp.uni);
416            self.unique_node_identifier = Some(resp.uni);
417            resp.command_id
418        };
419
420        Ok(SaveData {
421            undo_id: id,
422            unique_id: self.unique_node_identifier.expect("exists after save"),
423        })
424    }
425}