use super::{Parameter, Reference, Scope, Value};
use crate::descriptor::Model as ModelDescriptor;
use crate::design::{Model as ModelDesign, Parameter as ParameterDesign};
use crate::error::{LogicError, LogicResult};
use core::fmt::Debug;
use melodium_common::descriptor::{
Collection, Identified, Identifier, Model as ModelTrait, Parameter as ParameterDescriptor,
Parameterized,
};
use std::collections::HashMap;
use std::sync::{Arc, RwLock, Weak};
#[derive(Debug)]
pub struct Model {
collection: Arc<Collection>,
descriptor: Weak<ModelDescriptor>,
parameters: HashMap<String, Arc<RwLock<Parameter>>>,
design_reference: Option<Arc<dyn Reference>>,
auto_reference: Weak<RwLock<Self>>,
}
impl Model {
pub fn new(
descriptor: &Arc<ModelDescriptor>,
collection: Arc<Collection>,
design_reference: Option<Arc<dyn Reference>>,
) -> Arc<RwLock<Self>> {
Arc::<RwLock<Self>>::new_cyclic(|me| {
RwLock::new(Self {
descriptor: Arc::downgrade(descriptor),
collection,
parameters: HashMap::new(),
design_reference,
auto_reference: me.clone(),
})
})
}
pub fn update_collection(&mut self, collection: Arc<Collection>) -> LogicResult<()> {
self.collection = collection;
let mut result = LogicResult::new_success(());
let mut deletion_list = Vec::new();
for (name, param) in &self.parameters {
let res = param.write().unwrap().update_collection(&self.collection);
if res.is_failure() {
deletion_list.push(name.clone());
}
result = result.and_degrade_failure(res);
}
deletion_list.iter().for_each(|d| {
self.remove_parameter(d);
});
result
}
pub fn collection(&self) -> &Arc<Collection> {
&self.collection
}
pub fn descriptor(&self) -> Arc<ModelDescriptor> {
self.descriptor.upgrade().unwrap()
}
pub fn design_reference(&self) -> &Option<Arc<dyn Reference>> {
&self.design_reference
}
pub fn add_parameter(
&mut self,
name: &str,
design_reference: Option<Arc<dyn Reference>>,
) -> LogicResult<Arc<RwLock<Parameter>>> {
let base_model = self
.descriptor()
.base_model()
.expect("Designed model must have base model");
if base_model.parameters().contains_key(name) {
let parameter = Parameter::new(
&(self.auto_reference.upgrade().unwrap() as Arc<RwLock<dyn Scope>>),
&base_model.as_parameterized(),
name,
design_reference.clone(),
);
let rc_parameter = Arc::new(RwLock::new(parameter));
if self
.parameters
.insert(name.to_string(), Arc::clone(&rc_parameter))
.is_none()
{
Ok(rc_parameter).into()
} else {
Err(LogicError::multiple_parameter_assignation(
25,
self.descriptor().identifier().clone(),
base_model.identifier().clone(),
name.to_string(),
design_reference,
)
.into())
.into()
}
} else {
Err(LogicError::unexisting_parameter(
12,
self.descriptor().identifier().clone(),
base_model.identifier().clone(),
name.to_string(),
design_reference,
)
.into())
.into()
}
}
pub fn remove_parameter(&mut self, name: &str) -> LogicResult<bool> {
Ok(match self.parameters.remove(name) {
Some(_) => true,
None => false,
})
.into()
}
pub fn parameters(&self) -> &HashMap<String, Arc<RwLock<Parameter>>> {
&self.parameters
}
pub fn validate(&self) -> LogicResult<()> {
let mut result = LogicResult::new_success(());
result = self.parameters.iter().fold(result, |result, (_, param)| {
result.and_degrade_failure(param.read().unwrap().validate())
});
let rc_base_model = self
.descriptor()
.base_model()
.expect("Designed model must have base model");
let unset_params: Vec<&ParameterDescriptor> = rc_base_model
.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(
21,
self.descriptor().identifier().clone(),
rc_base_model.identifier().clone(),
unset_param.name().to_string(),
self.design_reference.clone(),
));
}
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(
29,
self.descriptor().identifier().clone(),
rc_base_model.identifier().clone(),
rc_base_model.identifier().name().to_string(),
name.to_string(),
param.read().unwrap().design_reference().clone(),
));
}
result
}
pub fn design(&self) -> LogicResult<ModelDesign> {
let result = self.validate();
result.and_then(|_| {
LogicResult::new_success(ModelDesign {
descriptor: self.descriptor.clone(),
parameters: self
.parameters
.iter()
.map(|(name, param)| {
(
name.clone(),
ParameterDesign {
name: name.clone(),
value: param.read().unwrap().value().as_ref().unwrap().clone(),
},
)
})
.collect(),
})
})
}
}
impl Scope for Model {
fn descriptor(&self) -> Arc<dyn Parameterized> {
Arc::clone(&self.descriptor()) as Arc<dyn Parameterized>
}
fn collection(&self) -> Arc<Collection> {
Arc::clone(&self.collection)
}
fn identifier(&self) -> Identifier {
self.descriptor().identifier().clone()
}
}