spin_manifest/
normalize.rs1use std::collections::HashSet;
4
5use crate::schema::v2::{AppManifest, ComponentSpec, KebabId};
6
7pub fn normalize_manifest(manifest: &mut AppManifest) {
12 normalize_trigger_ids(manifest);
13 normalize_inline_components(manifest);
14}
15
16fn normalize_inline_components(manifest: &mut AppManifest) {
17 let components = &mut manifest.components;
19
20 for trigger in manifest.triggers.values_mut().flatten() {
21 let trigger_id = &trigger.id;
22
23 let component_specs = trigger
24 .component
25 .iter_mut()
26 .chain(
27 trigger
28 .components
29 .values_mut()
30 .flat_map(|specs| specs.0.iter_mut()),
31 )
32 .collect::<Vec<_>>();
33 let multiple_components = component_specs.len() > 1;
34
35 let mut counter = 1;
36 for spec in component_specs {
37 if !matches!(spec, ComponentSpec::Inline(_)) {
38 continue;
39 };
40
41 let inline_id = {
42 let mut id = KebabId::try_from(format!("{trigger_id}-component"));
44 if multiple_components
46 || id.is_err()
47 || components.contains_key(id.as_ref().unwrap())
48 {
49 id = Ok(loop {
50 let id = KebabId::try_from(format!("inline-component{counter}")).unwrap();
51 if !components.contains_key(&id) {
52 break id;
53 }
54 counter += 1;
55 });
56 }
57 id.unwrap()
58 };
59
60 let inline_spec = std::mem::replace(spec, ComponentSpec::Reference(inline_id.clone()));
62 let ComponentSpec::Inline(component) = inline_spec else {
63 unreachable!();
64 };
65 components.insert(inline_id.clone(), *component);
67 }
68 }
69}
70
71fn normalize_trigger_ids(manifest: &mut AppManifest) {
72 let mut trigger_ids = manifest
73 .triggers
74 .values()
75 .flatten()
76 .cloned()
77 .map(|t| t.id)
78 .collect::<HashSet<_>>();
79 for (trigger_type, triggers) in &mut manifest.triggers {
80 let mut counter = 1;
81 for trigger in triggers {
82 if !trigger.id.is_empty() {
83 continue;
84 }
85 if let Some(ComponentSpec::Reference(component_id)) = &trigger.component {
87 let candidate_id = format!("{component_id}-{trigger_type}-trigger");
88 if !trigger_ids.contains(&candidate_id) {
89 trigger.id.clone_from(&candidate_id);
90 trigger_ids.insert(candidate_id);
91 continue;
92 }
93 }
94 trigger.id = loop {
96 let id = format!("{trigger_type}-trigger{counter}");
97 if !trigger_ids.contains(&id) {
98 trigger_ids.insert(id.clone());
99 break id;
100 }
101 counter += 1;
102 }
103 }
104 }
105}