melodium_engine/building/treatment/
source.rs

1use crate::building::{
2    BuildId, CheckBuild, CheckBuildResult, CheckEnvironment, CheckStep, ContextualEnvironment,
3    DynamicBuildResult, FeedingInputs, GenesisEnvironment, StaticBuildResult,
4};
5use crate::building::{Builder as BuilderTrait, HostTreatment};
6use crate::error::{LogicError, LogicResult};
7use crate::world::World;
8use core::fmt::Debug;
9use melodium_common::descriptor::{Status, Treatment as TreatmentDescriptor};
10use melodium_common::executive::TrackId;
11use std::collections::HashMap;
12use std::sync::{Arc, RwLock, Weak};
13
14#[derive(Debug)]
15struct BuildSample {
16    host_treatment: HostTreatment,
17    host_build_id: Option<BuildId>,
18    #[allow(unused)]
19    check: Arc<RwLock<CheckBuild>>,
20    label: String,
21}
22
23impl BuildSample {
24    pub fn new(host_treatment: &HostTreatment, host_build: &Option<BuildId>, label: &str) -> Self {
25        Self {
26            host_treatment: host_treatment.clone(),
27            host_build_id: host_build.clone(),
28            check: Arc::new(RwLock::new(CheckBuild::new(
29                host_treatment.host_id().cloned(),
30                label,
31            ))),
32            label: label.to_string(),
33        }
34    }
35}
36
37#[derive(Debug)]
38pub struct Builder {
39    world: Weak<World>,
40
41    descriptor: Weak<dyn TreatmentDescriptor>,
42
43    builds: RwLock<Vec<BuildSample>>,
44    building_inputs: RwLock<HashMap<(BuildId, TrackId), FeedingInputs>>,
45}
46
47impl Builder {
48    pub fn new(world: Weak<World>, descriptor: Weak<dyn TreatmentDescriptor>) -> Self {
49        Self {
50            world,
51            descriptor,
52            builds: RwLock::new(Vec::new()),
53            building_inputs: RwLock::new(HashMap::new()),
54        }
55    }
56}
57
58impl BuilderTrait for Builder {
59    fn static_build(
60        &self,
61        host_treatment: HostTreatment,
62        host_build: Option<BuildId>,
63        label: String,
64        environment: &GenesisEnvironment,
65    ) -> LogicResult<StaticBuildResult> {
66        let world = self.world.upgrade().unwrap();
67        // Make a BuildSample with matching informations
68        let build_sample = BuildSample::new(&host_treatment, &host_build, &label);
69
70        let mut builds_writer = self.builds.write().unwrap();
71        let idx = builds_writer.len() as BuildId;
72
73        let rc_descriptor = self.descriptor.upgrade().unwrap();
74        for (model_name, sources) in rc_descriptor.source_from() {
75            let (_, matching_model) = environment
76                .models()
77                .iter()
78                .find(|(name, _)| name == &model_name)
79                .unwrap();
80
81            for source in sources {
82                world.add_source(
83                    matching_model.id().unwrap(),
84                    source,
85                    self.descriptor.upgrade().unwrap(),
86                    environment.variables().clone(),
87                    idx,
88                );
89            }
90        }
91
92        builds_writer.push(build_sample);
93
94        Status::new_success(StaticBuildResult::Build(idx))
95    }
96
97    fn dynamic_build(
98        &self,
99        build: BuildId,
100        _with_inputs: Vec<String>,
101        environment: &ContextualEnvironment,
102    ) -> Option<DynamicBuildResult> {
103        let world = self.world.upgrade().unwrap();
104
105        // Look for existing build
106        {
107            let borrowed_building_inputs = self.building_inputs.read().unwrap();
108
109            if let Some(existing_building_inputs) =
110                borrowed_building_inputs.get(&(build, environment.track_id()))
111            {
112                let mut dynamic_result = DynamicBuildResult::new();
113                dynamic_result
114                    .feeding_inputs
115                    .extend(existing_building_inputs.clone());
116
117                return Some(dynamic_result);
118            }
119        }
120
121        // Get build
122        let borrowed_builds = self.builds.read().unwrap();
123        let build_sample = borrowed_builds.get(build as usize).unwrap();
124        let descriptor = self.descriptor.upgrade().unwrap();
125
126        let mut result = DynamicBuildResult::new();
127
128        match &build_sample.host_treatment {
129            HostTreatment::Treatment(host_descriptor) => {
130                let host_build = world
131                    .builder(host_descriptor.identifier())
132                    .success()
133                    .unwrap()
134                    .give_next(
135                        build_sample.host_build_id.unwrap(),
136                        build_sample.label.to_string(),
137                        descriptor.outputs().keys().cloned().collect(),
138                        &environment.base_on(),
139                    )
140                    .unwrap();
141
142                result.feeding_inputs = host_build.feeding_inputs;
143                // We add here blocked inputs for source outputs that might not be used in scripts.
144                for (name, _) in descriptor.outputs() {
145                    if !result.feeding_inputs.contains_key(name) {
146                        result
147                            .feeding_inputs
148                            .insert(name.clone(), vec![world.new_blocked_input()]);
149                    }
150                }
151                result.prepared_futures.extend(host_build.prepared_futures);
152            }
153            HostTreatment::Direct => {
154                panic!("Source cannot be directly instancied (nonsense, model missing)")
155            }
156        }
157
158        self.building_inputs.write().unwrap().insert(
159            (build, environment.track_id()),
160            result.feeding_inputs.clone(),
161        );
162
163        Some(result)
164    }
165
166    fn give_next(
167        &self,
168        _within_build: BuildId,
169        _for_label: String,
170        _for_outputs: Vec<String>,
171        _environment: &ContextualEnvironment,
172    ) -> Option<DynamicBuildResult> {
173        // A core treatment cannot have sub-treatments (its not a sequence), so nothing to ever return.
174        None
175    }
176
177    fn check_dynamic_build(
178        &self,
179        build: BuildId,
180        environment: CheckEnvironment,
181        previous_steps: Vec<CheckStep>,
182    ) -> Option<CheckBuildResult> {
183        let world = self.world.upgrade().unwrap();
184        let descriptor = self.descriptor.upgrade().unwrap();
185
186        let mut errors = Vec::new();
187        // Check if we're not in our own previous steps
188        let check_step = CheckStep {
189            identifier: descriptor.identifier().clone(),
190            build_id: build,
191        };
192        if let Some(_existing_check_step) = previous_steps.iter().find(|&cs| cs == &check_step) {
193            errors.push(LogicError::already_included_build_step(
194                58,
195                descriptor.identifier().clone(),
196                check_step.clone(),
197                previous_steps.clone(),
198                None,
199            ));
200        }
201        let mut current_previous_steps = previous_steps.clone();
202        current_previous_steps.push(check_step);
203
204        // Get build
205        let borrowed_builds = self.builds.read().unwrap();
206        let build_sample = borrowed_builds.get(build as usize).unwrap();
207
208        let mut all_builds = Vec::new();
209        if errors.is_empty() {
210            match &build_sample.host_treatment {
211                HostTreatment::Treatment(host_descriptor) => {
212                    let build_result = world
213                        .builder(host_descriptor.identifier())
214                        .success()
215                        .unwrap()
216                        .check_give_next(
217                            build_sample.host_build_id.unwrap(),
218                            build_sample.label.to_string(),
219                            environment.clone(),
220                            current_previous_steps,
221                        )
222                        .unwrap();
223
224                    all_builds.extend(build_result.checked_builds);
225
226                    errors.extend(build_result.errors);
227                }
228                HostTreatment::Direct => {}
229            }
230            all_builds.push(Arc::clone(&build_sample.check));
231        }
232
233        // Return checked build result
234        let own_checked_build_result = CheckBuildResult {
235            checked_builds: all_builds,
236            build: Arc::clone(&build_sample.check),
237            errors,
238        };
239
240        Some(own_checked_build_result)
241    }
242
243    fn check_give_next(
244        &self,
245        _within_build: BuildId,
246        _for_label: String,
247        _environment: CheckEnvironment,
248        _previous_steps: Vec<CheckStep>,
249    ) -> Option<CheckBuildResult> {
250        // A core treatment cannot have sub-treatments (its not a sequence), so nothing to ever return.
251        None
252    }
253}