use super::assignative_element::{AssignativeElement, AssignativeElementType};
use super::assigned_model::AssignedModel;
use super::assigned_parameter::AssignedParameter;
use super::common::Node;
use super::common::Reference;
use super::declarative_element::DeclarativeElement;
use super::r#use::Use;
use super::treatment::Treatment;
use super::AssignedGeneric;
use crate::error::ScriptError;
use crate::path::Path;
use crate::text::Instanciation as TextTreatment;
use crate::ScriptResult;
use melodium_common::descriptor::{Collection, IdentifierRequirement, VersionReq};
use melodium_engine::designer::{
GenericInstanciation, TreatmentInstanciation as TreatmentInstanciationDesigner,
};
use std::collections::HashMap;
use std::sync::{Arc, RwLock, Weak};
#[derive(Debug)]
pub struct TreatmentInstanciation {
pub text: TextTreatment,
pub treatment: Weak<RwLock<Treatment>>,
pub name: String,
pub r#type: RefersTo,
pub generics: Vec<Arc<RwLock<AssignedGeneric>>>,
pub models: Vec<Arc<RwLock<AssignedModel>>>,
pub parameters: Vec<Arc<RwLock<AssignedParameter>>>,
pub type_identifier: Option<IdentifierRequirement>,
}
#[derive(Debug)]
pub enum RefersTo {
Unknown(Reference<()>),
Use(Reference<Use>),
Treatment(Reference<Treatment>),
}
impl TreatmentInstanciation {
pub fn new(
treatment: Arc<RwLock<Treatment>>,
text: TextTreatment,
) -> ScriptResult<Arc<RwLock<Self>>> {
let treatment_instanciation = Arc::<RwLock<Self>>::new(RwLock::new(Self {
text: text.clone(),
treatment: Arc::downgrade(&treatment),
name: text.name.string.clone(),
r#type: RefersTo::Unknown(Reference::new(text.r#type.string)),
generics: Vec::new(),
models: Vec::new(),
parameters: Vec::new(),
type_identifier: None,
}));
let mut result = ScriptResult::new_success(Arc::clone(&treatment_instanciation));
{
let borrowed_treatment = treatment.read().unwrap();
if let Some(_) = borrowed_treatment.find_treatment_instanciation(&text.name.string) {
result = result.and_degrade_failure(ScriptResult::new_failure(
ScriptError::already_used_name(116, text.name),
));
}
}
for generic in &text.generics {
if let Some(generic) = result.merge_degrade_failure(AssignedGeneric::new(
Arc::clone(&treatment) as Arc<RwLock<dyn DeclarativeElement>>,
generic.clone(),
)) {
treatment_instanciation
.write()
.unwrap()
.generics
.push(generic);
}
}
for m in text.configuration {
if let Some(assigned_model) = result.merge_degrade_failure(AssignedModel::new(
Arc::clone(&treatment_instanciation) as Arc<RwLock<dyn AssignativeElement>>,
m,
)) {
treatment_instanciation
.write()
.unwrap()
.models
.push(assigned_model);
}
}
for p in text.parameters {
if let Some(assigned_parameter) = result.merge_degrade_failure(AssignedParameter::new(
Arc::clone(&treatment_instanciation) as Arc<RwLock<dyn AssignativeElement>>,
p,
)) {
treatment_instanciation
.write()
.unwrap()
.parameters
.push(assigned_parameter);
}
}
result
}
pub fn make_design(
&self,
designer: &Arc<RwLock<TreatmentInstanciationDesigner>>,
collection: &Arc<Collection>,
) -> ScriptResult<()> {
let mut designer = designer.write().unwrap();
let mut result = ScriptResult::new_success(());
let treatment_desc = designer.descriptor();
for i in 0..treatment_desc.generics().len() {
let desc_generic = &treatment_desc.generics()[i];
if let Some(rc_generic) = self.generics.get(i) {
let borrowed_generic = rc_generic.read().unwrap();
let borrwed_type = borrowed_generic.r#type.read().unwrap();
if let Some((r#type, _)) =
result.merge_degrade_failure(borrwed_type.make_descriptor(collection))
{
result = result.and_degrade_failure(ScriptResult::from(
designer.set_generic(desc_generic.name.clone(), r#type),
));
}
} else {
result = result.and_degrade_failure(ScriptResult::new_failure(
ScriptError::missing_function_generic(173, self.text.name.clone(), i),
));
}
}
for rc_model_assignation in &self.models {
let borrowed_model_assignation = rc_model_assignation.read().unwrap();
result = result.and_degrade_failure(ScriptResult::from(designer.add_model(
&borrowed_model_assignation.name,
&borrowed_model_assignation.model.name,
)));
}
for rc_param_assignation in &self.parameters {
let borrowed_param_assignation = rc_param_assignation.read().unwrap();
if let Some(param_assignation_designer) =
result.merge_degrade_failure(ScriptResult::from(designer.add_parameter(
&borrowed_param_assignation.name,
Some(borrowed_param_assignation.text.name.into_ref()),
)))
{
result = result.and_degrade_failure(
borrowed_param_assignation.make_design(¶m_assignation_designer, collection),
);
}
}
result.and_degrade_failure(ScriptResult::from(designer.validate()))
}
}
impl AssignativeElement for TreatmentInstanciation {
fn assignative_element(&'_ self) -> AssignativeElementType<'_> {
AssignativeElementType::Treatment(self)
}
fn associated_declarative_element(&self) -> Arc<RwLock<dyn DeclarativeElement>> {
self.treatment.upgrade().unwrap() as Arc<RwLock<dyn DeclarativeElement>>
}
fn find_assigned_model(&self, name: &str) -> Option<&Arc<RwLock<AssignedModel>>> {
self.models.iter().find(|&m| m.read().unwrap().name == name)
}
fn find_assigned_parameter(&self, name: &str) -> Option<&Arc<RwLock<AssignedParameter>>> {
self.parameters
.iter()
.find(|&a| a.read().unwrap().name == name)
}
}
impl Node for TreatmentInstanciation {
fn children(&self) -> Vec<Arc<RwLock<dyn Node>>> {
let mut children: Vec<Arc<RwLock<dyn Node>>> = Vec::new();
self.generics
.iter()
.for_each(|g| children.push(Arc::clone(&g) as Arc<RwLock<dyn Node>>));
self.models
.iter()
.for_each(|m| children.push(Arc::clone(&m) as Arc<RwLock<dyn Node>>));
self.parameters
.iter()
.for_each(|p| children.push(Arc::clone(&p) as Arc<RwLock<dyn Node>>));
children
}
fn make_references(
&mut self,
path: &Path,
_versions: &HashMap<String, VersionReq>,
) -> ScriptResult<()> {
if let RefersTo::Unknown(reference) = &self.r#type {
let rc_treatment = self.treatment.upgrade().unwrap();
let borrowed_treatment = rc_treatment.read().unwrap();
let rc_script = borrowed_treatment.script.upgrade().unwrap();
let borrowed_script = rc_script.read().unwrap();
let r#use = borrowed_script.find_use(&reference.name);
if r#use.is_some() {
let r#use = r#use.unwrap();
self.type_identifier = r#use.read().unwrap().identifier.as_ref().cloned();
self.r#type = RefersTo::Use(Reference {
name: reference.name.clone(),
reference: Some(Arc::downgrade(r#use)),
});
} else {
let treatment = borrowed_script.find_treatment(&reference.name);
if treatment.is_some() {
self.type_identifier = path.to_identifier_requirement(&reference.name);
self.r#type = RefersTo::Treatment(Reference {
name: reference.name.clone(),
reference: Some(Arc::downgrade(treatment.unwrap())),
});
} else {
return ScriptResult::new_failure(ScriptError::unimported_element(
117,
self.text.r#type.clone(),
));
}
}
}
ScriptResult::new_success(())
}
}