use std::collections::HashMap; use serde::{de, Serialize, Deserialize, Deserializer}; use crate::{ parameterexpression::{ParameterExpression, EvaluationError}, parameter::{Parameter}, componentwrapper::{ComponentWrapper, ComponentType}, components::{Serialise, Clan}, Template, Result as AnyResult, aerr, sprite::Sprite, compmap }; #[derive(Debug, PartialEq, Clone)] pub struct Assemblage { arguments: HashMap>, components: Vec<(ComponentType, HashMap)>, save: bool, extract: Vec<(String, ComponentType, String)> } impl Assemblage { pub fn validate(&self) -> AnyResult<()> { for (comptype, parameters) in &self.components { let mut is_complete = true; let mut compargs = HashMap::new(); for paramname in comptype.parameters() { let param = parameters.get(paramname).ok_or(aerr!("missing parameter {} for component {:?}", paramname, comptype))?; match param.evaluate(&self.arguments, &Template::empty("")) { Err(EvaluationError::MissingArgument(_)) => {is_complete = false;} Err(EvaluationError::Other(msg)) => {return Err(aerr!("invalid value for {}: {}", paramname, msg))} Ok(p) => {compargs.insert(paramname, p);} } } if is_complete { ComponentWrapper::load_component(*comptype, compargs)?; } } Ok(()) } pub fn instantiate(&self, template: &Template) -> AnyResult>{ let mut arguments = self.arguments.clone(); for (key, param) in template.kwargs.clone() { arguments.insert(key, Some(param)); } let mut components: Vec = Vec::new(); for (comptype, compparams) in &self.components { let mut compargs: HashMap<&str, Parameter> = HashMap::new(); for (name, param) in compparams { compargs.insert(name.as_str(), param.evaluate(&arguments, template).map_err(|e| match e { EvaluationError::MissingArgument(arg) => aerr!("argument {} has no value", arg), EvaluationError::Other(msg) => aerr!("{}", msg) })?); } components.push(ComponentWrapper::load_component(*comptype, compargs)?); } if template.should_save() && self.save { components.push(ComponentWrapper::Serialise(Serialise{template: template.clone(), extract: self.extract.clone() })); } if let Some(clan) = &template.clan { components.push(ComponentWrapper::Clan(Clan{name: clan.clone()})); } Ok(components) } pub fn apply_arguments(&self, arguments: HashMap) -> Self { let mut assemblage = self.clone(); for (key, val) in arguments { assemblage.arguments.insert(key, Some(val)); } assemblage } pub fn new_item(id: String, sprite: Sprite, name: String) -> Assemblage { Assemblage { arguments: HashMap::new(), save: true, extract: Vec::new(), components: vec![ (ComponentType::Visible, compmap!{height: 0.3_f64, sprite: sprite.0, name: name}), (ComponentType::Item, compmap!{item: id}) ] } } } #[macro_export] macro_rules! compmap { {$($name: ident: $val: expr),*} => {{ #[allow(unused_imports)] use crate::fromtoparameter::FromToParameter; #[allow(unused_mut)] let mut h = std::collections::HashMap::new(); $( h.insert(stringify!($name).to_string(), crate::parameterexpression::ParameterExpression::Constant($val.to_parameter())); )* h }} } impl<'de> Deserialize<'de> for Assemblage { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de> { let AssemblageSave{arguments, mut components, save, extract, name, sprite, height, flags, substitute} = AssemblageSave::deserialize(deserializer)?; if let Some(f) = flags { components.push((ComponentType::Flags, compmap!{flags: f})); } if let Some(spr) = sprite { components.push((ComponentType::Visible, compmap!{ sprite: spr.clone(), height: height.ok_or(de::Error::custom("height must be included in assemblage when sprite is included"))?, name: name.unwrap_or(spr) })); } if let Some(sub) = substitute { components.push((ComponentType::Substitute, compmap!{into: sub})); } Ok(Assemblage { arguments, components, save, extract: extract.into_iter().map(|(k, (t, v))| (k, t, v)).collect() }) } } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] struct AssemblageSave { #[serde(default)] pub arguments: HashMap>, #[serde(default)] pub components: Vec<(ComponentType, HashMap)>, #[serde(default="return_true")] pub save: bool, #[serde(default)] pub extract: HashMap, pub name: Option, pub sprite: Option, pub height: Option, pub flags: Option>, pub substitute: Option