use super::{Parameter, Reference, Scope, Treatment, Value};
use crate::design::ModelInstanciation as ModelInstanciationDesign;
use crate::error::{LogicError, LogicResult};
use core::fmt::Debug;
use melodium_common::descriptor::{
Attribuable, Attribute, Attributes, Collection, Identified, Identifier,
Model as ModelDescriptor, Parameter as ParameterDescriptor, Treatment as TreatmentDescriptor,
Variability,
};
use std::collections::HashMap;
use std::sync::{Arc, RwLock, Weak};
#[derive(Debug)]
pub struct ModelInstanciation {
host_descriptor: Weak<dyn TreatmentDescriptor>,
host_treatment: Weak<RwLock<Treatment>>,
host_id: Identifier,
descriptor: Weak<dyn ModelDescriptor>,
name: String,
parameters: HashMap<String, Arc<RwLock<Parameter>>>,
attributes: Attributes,
design_reference: Option<Arc<dyn Reference>>,
_auto_reference: Weak<RwLock<Self>>,
}
impl ModelInstanciation {
pub fn new(
host_descriptor: &Arc<dyn TreatmentDescriptor>,
host_treatment: &Arc<RwLock<Treatment>>,
host_id: Identifier,
descriptor: &Arc<dyn ModelDescriptor>,
name: &str,
design_reference: Option<Arc<dyn Reference>>,
) -> Arc<RwLock<Self>> {
Arc::<RwLock<Self>>::new_cyclic(|me| {
RwLock::new(Self {
host_descriptor: Arc::downgrade(host_descriptor),
host_treatment: Arc::downgrade(host_treatment),
host_id,
descriptor: Arc::downgrade(descriptor),
name: name.to_string(),
parameters: HashMap::with_capacity(descriptor.parameters().len()),
attributes: Attributes::default(),
design_reference,
_auto_reference: me.clone(),
})
})
}
pub fn descriptor(&self) -> Arc<dyn ModelDescriptor> {
self.descriptor.upgrade().unwrap()
}
pub fn design_reference(&self) -> &Option<Arc<dyn Reference>> {
&self.design_reference
}
pub(crate) fn import_design(
&mut self,
design: &ModelInstanciationDesign,
collection: &Arc<Collection>,
replace: &HashMap<Identifier, Identifier>,
) -> LogicResult<()> {
let mut result = LogicResult::new_success(());
for (name, parameter_design) in &design.parameters {
if let Some(parameter) = result
.merge_degrade_failure(self.add_parameter(name, self.design_reference.clone()))
{
result.merge_degrade_failure(parameter.write().unwrap().import_design(
parameter_design,
collection,
replace,
));
}
}
result
}
pub fn name(&self) -> &str {
&self.name
}
pub(super) fn set_name(&mut self, name: String) {
self.name = name;
}
pub fn add_attribute(&mut self, name: String, attribute: Attribute) {
self.attributes.insert(name, attribute);
}
pub fn remove_attribute(&mut self, name: &str) -> bool {
match self.attributes.remove(name) {
Some(_) => true,
None => false,
}
}
pub fn add_parameter(
&mut self,
name: &str,
design_reference: Option<Arc<dyn Reference>>,
) -> LogicResult<Arc<RwLock<Parameter>>> {
let mut result = LogicResult::new_success(());
let host_descriptor = self.host_descriptor.upgrade().unwrap();
let parameter = Parameter::new(
&(self.host_treatment.upgrade().unwrap() as Arc<RwLock<dyn Scope>>),
&host_descriptor.as_parameterized(),
&Arc::new(RwLock::new(HashMap::new())),
self.host_id.clone(),
&self.descriptor().as_parameterized(),
&Arc::new(RwLock::new(HashMap::new())),
name,
design_reference.clone(),
);
let rc_parameter = Arc::new(RwLock::new(parameter));
if self
.parameters
.insert(name.to_string(), Arc::clone(&rc_parameter))
.is_some()
{
result = result.and_degrade_failure(LogicResult::new_failure(
LogicError::multiple_parameter_assignation(
24,
self.host_id.clone(),
self.descriptor().identifier().clone(),
name.to_string(),
design_reference.clone(),
),
));
}
if !self.descriptor().parameters().contains_key(name) {
result.errors_mut().push(LogicError::unexisting_parameter(
10,
self.host_id.clone(),
self.descriptor().identifier().clone(),
self.name.clone(),
design_reference,
));
}
result.and(Ok(rc_parameter).into())
}
pub fn remove_parameter(&mut self, name: &str) -> LogicResult<bool> {
if let Some(_) = self.parameters.remove(name) {
Ok(true).into()
} else {
Ok(false).into()
}
}
pub fn parameters(&self) -> &HashMap<String, Arc<RwLock<Parameter>>> {
&self.parameters
}
pub fn validate(&self) -> LogicResult<()> {
let mut result = LogicResult::new_success(());
let rc_host = self.host_treatment.upgrade().unwrap();
let host = rc_host.read().unwrap();
let descriptor = self.descriptor();
result = self
.parameters
.iter()
.fold(result, |mut result, (name, param)| {
if !self.descriptor().parameters().contains_key(name) {
result.errors_mut().push(LogicError::unexisting_parameter(
193,
host.identifier().clone(),
descriptor.identifier().clone(),
self.name.clone(),
self.design_reference.clone(),
));
}
result.and_degrade_failure(param.read().unwrap().validate())
});
let unset_params: Vec<&ParameterDescriptor> = descriptor
.parameters()
.iter()
.filter_map(|(core_param_name, core_param)| {
if self.parameters.contains_key(core_param_name) {
None
} else if core_param.default().is_some() {
None
} else {
Some(core_param)
}
})
.collect();
for unset_param in unset_params {
result.errors_mut().push(LogicError::unset_parameter(
22,
host.descriptor().identifier().clone(),
descriptor.identifier().clone(),
unset_param.name().to_string(),
self.design_reference.clone(),
));
}
if result.has_errors() || result.is_failure() {
return result;
}
for (name, param) in self.parameters.iter().filter(|&(_param_name, param)| {
matches!(param.read().unwrap().value(), Some(Value::Context { .. }))
}) {
result.errors_mut().push(LogicError::no_context(
28,
host.descriptor().identifier().clone(),
descriptor.identifier().clone(),
self.name.clone(),
name.to_string(),
param.read().unwrap().design_reference().clone(),
));
}
for (name, param) in self.parameters.iter().filter(|&(param_name, param)| {
*param
.read()
.unwrap()
.parent_descriptor()
.upgrade()
.unwrap()
.parameters()
.get(param_name)
.unwrap()
.variability()
!= Variability::Const
}) {
result
.errors_mut()
.push(LogicError::model_instanciation_const_only(
62,
host.descriptor().identifier().clone(),
descriptor.identifier().clone(),
self.name.clone(),
name.to_string(),
param.read().unwrap().design_reference().clone(),
));
}
result
}
}
impl Attribuable for ModelInstanciation {
fn attributes(&self) -> &Attributes {
&self.attributes
}
}