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}