From 70db58f688f0dbdd69231da570cf8dbb54e5ca81 Mon Sep 17 00:00:00 2001 From: troido Date: Fri, 7 Feb 2020 14:08:18 +0100 Subject: named stuff properly; added encyclopedia and template --- src/assemblage.rs | 303 +++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 253 insertions(+), 50 deletions(-) (limited to 'src/assemblage.rs') diff --git a/src/assemblage.rs b/src/assemblage.rs index a81ce82..1e7eb44 100644 --- a/src/assemblage.rs +++ b/src/assemblage.rs @@ -1,63 +1,266 @@ +use std::collections::HashMap; +use serde_json::Value; +use super::componentparameter::ComponentParameter; +use super::parameter::{Parameter, ParameterType}; +use super::componentwrapper::{ComponentWrapper, ComponentType}; -use std::any::Any; - - -#[macro_export] -macro_rules! assemblage { - ($name:ident { $($arg:ident : $argt:ident ),* } ; $( $comp:expr ),* ) => { - #[derive(Debug, Clone, Default)] - pub struct $name {$( - pub $arg : $argt - )* } - impl Assemblage for $name { - fn build<'a>(&self, mut builder: specs::EntityBuilder<'a>) -> specs::EntityBuilder<'a>{ - $( - let $arg = &self.$arg; - )* - $( - builder = specs::Builder::with(builder, $comp); - )* - builder +#[derive(Debug, PartialEq)] +pub struct Assemblage { + pub arguments: Vec<(String, ParameterType, Option)>, + pub components: Vec<(ComponentType, HashMap)> +} + +impl Assemblage { + + + fn parse_definition_arguments(args: &Value) -> Result)>, &'static str> { + let mut arguments: Vec<(String, ParameterType, Option)> = Vec::new(); + for arg in args.as_array().ok_or("arguments is not an array")? { + let tup = arg.as_array().ok_or("argument is not an array")?; + let key = tup.get(0).ok_or("argument has no name")?.as_str().ok_or("argument name is not a string")?.to_string(); + let typ = ParameterType::from_str(tup.get(1).ok_or("argument has no type")?.as_str().ok_or("argument type not a string")?).ok_or("failed to parse argument type")?; + let def = tup.get(2).ok_or("argument has no default")?; + if def.is_null() { + arguments.push((key.clone(), typ, None)); + } else { + arguments.push((key.clone(), typ, Some(Parameter::from_typed_json(typ, def).ok_or("invalid argument default")?))); } - - #[allow(unused_variables, unused_mut)] - fn init_from_json(&mut self, mut args: Vec, kwargs: std::collections::HashMap) { - $( - if args.len() > 0 { - let val = args.remove(0); - if let Some(actual_val) = super::unpack_json!($argt, val) { - self.$arg = actual_val; - } - } - )* - $( - if let Some(val) = kwargs.get(stringify!($arg)) { - if let Some(actual_val) = super::unpack_json!($argt, val) { - self.$arg = actual_val; - } - } - )* + } + Ok(arguments) + } + + fn parse_definition_components(comps: &Value) -> Result)>, &'static str> { + let mut components = Vec::new(); + for tup in comps.as_array().ok_or("components is not a json array")? { + let comptype = ComponentType::from_str(tup + .get(0).ok_or("index 0 not in component")? + .as_str().ok_or("component name not a string")? + ).ok_or("not a valid componenttype")?; + let mut parameters: HashMap = HashMap::new(); + for (key, value) in tup.get(1).ok_or("index 1 not in component")?.as_object().ok_or("component parameters not a json object")? { + let param = ComponentParameter::from_json(value)?; + parameters.insert(key.clone(), param); } + components.push((comptype, parameters)); } + Ok(components) + } + + fn validate(&self) -> Result<(), &'static str> { + for (comptype, parameters) in &self.components { + for (paramname, paramtype) in comptype.parameters() { + let param = parameters.get(paramname).ok_or("missing parameter")?; + let actualtype = param.get_type(&self.arguments)?; + if actualtype != paramtype { + return Err("parameter type incorrect"); + } + } + } + Ok(()) + } + + pub fn from_json(val: &Value) -> Result{ + let assemblage = Self { + arguments: Self::parse_definition_arguments(val.get("arguments").ok_or("property 'arguments' not found")?)?, + components: Self::parse_definition_components(val.get("components").ok_or("property 'components' not found")?)? + }; + assemblage.validate()?; + Ok(assemblage) + } + + fn prepare_arguments(&self, args: &Vec, kwargs: &HashMap) -> Result, &str> { + let mut arguments: HashMap<&str, Parameter> = HashMap::new(); + for (idx, (name, typ, def)) in self.arguments.iter().enumerate() { + let value: Option = { + if let Some(val) = kwargs.get(name) { + Some(val.clone()) + } else if let Some(val) = args.get(idx) { + Some(val.clone()) + } else if let Some(val) = def { + Some(val.clone()) + } else { + None + } + }; + let param = value.ok_or("argument has no value")?; + if param.paramtype() != *typ { + return Err("argument has incorrect type"); + } + arguments.insert(name, param); + } + Ok(arguments) } -} -#[macro_export] -macro_rules! unpack_json { - (String, $val: ident) => { - if let Some(txt) = $val.as_str(){ - Some(txt.to_string()) - } else { - None + pub fn instantiate(&self, args: &Vec, kwargs: &HashMap) -> Result, &str>{ + let mut components: Vec = Vec::new(); + let arguments = self.prepare_arguments(args, kwargs)?; + 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).ok_or("argument not found")?); + } + components.push(ComponentWrapper::load_component(*comptype, compargs).ok_or("failed to load component")?); } + Ok(components) } } -pub trait Assemblage: Send + Sync + Any { - fn build<'a>(&self, builder: specs::EntityBuilder<'a>) -> specs::EntityBuilder<'a>; - fn init_from_json(&mut self, args: Vec, kwargs: std::collections::HashMap); -} - +#[cfg(test)] +mod tests { + use super::*; + use crate::hashmap; + use serde_json::json; + + + #[test] + fn empty_assemblage_from_json() { + assert_eq!( + Assemblage::from_json(&json!({ + "arguments": [], + "components": [] + })).unwrap(), + Assemblage{ + arguments: vec![], + components: vec![] + } + ); + } + + #[test] + fn grass_from_json(){ + let result = Assemblage::from_json(&json!({ + "arguments": [ + ["sprite", "string", "grass1"] + ], + "components": [ + ["Visible", { + "sprite": ["A", "sprite"], + "height": ["float", 0.1] + }] + ] + })).unwrap(); + let constructed = Assemblage{ + arguments: vec![("sprite".to_string(), ParameterType::String, Some(Parameter::String("grass1".to_string())))], + components: vec![ + (ComponentType::Visible, hashmap!( + "sprite".to_string() => ComponentParameter::Argument("sprite".to_string()), + "height".to_string() => ComponentParameter::Constant(Parameter::Float(0.1)) + )) + ] + }; + assert_eq!(result, constructed); + } + + #[test] + fn invalid_component_name(){ + let result = Assemblage::from_json(&json!({ + "arguments": [ + ["sprite", "string", null] + ], + "components": [ + ["visible", { // no capital so invalid + "sprite": ["A", "sprite"], + "height": ["float", 0.1] + }] + ] + })).unwrap_err(); + assert_eq!(result, "not a valid componenttype"); + } + + + + #[test] + fn invalid_parameter_type(){ + let result = Assemblage::from_json(&json!({ + "arguments": [ + ["sprite", "string", "grass1"] + ], + "components": [ + ["Visible", { + "sprite": ["A", "sprite"], + "height": ["string", "0.1"] + }] + ] + })).unwrap_err(); + assert_eq!(result, "parameter type incorrect"); + } + + #[test] + fn unknown_argument_name(){ + let result = Assemblage::from_json(&json!({ + "arguments": [ + ["sprite", "string", "grass1"] + ], + "components": [ + ["Visible", { + "sprite": ["A", "sprits"], + "height": ["float", 0.1] + }] + ] + })).unwrap_err(); + assert_eq!(result, "unknown argument name"); + } + + #[test] + fn wrong_argument_type(){ + let result = Assemblage::from_json(&json!({ + "arguments": [ + ["sprite", "int", 1] + ], + "components": [ + ["Visible", { + "sprite": ["A", "sprite"], + "height": ["float", 0.1] + }] + ] + })).unwrap_err(); + assert_eq!(result, "parameter type incorrect"); + } + + + + #[test] + fn wrong_argument_default(){ + let result = Assemblage::from_json(&json!({ + "arguments": [ + ["sprite", "string", 1] + ], + "components": [ + ["Visible", { + "sprite": ["A", "sprits"], + "height": ["float", 0.1] + }] + ] + })).unwrap_err(); + assert_eq!(result, "invalid argument default"); + } + + + #[test] + fn null_argument(){ + let result = Assemblage::from_json(&json!({ + "arguments": [ + ["sprite", "string", null] + ], + "components": [ + ["Visible", { + "sprite": ["A", "sprite"], + "height": ["float", 0.1] + }] + ] + })).unwrap(); + let constructed = Assemblage{ + arguments: vec![("sprite".to_string(), ParameterType::String, None)], + components: vec![ + (ComponentType::Visible, hashmap!( + "sprite".to_string() => ComponentParameter::Argument("sprite".to_string()), + "height".to_string() => ComponentParameter::Constant(Parameter::Float(0.1)) + )) + ] + }; + assert_eq!(result, constructed); + } +} -- cgit