pax_designtime/orm/template/
node_builder.rs1use 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
17pub 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 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 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 *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 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 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}