Skip to main content

melodium_share/
treatment.rs

1use super::{
2    Attributes, Generic, Identifier, Input, Output, Parameter, SharingError, SharingResult,
3    TreatmentDesign,
4};
5use melodium_common::descriptor::{
6    Collection, Entry as CommonEntry, Identifier as CommonIdentifier, Treatment as CommonTreatment,
7};
8use melodium_engine::{descriptor::Treatment as DesignedTreatment, LogicError};
9use serde::{Deserialize, Serialize};
10use std::{
11    collections::{BTreeMap, HashMap},
12    sync::Arc,
13};
14
15#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
16#[cfg_attr(feature = "webassembly", derive(tsify::Tsify))]
17#[cfg_attr(feature = "webassembly", tsify(into_wasm_abi, from_wasm_abi))]
18pub struct Treatment {
19    pub identifier: Identifier,
20    pub documentation: String,
21    pub generics: BTreeMap<String, Generic>,
22    pub parameters: BTreeMap<String, Parameter>,
23    pub implementation_kind: TreatmentImplementationKind,
24    pub inputs: BTreeMap<String, Input>,
25    pub outputs: BTreeMap<String, Output>,
26    pub models: BTreeMap<String, Identifier>,
27    pub contexts: BTreeMap<String, Identifier>,
28    pub attributes: Attributes,
29}
30
31impl Treatment {
32    pub fn make_descriptor(
33        &self,
34        collection: &Collection,
35    ) -> SharingResult<Arc<DesignedTreatment>> {
36        let identifier = if let Ok(identifier) = (&self.identifier).try_into() {
37            identifier
38        } else {
39            return SharingResult::new_failure(SharingError::invalid_identifier(
40                11,
41                self.identifier.clone(),
42            ));
43        };
44
45        if self.implementation_kind.is_compiled() {
46            return SharingResult::new_failure(SharingError::compiled_treatment(12, identifier));
47        }
48
49        let mut result = SharingResult::new_success(());
50        let mut descriptor = DesignedTreatment::new(identifier.clone());
51
52        descriptor.set_documentation(&self.documentation);
53
54        for (name, attribute) in &self.attributes.0 {
55            descriptor.add_attribute(name.clone(), attribute.clone());
56        }
57
58        for (_, generic) in &self.generics {
59            descriptor.add_generic(generic.into());
60        }
61
62        for (name, model) in &self.models {
63            let model_identifier: CommonIdentifier = if let Ok(identifier) = model.try_into() {
64                identifier
65            } else {
66                return SharingResult::new_failure(SharingError::invalid_identifier(
67                    12,
68                    self.identifier.clone(),
69                ));
70            };
71
72            if let Some(CommonEntry::Model(model)) = collection.get(&(&model_identifier).into()) {
73                descriptor.add_model(name, model);
74            } else {
75                result = result.and(SharingResult::new_failure(
76                    LogicError::unexisting_model(
77                        236,
78                        identifier.clone(),
79                        model_identifier.into(),
80                        None,
81                    )
82                    .into(),
83                ));
84            }
85        }
86
87        for (_, context) in &self.contexts {
88            let context_identifier: CommonIdentifier = if let Ok(identifier) = context.try_into() {
89                identifier
90            } else {
91                return SharingResult::new_failure(SharingError::invalid_identifier(
92                    13,
93                    self.identifier.clone(),
94                ));
95            };
96
97            if let Some(CommonEntry::Context(context)) =
98                collection.get(&(&context_identifier).into())
99            {
100                descriptor.add_context(context);
101            } else {
102                result = result.and(SharingResult::new_failure(
103                    LogicError::unexisting_context(
104                        237,
105                        identifier.clone(),
106                        context_identifier.into(),
107                        None,
108                    )
109                    .into(),
110                ));
111            }
112        }
113
114        for (_, param) in &self.parameters {
115            if let Some(parameter) =
116                result.merge_degrade_failure(param.to_parameter(collection, &identifier))
117            {
118                descriptor.add_parameter(parameter);
119            }
120        }
121
122        for (_, input) in &self.inputs {
123            if let Some(input) =
124                result.merge_degrade_failure(input.to_input(collection, &identifier))
125            {
126                descriptor.add_input(input);
127            }
128        }
129
130        for (_, output) in &self.outputs {
131            if let Some(output) =
132                result.merge_degrade_failure(output.to_output(collection, &identifier))
133            {
134                descriptor.add_output(output);
135            }
136        }
137
138        result.and(SharingResult::new_success(descriptor.commit()))
139    }
140
141    pub fn make_design(&self, collection: &Arc<Collection>) -> SharingResult<()> {
142        let identifier = if let Ok(identifier) = (&self.identifier).try_into() {
143            identifier
144        } else {
145            return SharingResult::new_failure(SharingError::invalid_identifier(
146                14,
147                self.identifier.clone(),
148            ));
149        };
150
151        let design = if let TreatmentImplementationKind::Designed(Some(design)) =
152            &self.implementation_kind
153        {
154            design
155        } else {
156            return SharingResult::new_failure(SharingError::no_treatment_design_available(
157                15, identifier,
158            ));
159        };
160
161        let descriptor = if let Some(CommonEntry::Treatment(treatment)) =
162            collection.get(&(&identifier).into())
163        {
164            if let Ok(treatment) = treatment.clone().downcast_arc::<DesignedTreatment>() {
165                treatment
166            } else {
167                return SharingResult::new_failure(
168                    LogicError::unexisting_treatment(
169                        238,
170                        identifier.clone(),
171                        identifier.into(),
172                        None,
173                    )
174                    .into(),
175                );
176            }
177        } else {
178            return SharingResult::new_failure(
179                LogicError::unexisting_treatment(239, identifier.clone(), identifier.into(), None)
180                    .into(),
181            );
182        };
183
184        let designer = match descriptor.designer(Arc::clone(collection), None) {
185            melodium_common::descriptor::Status::Success { success, errors: _ } => success,
186            melodium_common::descriptor::Status::Failure { failure, errors: _ } => {
187                return SharingResult::new_failure(failure.into())
188            }
189        };
190
191        design
192            .make_design(collection, &descriptor)
193            .and_then(|design| {
194                SharingResult::from(designer.write().unwrap().import_design(
195                    &design,
196                    &HashMap::new(),
197                    None,
198                ))
199            })
200            .and_then(|()| SharingResult::from(descriptor.commit_design()))
201    }
202}
203
204impl From<&Arc<dyn CommonTreatment>> for Treatment {
205    fn from(value: &Arc<dyn CommonTreatment>) -> Self {
206        Self {
207            identifier: Identifier::from(value.identifier()),
208            documentation: value.documentation().to_string(),
209            generics: value
210                .generics()
211                .iter()
212                .map(|g| (g.name.clone(), g.into()))
213                .collect(),
214            parameters: value
215                .parameters()
216                .iter()
217                .map(|(name, param)| (name.clone(), Parameter::from(param)))
218                .collect(),
219            implementation_kind: match value.build_mode() {
220                melodium_common::descriptor::TreatmentBuildMode::Compiled(_, _)
221                | melodium_common::descriptor::TreatmentBuildMode::Source(_) => {
222                    TreatmentImplementationKind::Compiled
223                }
224                melodium_common::descriptor::TreatmentBuildMode::Designed() => {
225                    TreatmentImplementationKind::Designed(
226                        value
227                            .clone()
228                            .downcast_arc::<DesignedTreatment>()
229                            .unwrap()
230                            .design()
231                            .success()
232                            .map(|design| design.as_ref().into()),
233                    )
234                }
235            },
236            inputs: value
237                .inputs()
238                .iter()
239                .map(|(name, input)| (name.clone(), Input::from(input)))
240                .collect(),
241            outputs: value
242                .outputs()
243                .iter()
244                .map(|(name, output)| (name.clone(), Output::from(output)))
245                .collect(),
246            models: value
247                .models()
248                .iter()
249                .map(|(name, model)| (name.clone(), Identifier::from(model.identifier())))
250                .collect(),
251            contexts: value
252                .contexts()
253                .iter()
254                .map(|(name, context)| (name.clone(), Identifier::from(context.identifier())))
255                .collect(),
256            attributes: value.attributes().into(),
257        }
258    }
259}
260
261#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
262#[serde(rename_all = "snake_case")]
263#[cfg_attr(feature = "webassembly", derive(tsify::Tsify))]
264#[cfg_attr(feature = "webassembly", tsify(into_wasm_abi, from_wasm_abi))]
265pub enum TreatmentImplementationKind {
266    Compiled,
267    Designed(Option<TreatmentDesign>),
268}
269
270impl TreatmentImplementationKind {
271    pub fn is_compiled(&self) -> bool {
272        match self {
273            TreatmentImplementationKind::Compiled => true,
274            _ => false,
275        }
276    }
277}