cfgd_core/composition/
engine.rs1use std::collections::HashMap;
2use std::path::PathBuf;
3
4use crate::config::{EnvVar, ProfileLayer, ResolvedProfile, validate_secret_specs};
5use crate::errors::Result;
6
7use super::layers::build_source_layers;
8use super::merge::merge_with_policy;
9use super::{CompositionInput, CompositionResult, ConflictResolution};
10
11pub fn compose(local: &ResolvedProfile, sources: &[CompositionInput]) -> Result<CompositionResult> {
24 let mut all_layers: Vec<ProfileLayer> = local.layers.clone();
25 let mut conflicts: Vec<ConflictResolution> = Vec::new();
26 let mut source_env: HashMap<String, Vec<EnvVar>> = HashMap::new();
27
28 let mut sorted_sources: Vec<&CompositionInput> = sources.iter().collect();
30 sorted_sources.sort_by(|a, b| {
31 a.priority
32 .cmp(&b.priority)
33 .then(a.source_name.cmp(&b.source_name))
34 });
35
36 for input in &sorted_sources {
37 let mut env: Vec<EnvVar> = Vec::new();
39 for layer in &input.layers {
40 crate::merge_env(&mut env, &layer.spec.env);
41 }
42 source_env.insert(input.source_name.clone(), env);
43
44 let source_layers = build_source_layers(input, &mut conflicts)?;
45 all_layers.extend(source_layers);
46 }
47
48 all_layers.sort_by_key(|a| a.priority);
50
51 let mut merged = merge_with_policy(&all_layers, &mut conflicts)?;
52
53 let mut file_origins: HashMap<PathBuf, String> = HashMap::new();
56 for layer in &all_layers {
57 if layer.source != "local"
58 && let Some(ref files) = layer.spec.files
59 {
60 for managed in &files.managed {
61 file_origins.insert(managed.target.clone(), layer.source.clone());
63 }
64 }
65 }
66 for merged_file in &mut merged.files.managed {
67 if merged_file.origin.is_none()
68 && let Some(source) = file_origins.get(&merged_file.target)
69 {
70 merged_file.origin = Some(source.clone());
71 }
72 }
73
74 validate_secret_specs(&merged.secrets)?;
76
77 Ok(CompositionResult {
78 resolved: ResolvedProfile {
79 layers: all_layers,
80 merged,
81 },
82 conflicts,
83 source_env,
84 source_commits: HashMap::new(),
85 })
86}