use crate::design::Model as Design;
use crate::designer::{Model as Designer, Reference};
use crate::error::{LogicError, LogicResult};
use core::fmt::{Display, Formatter, Result as FmtResult};
use melodium_common::descriptor::{
Attribuable, Attribute, Attributes, Buildable, Collection, Context, Documented, Entry, Generic,
Generics, Identified, Identifier, Model as ModelDescriptor, ModelBuildMode, Parameter,
Parameterized, Status, Variability,
};
use std::collections::HashMap;
use std::sync::{Arc, Mutex, OnceLock, RwLock, Weak};
#[derive(Debug)]
pub struct Model {
identifier: Identifier,
#[cfg(feature = "doc")]
documentation: String,
attributes: Attributes,
base_model: Arc<dyn ModelDescriptor>,
parameters: HashMap<String, Parameter>,
designer: Mutex<Option<Arc<RwLock<Designer>>>>,
design: Mutex<Option<Arc<Design>>>,
auto_reference: Weak<Self>,
}
impl Model {
pub fn new(identifier: Identifier, base_model: &Arc<dyn ModelDescriptor>) -> Self {
Self {
identifier,
#[cfg(feature = "doc")]
documentation: String::new(),
attributes: Attributes::default(),
base_model: Arc::clone(base_model),
parameters: HashMap::new(),
designer: Mutex::new(None),
design: Mutex::new(None),
auto_reference: Weak::default(),
}
}
pub fn set_identifier(&mut self, identifier: Identifier) {
self.identifier = identifier;
}
pub fn reset_designer(&self) {
let mut option_designer = self.designer.lock().expect("Mutex poisoned");
*option_designer = None;
}
pub fn designer(
&self,
collection: Arc<Collection>,
design_reference: Option<Arc<dyn Reference>>,
) -> LogicResult<Arc<RwLock<Designer>>> {
if self.auto_reference.strong_count() == 0 {
return Status::new_failure(
LogicError::uncommited_descriptor(2, self.identifier.clone(), design_reference)
.into(),
);
}
let mut option_designer = self.designer.lock().expect("Mutex poisoned");
if let Some(designer_ref) = &*option_designer {
Status::new_success(designer_ref.clone())
} else {
let new_designer = Designer::new(
&self.auto_reference.upgrade().unwrap(),
collection,
design_reference,
);
*option_designer = Some(new_designer.clone());
Status::new_success(new_designer)
}
}
pub fn commit_design(&self) -> LogicResult<()> {
let option_designer = self.designer.lock().expect("Mutex poisoned");
let mut option_design = self.design.lock().expect("Mutex poisoned");
if let Some(designer_ref) = &*option_designer {
let designer = designer_ref.read().unwrap();
let mut result_design = designer.design();
if result_design.is_success() && result_design.has_errors() {
result_design =
result_design.and(Status::new_failure(LogicError::erroneous_design(
66,
self.identifier.clone(),
designer.design_reference().clone(),
)));
}
result_design.and_then(|design| {
*option_design = Some(Arc::new(design));
Status::new_success(())
})
} else {
Status::new_failure(LogicError::no_designer(65, self.identifier.clone(), None))
}
}
pub fn design(&self) -> LogicResult<Arc<Design>> {
let option_design = self.design.lock().expect("Mutex poisoned");
option_design
.as_ref()
.map(|design| Arc::clone(design))
.ok_or_else(|| LogicError::unavailable_design(4, self.identifier.clone(), None).into())
.into()
}
pub fn update_with_collection(
&mut self,
collection: &Collection,
replace: &HashMap<Identifier, Identifier>,
) -> LogicResult<()> {
let base_identifier = replace
.get(self.base_model.identifier())
.unwrap_or_else(|| self.base_model.identifier());
if let Some(Entry::Model(base_model)) = collection.get(&base_identifier.into()) {
self.base_model = base_model.clone();
LogicResult::new_success(())
} else {
LogicResult::new_failure(LogicError::unexisting_model(
208,
self.identifier.clone(),
base_identifier.into(),
None,
))
}
}
pub fn set_documentation(&mut self, documentation: &str) {
#[cfg(feature = "doc")]
{
self.documentation = String::from(documentation);
}
#[cfg(not(feature = "doc"))]
let _ = documentation;
}
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, mut parameter: Parameter) {
if parameter.variability() != &Variability::Const {
parameter = Parameter::new(
parameter.name(),
Variability::Const,
parameter.described_type().clone(),
parameter.default().clone(),
parameter.attributes().clone(),
);
}
self.parameters
.insert(parameter.name().to_string(), parameter);
}
pub fn remove_parameter(&mut self, name: &str) -> bool {
match self.parameters.remove(name) {
Some(_) => true,
None => false,
}
}
pub fn commit(self) -> Arc<Self> {
Arc::new_cyclic(|me| Self {
identifier: self.identifier,
#[cfg(feature = "doc")]
documentation: self.documentation,
attributes: self.attributes,
base_model: self.base_model,
parameters: self.parameters,
designer: self.designer,
design: self.design,
auto_reference: me.clone(),
})
}
}
impl Attribuable for Model {
fn attributes(&self) -> &Attributes {
&self.attributes
}
}
impl Identified for Model {
fn identifier(&self) -> &Identifier {
&self.identifier
}
fn make_use(&self, identifier: &Identifier) -> bool {
self.base_model.identifier() == identifier
|| self.base_model.make_use(identifier)
|| self.parameters.values().any(|parameter| {
parameter
.described_type()
.final_type()
.data()
.map(|data| data.identifier() == identifier || data.make_use(identifier))
.unwrap_or(false)
})
|| self
.design
.lock()
.unwrap()
.as_ref()
.map(|design| design.make_use(identifier))
.unwrap_or(false)
|| self
.designer
.lock()
.unwrap()
.as_ref()
.map(|designer| designer.read().unwrap().make_use(identifier))
.unwrap_or(false)
}
fn uses(&self) -> Vec<Identifier> {
let mut uses = vec![self.base_model.identifier().clone()];
self.parameters.values().for_each(|parameter| {
if let Some(data) = parameter.described_type().final_type().data() {
uses.push(data.identifier().clone());
uses.extend(data.uses());
}
});
if let Some(design) = self.design.lock().unwrap().as_ref() {
uses.extend(design.uses());
}
if let Some(designer) = self.designer.lock().unwrap().as_ref() {
uses.extend(designer.read().unwrap().uses());
}
uses
}
}
impl Documented for Model {
fn documentation(&self) -> &str {
#[cfg(feature = "doc")]
{
&self.documentation
}
#[cfg(not(feature = "doc"))]
{
&""
}
}
}
impl Parameterized for Model {
fn parameters(&self) -> &HashMap<String, Parameter> {
&self.parameters
}
fn as_identified(&self) -> Arc<dyn Identified> {
self.auto_reference.upgrade().unwrap()
}
}
impl Generics for Model {
fn generics(&self) -> &Vec<Generic> {
static VEC: OnceLock<Vec<Generic>> = OnceLock::new();
VEC.get_or_init(|| Vec::new())
}
}
impl Buildable<ModelBuildMode> for Model {
fn build_mode(&self) -> ModelBuildMode {
ModelBuildMode::Designed()
}
}
impl ModelDescriptor for Model {
fn is_core_model(&self) -> bool {
false
}
fn base_model(&self) -> Option<Arc<dyn ModelDescriptor>> {
Some(Arc::clone(&self.base_model))
}
fn sources(&self) -> &HashMap<String, Vec<Arc<dyn Context>>> {
self.base_model.sources()
}
fn as_identified(&self) -> Arc<dyn Identified> {
self.auto_reference.upgrade().unwrap()
}
fn as_buildable(&self) -> Arc<dyn Buildable<ModelBuildMode>> {
self.auto_reference.upgrade().unwrap()
}
fn as_parameterized(&self) -> Arc<dyn Parameterized> {
self.auto_reference.upgrade().unwrap()
}
}
impl Clone for Model {
fn clone(&self) -> Self {
Self {
identifier: self.identifier.clone(),
#[cfg(feature = "doc")]
documentation: self.documentation.clone(),
attributes: self.attributes.clone(),
base_model: self.base_model.clone(),
parameters: self.parameters.clone(),
designer: Mutex::new(None),
design: Mutex::new(None),
auto_reference: Weak::default(),
}
}
}
impl Display for Model {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
write!(
f,
"model {}({})",
self.identifier.to_string(),
self.parameters()
.iter()
.map(|(_, p)| p.to_string())
.collect::<Vec<_>>()
.join(", "),
)?;
Ok(())
}
}