use super::assignative_element::{AssignativeElement, AssignativeElementType};
use super::assigned_parameter::AssignedParameter;
use super::common::Node;
use super::common::Reference;
use super::declarative_element::{DeclarativeElement, DeclarativeElementType};
use super::declared_parameter::DeclaredParameter;
use super::r#use::Use;
use super::script::Script;
use crate::error::ScriptError;
use crate::path::Path;
use crate::text::Model as TextModel;
use crate::ScriptResult;
use melodium_common::descriptor::{Collection, Entry, Identifier, VersionReq};
use melodium_engine::descriptor::Model as ModelDescriptor;
use melodium_engine::designer::Model as ModelDesigner;
use melodium_engine::LogicError;
use std::collections::HashMap;
use std::sync::{Arc, RwLock, Weak};
#[derive(Debug)]
pub struct Model {
pub text: TextModel,
pub script: Weak<RwLock<Script>>,
pub name: String,
pub parameters: Vec<Arc<RwLock<DeclaredParameter>>>,
pub r#type: RefersTo,
pub assignations: Vec<Arc<RwLock<AssignedParameter>>>,
pub identifier: Option<Identifier>,
pub descriptor: RwLock<Option<Arc<ModelDescriptor>>>,
auto_reference: Weak<RwLock<Self>>,
}
#[derive(Debug)]
pub enum RefersTo {
Unknown(Reference<()>),
Use(Reference<Use>),
Model(Reference<Model>),
}
impl Model {
pub fn new(script: Arc<RwLock<Script>>, text: TextModel) -> ScriptResult<Arc<RwLock<Self>>> {
let model = Arc::<RwLock<Self>>::new_cyclic(|me| {
RwLock::new(Self {
text: text.clone(),
script: Arc::downgrade(&script),
name: text.name.string.clone(),
parameters: Vec::new(),
r#type: RefersTo::Unknown(Reference::new(text.r#type.string.clone())),
assignations: Vec::new(),
identifier: None,
descriptor: RwLock::new(None),
auto_reference: me.clone(),
})
});
let mut result = ScriptResult::new_success(model.clone());
{
let borrowed_script = script.read().unwrap();
let model = borrowed_script.find_model(&text.name.string);
if model.is_some() {
result = result.and_degrade_failure(ScriptResult::new_failure(
ScriptError::already_used_name(122, text.name.clone()),
));
}
let r#use = borrowed_script.find_use(&text.name.string);
if r#use.is_some() {
result = result.and_degrade_failure(ScriptResult::new_failure(
ScriptError::already_used_name(123, text.name),
));
}
}
for p in text.parameters {
if let Some(declared_parameter) = result.merge_degrade_failure(DeclaredParameter::new(
Arc::clone(&model) as Arc<RwLock<dyn DeclarativeElement>>,
p,
)) {
model.write().unwrap().parameters.push(declared_parameter);
}
}
for a in text.assignations {
if let Some(assigned_parameter) = result.merge_degrade_failure(AssignedParameter::new(
Arc::clone(&model) as Arc<RwLock<dyn AssignativeElement>>,
a,
)) {
model.write().unwrap().assignations.push(assigned_parameter);
}
}
result
}
pub fn make_descriptor(&self, collection: &Collection) -> ScriptResult<Arc<ModelDescriptor>> {
let (ref type_identifier, _position) = match &self.r#type {
RefersTo::Model(m) => (
m.reference
.as_ref()
.unwrap()
.upgrade()
.unwrap()
.read()
.unwrap()
.identifier
.as_ref()
.unwrap()
.into(),
m.reference
.as_ref()
.unwrap()
.upgrade()
.unwrap()
.read()
.unwrap()
.text
.name
.position,
),
RefersTo::Use(u) => (
u.reference
.as_ref()
.unwrap()
.upgrade()
.unwrap()
.read()
.unwrap()
.identifier
.as_ref()
.unwrap()
.clone(),
u.reference
.as_ref()
.unwrap()
.upgrade()
.unwrap()
.read()
.unwrap()
.text
.element
.position,
),
_ => {
return ScriptResult::new_failure(ScriptError::reference_unset(
124,
format!("{:?}", &self.r#type),
))
}
};
if let Some(Entry::Model(base_descriptor)) = collection.get(type_identifier) {
let mut result = ScriptResult::new_success(());
let mut descriptor =
ModelDescriptor::new(self.identifier.as_ref().unwrap().clone(), base_descriptor);
if let Some(annotations) = self.text.annotations.as_ref() {
if let Some(doc) = &annotations.doc {
descriptor.set_documentation(&doc.string);
}
for annotation in &annotations.annotations {
if let Some((name, attribute)) = annotation.as_attribute() {
descriptor.add_attribute(name, attribute);
}
}
}
for rc_parameter in &self.parameters {
let borrowed_parameter = rc_parameter.read().unwrap();
if let Some(parameter_descriptor) =
result.merge_degrade_failure(borrowed_parameter.make_descriptor(collection))
{
descriptor.add_parameter(parameter_descriptor);
}
}
result.and_then(|_| {
let descriptor = descriptor.commit();
*self.descriptor.write().unwrap() = Some(descriptor.clone());
ScriptResult::new_success(descriptor)
})
} else {
ScriptResult::new_failure(
LogicError::unexisting_model(
125,
self.identifier.as_ref().unwrap().clone(),
type_identifier.clone(),
Some(self.text.r#type.into_ref()),
)
.into(),
)
}
}
pub fn make_design(&self, collection: &Arc<Collection>) -> ScriptResult<()> {
let borrowed_descriptor = self.descriptor.read().unwrap();
let descriptor = if let Some(descriptor) = &*borrowed_descriptor {
descriptor
} else {
return ScriptResult::new_failure(ScriptError::no_descriptor(
126,
self.text.name.clone(),
));
};
let mut result = ScriptResult::new_success(());
let rc_designer: Arc<RwLock<ModelDesigner>> = if let Some(designer) = result
.merge_degrade_failure(ScriptResult::from(
descriptor.designer(Arc::clone(collection), Some(self.text.name.into_ref())),
)) {
designer
} else {
return result;
};
for rc_assignation in &self.assignations {
let borrowed_assignation = rc_assignation.read().unwrap();
let tmp_status = rc_designer.write().unwrap().add_parameter(
&borrowed_assignation.name,
Some(borrowed_assignation.text.name.into_ref()),
);
if let Some(assignation_designer) =
result.merge_degrade_failure(ScriptResult::from(tmp_status))
{
result = result.and_degrade_failure(
borrowed_assignation.make_design(&assignation_designer, collection),
);
}
}
result = result.and_degrade_failure(ScriptResult::from(descriptor.commit_design()));
result
}
}
impl DeclarativeElement for Model {
fn declarative_element(&'_ self) -> DeclarativeElementType<'_> {
DeclarativeElementType::Model(&self)
}
fn find_declared_parameter(&self, name: &str) -> Option<&Arc<RwLock<DeclaredParameter>>> {
self.parameters
.iter()
.find(|&p| p.read().unwrap().name == name)
}
}
impl AssignativeElement for Model {
fn assignative_element(&'_ self) -> AssignativeElementType<'_> {
AssignativeElementType::Model(&self)
}
fn associated_declarative_element(&self) -> Arc<RwLock<dyn DeclarativeElement>> {
self.auto_reference.upgrade().unwrap()
}
fn find_assigned_parameter(&self, name: &str) -> Option<&Arc<RwLock<AssignedParameter>>> {
self.assignations
.iter()
.find(|&a| a.read().unwrap().name == name)
}
}
impl Node for Model {
fn children(&self) -> Vec<Arc<RwLock<dyn Node>>> {
let mut children: Vec<Arc<RwLock<dyn Node>>> = Vec::new();
self.parameters
.iter()
.for_each(|p| children.push(Arc::clone(&p) as Arc<RwLock<dyn Node>>));
self.assignations
.iter()
.for_each(|a| children.push(Arc::clone(&a) as Arc<RwLock<dyn Node>>));
children
}
fn make_references(
&mut self,
path: &Path,
_versions: &HashMap<String, VersionReq>,
) -> ScriptResult<()> {
if let RefersTo::Unknown(r#type) = &self.r#type {
let rc_script = self.script.upgrade().unwrap();
let borrowed_script = rc_script.read().unwrap();
if let Some(model) = borrowed_script.find_model(&r#type.name) {
self.r#type = RefersTo::Model(Reference {
name: r#type.name.clone(),
reference: Some(Arc::downgrade(model)),
});
} else if let Some(r#use) = borrowed_script.find_use(&r#type.name) {
self.r#type = RefersTo::Use(Reference {
name: r#type.name.clone(),
reference: Some(Arc::downgrade(r#use)),
});
} else {
return ScriptResult::new_failure(ScriptError::unimported_element(
127,
self.text.name.clone(),
));
}
self.identifier = path.to_identifier(&self.name);
}
ScriptResult::new_success(())
}
}