From c18970e8d1003a7b4b3b95b8ade07226bd235f0f Mon Sep 17 00:00:00 2001 From: troido Date: Thu, 6 Feb 2020 21:00:13 +0100 Subject: refactored into multiple classes --- src/componentparameter.rs | 46 +++++ src/componentwrapper.rs | 73 ++++++++ src/compwrapper.rs | 450 ---------------------------------------------- src/main.rs | 9 +- src/parameter.rs | 81 +++++++++ src/room.rs | 4 +- src/template.rs | 284 +++++++++++++++++++++++++---- 7 files changed, 459 insertions(+), 488 deletions(-) create mode 100644 src/componentparameter.rs create mode 100644 src/componentwrapper.rs delete mode 100644 src/compwrapper.rs create mode 100644 src/parameter.rs diff --git a/src/componentparameter.rs b/src/componentparameter.rs new file mode 100644 index 0000000..a2f7b70 --- /dev/null +++ b/src/componentparameter.rs @@ -0,0 +1,46 @@ + +use std::collections::HashMap; +use serde_json::Value; +use crate::parameter::{Parameter, ParameterType}; + +#[derive(Debug, PartialEq)] +pub enum ComponentParameter { + Constant(Parameter), + Argument(String) +} + +impl ComponentParameter { + pub fn evaluate(&self, arguments: &HashMap<&str, Parameter>) -> Option { + match self { + Self::Constant(val) => { + Some(val.clone()) + }, + Self::Argument(argname) => { + Some(arguments.get(argname.as_str())?.clone()) + } + } + } + + pub fn from_json(value: &Value) -> Result { + let paramvalue = value.get(1).ok_or("index 0 not in component parameter")?; + let typename = value.get(0).ok_or("index 0 not in component parameter")?.as_str().ok_or("compparam type not a string")?; + if let Some(paramtype) = ParameterType::from_str(typename) { + Ok(Self::Constant(Parameter::from_typed_json(paramtype, paramvalue).ok_or("failed to parse parameter constant")?)) + } else { + match typename { + "A" | "arg" => { + let argname = paramvalue.as_str().ok_or("argument parameter not a string")?.to_string(); + Ok(Self::Argument(argname)) + }, + _ => Err("unknown compparam type") + } + } + } + + pub fn get_type(&self, arguments: &Vec<(String, ParameterType, Option)>) -> Result{ + Ok(match self { + Self::Constant(param) => param.paramtype(), + Self::Argument(argname) => arguments.iter().find(|(n, _t, _d)| n == argname).ok_or("unknown argument name")?.1 + }) + } +} diff --git a/src/componentwrapper.rs b/src/componentwrapper.rs new file mode 100644 index 0000000..6da6d5a --- /dev/null +++ b/src/componentwrapper.rs @@ -0,0 +1,73 @@ + +use std::collections::HashMap; +use specs::{Builder, EntityBuilder}; + +use crate::components::{Visible, Blocking, Played}; +use crate::hashmap; +use crate::parameter::{Parameter, ParameterType}; + + +#[derive(Clone)] +pub enum ComponentWrapper{ + Visible(Visible), + Blocking(Blocking), + Player(Played) +} + +impl ComponentWrapper { + + pub fn build<'a>(&self, builder: EntityBuilder<'a>) -> EntityBuilder<'a> { + match self.clone() { + Self::Visible(c) => builder.with(c), + Self::Blocking(c) => builder.with(c), + Self::Player(c) => builder.with(c) + } + } + + pub fn load_component(comptype: ComponentType, mut parameters: HashMap<&str, Parameter>) -> Option { + match comptype { + ComponentType::Visible => Some(Self::Visible(Visible{ + sprite: parameters.remove("sprite")?.as_str()?.to_string(), + height: parameters.remove("height")?.as_f64()? + })), + ComponentType::Blocking => Some(Self::Blocking(Blocking)), + ComponentType::Player => Some(Self::Player(Played::new( + parameters.remove("name")?.as_str()?.to_string() + ))) + } + } +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum ComponentType { + Visible, + Blocking, + Player +} + +impl ComponentType { + + pub fn from_str(typename: &str) -> Option{ + match typename { + "Visible" => Some(ComponentType::Visible), + "Blocking" => Some(ComponentType::Blocking), + "Player" => Some(ComponentType::Player), + _ => None + } + } + + pub fn parameters(&self) -> HashMap<&str, ParameterType> { + match self { + ComponentType::Visible => hashmap!("sprite" => ParameterType::String, "height" => ParameterType::Float), + ComponentType::Blocking => HashMap::new(), + ComponentType::Player => hashmap!("name" => ParameterType::String) + } + } +} + + + + + + + diff --git a/src/compwrapper.rs b/src/compwrapper.rs deleted file mode 100644 index 5af7633..0000000 --- a/src/compwrapper.rs +++ /dev/null @@ -1,450 +0,0 @@ - -use std::collections::HashMap; -use specs::{Builder, EntityBuilder}; -use serde_json::Value; - -use crate::components::{Visible, Blocking, Played}; -use crate::pos::Pos; -use crate::hashmap; - - -#[derive(Clone)] -pub enum CompWrapper{ - Visible(Visible), - Blocking(Blocking), - Player(Played) -} - -impl CompWrapper { - - pub fn build<'a>(&self, builder: EntityBuilder<'a>) -> EntityBuilder<'a> { - match self.clone() { - Self::Visible(c) => builder.with(c), - Self::Blocking(c) => builder.with(c), - Self::Player(c) => builder.with(c) - } - } - - pub fn load_component(comptype: ComponentType, mut parameters: HashMap<&str, Parameter>) -> Option { - match comptype { - ComponentType::Visible => Some(CompWrapper::Visible(Visible{ - sprite: parameters.remove("sprite")?.as_str()?.to_string(), - height: parameters.remove("height")?.as_f64()? - })), - ComponentType::Blocking => Some(CompWrapper::Blocking(Blocking)), - ComponentType::Player => Some(CompWrapper::Player(Played::new( - parameters.remove("name")?.as_str()?.to_string() - ))) - } - } -} - -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub enum ComponentType { - Visible, - Blocking, - Player -} - -impl ComponentType { - - pub fn from_str(typename: &str) -> Option{ - match typename { - "Visible" => Some(ComponentType::Visible), - "Blocking" => Some(ComponentType::Blocking), - "Player" => Some(ComponentType::Player), - _ => None - } - } - - pub fn parameters(&self) -> HashMap<&str, ParamType> { - match self { - ComponentType::Visible => hashmap!("sprite" => ParamType::String, "height" => ParamType::Float), - ComponentType::Blocking => HashMap::new(), - ComponentType::Player => hashmap!("name" => ParamType::String) - } - } -} - -#[derive(Debug, PartialEq, Clone)] -pub enum Parameter { - String(String), - Int(i64), -// Pos(Pos), - Float(f64) -} - -impl Parameter { - - pub fn from_typed_json(typ: ParamType, val: &Value) -> Option{ - match typ { - ParamType::String => Some(Parameter::String(val.as_str()?.to_string())), - ParamType::Int => Some(Parameter::Int(val.as_i64()?)), - ParamType::Float => Some(Parameter::Float(val.as_f64()?)) - } - } - - pub fn paramtype(&self) -> ParamType { - match self { - Parameter::String(_) => ParamType::String, - Parameter::Int(_) => ParamType::Int, - Parameter::Float(_) => ParamType::Float - } - } - - pub fn from_json(val: &Value) -> Option { - Parameter::from_typed_json(ParamType::from_str(val.get(0)?.as_str()?)?, val.get(1)?) - } - - pub fn as_str(&self) -> Option<&str> { - if let Parameter::String(str) = self { - Some(str) - } else { - None - } - } - - pub fn as_string(&self) -> Option { - Some(self.as_str()?.to_string()) - } - - pub fn as_i64(&self) -> Option { - if let Parameter::Int(num) = self { - Some(*num) - } else { - None - } - } - - pub fn as_f64(&self) -> Option { - if let Parameter::Float(num) = self { - Some(*num) - } else { - None - } - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum ParamType { - String, -// Pos, - Int, - Float -} - -impl ParamType { - - pub fn from_str(typename: &str) -> Option{ - match typename { - "string" => Some(ParamType::String), - "int" => Some(ParamType::Int), - "float" => Some(ParamType::Float), - _ => None - } - } -} - -#[derive(Debug, PartialEq)] -pub struct Template { - pub arguments: Vec<(String, ParamType, Option)>, - pub components: Vec<(ComponentType, HashMap)> -} - -impl Template { - - - fn parse_definition_arguments(args: &Value) -> Result)>, &'static str> { - let mut arguments: Vec<(String, ParamType, 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 = ParamType::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")?))); - } - } - 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 = CompParam::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 template = Template { - 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")?)? - }; - template.validate()?; - Ok(template) - } - - 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) - } - - 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(CompWrapper::load_component(*comptype, compargs).ok_or("failed to load component")?); - } - Ok(components) - } -} - - -#[derive(Debug, PartialEq)] -pub enum CompParam { - Constant(Parameter), - Argument(String) -} - -impl CompParam { - pub fn evaluate(&self, arguments: &HashMap<&str, Parameter>) -> Option { - match self { - CompParam::Constant(val) => { - Some(val.clone()) - }, - CompParam::Argument(argname) => { - Some(arguments.get(argname.as_str())?.clone()) - } - } - } - - pub fn from_json(value: &Value) -> Result { - let paramvalue = value.get(1).ok_or("index 0 not in component parameter")?; - let typename = value.get(0).ok_or("index 0 not in component parameter")?.as_str().ok_or("compparam type not a string")?; - if let Some(paramtype) = ParamType::from_str(typename) { - Ok(CompParam::Constant(Parameter::from_typed_json(paramtype, paramvalue).ok_or("failed to parse parameter constant")?)) - } else { - match typename { - "A" | "arg" => { - let argname = paramvalue.as_str().ok_or("argument parameter not a string")?.to_string(); - Ok(CompParam::Argument(argname)) - }, - _ => Err("unknown compparam type") - } - } - } - - pub fn get_type(&self, arguments: &Vec<(String, ParamType, Option)>) -> Result{ - Ok(match self { - Self::Constant(param) => param.paramtype(), - Self::Argument(argname) => arguments.iter().find(|(n, _t, _d)| n == argname).ok_or("unknown argument name")?.1 - }) - } -} - - -#[cfg(test)] -mod tests { - use super::*; - use serde_json::json; - - - #[test] - fn empty_template_from_json() { - assert_eq!( - Template::from_json(&json!({ - "arguments": [], - "components": [] - })).unwrap(), - Template{ - arguments: vec![], - components: vec![] - } - ); - } - - #[test] - fn grass_from_json(){ - let result = Template::from_json(&json!({ - "arguments": [ - ["sprite", "string", "grass1"] - ], - "components": [ - ["Visible", { - "sprite": ["A", "sprite"], - "height": ["float", 0.1] - }] - ] - })).unwrap(); - let constructed = Template{ - arguments: vec![("sprite".to_string(), ParamType::String, Some(Parameter::String("grass1".to_string())))], - components: vec![ - (ComponentType::Visible, hashmap!( - "sprite".to_string() => CompParam::Argument("sprite".to_string()), - "height".to_string() => CompParam::Constant(Parameter::Float(0.1)) - )) - ] - }; - assert_eq!(result, constructed); - } - - #[test] - fn invalid_component_name(){ - let result = Template::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 = Template::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 = Template::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 = Template::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 = Template::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 = Template::from_json(&json!({ - "arguments": [ - ["sprite", "string", null] - ], - "components": [ - ["Visible", { - "sprite": ["A", "sprite"], - "height": ["float", 0.1] - }] - ] - })).unwrap(); - let constructed = Template{ - arguments: vec![("sprite".to_string(), ParamType::String, None)], - components: vec![ - (ComponentType::Visible, hashmap!( - "sprite".to_string() => CompParam::Argument("sprite".to_string()), - "height".to_string() => CompParam::Constant(Parameter::Float(0.1)) - )) - ] - }; - assert_eq!(result, constructed); - } -} - - - diff --git a/src/main.rs b/src/main.rs index 5b79849..545245c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -20,16 +20,19 @@ mod worldmessages; mod pos; mod assemblage; // mod load; -mod compwrapper; +mod componentwrapper; +mod parameter; +mod template; +mod componentparameter; use self::gameserver::GameServer; use self::server::unixserver::UnixServer; use self::server::tcpserver::TcpServer; use self::server::Server; -use self::assemblages::{Wall, Grass}; +use self::assemblages::{Grass}; use self::room::Room; use self::util::ToJson; -use self::compwrapper::{Parameter, Template, CompParam, ComponentType}; +use self::template::{Template}; diff --git a/src/parameter.rs b/src/parameter.rs new file mode 100644 index 0000000..3ae88cd --- /dev/null +++ b/src/parameter.rs @@ -0,0 +1,81 @@ + +use serde_json::Value; + +#[derive(Debug, PartialEq, Clone)] +pub enum Parameter { + String(String), + Int(i64), +// Pos(Pos), + Float(f64) +} + +impl Parameter { + + pub fn from_typed_json(typ: ParameterType, val: &Value) -> Option{ + match typ { + ParameterType::String => Some(Self::String(val.as_str()?.to_string())), + ParameterType::Int => Some(Self::Int(val.as_i64()?)), + ParameterType::Float => Some(Self::Float(val.as_f64()?)) + } + } + + pub fn paramtype(&self) -> ParameterType { + match self { + Self::String(_) => ParameterType::String, + Self::Int(_) => ParameterType::Int, + Self::Float(_) => ParameterType::Float + } + } + +// pub fn from_json(val: &Value) -> Option { +// Self::from_typed_json(ParameterType::from_str(val.get(0)?.as_str()?)?, val.get(1)?) +// } + + pub fn as_str(&self) -> Option<&str> { + if let Self::String(str) = self { + Some(str) + } else { + None + } + } + +// pub fn as_string(&self) -> Option { +// Some(self.as_str()?.to_string()) +// } + + pub fn as_i64(&self) -> Option { + if let Self::Int(num) = self { + Some(*num) + } else { + None + } + } + + pub fn as_f64(&self) -> Option { + if let Self::Float(num) = self { + Some(*num) + } else { + None + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ParameterType { + String, +// Pos, + Int, + Float +} + +impl ParameterType { + + pub fn from_str(typename: &str) -> Option{ + match typename { + "string" => Some(Self::String), + "int" => Some(Self::Int), + "float" => Some(Self::Float), + _ => None + } + } +} diff --git a/src/room.rs b/src/room.rs index 4fcf658..f7e09bf 100644 --- a/src/room.rs +++ b/src/room.rs @@ -28,7 +28,7 @@ use super::systems::{ controlinput::ControlInput, view::View }; -use super::compwrapper::CompWrapper; +use super::componentwrapper::ComponentWrapper; @@ -84,7 +84,7 @@ impl <'a, 'b>Room<'a, 'b> { template.build(self.world.create_entity()).with(Position::new(Pos{x, y})).build() } - pub fn add_complist(&mut self, template: &Vec, (x, y): (i32, i32)) -> Entity{ + pub fn add_complist(&mut self, template: &Vec, (x, y): (i32, i32)) -> Entity{ let mut builder = self.world.create_entity(); for comp in template { builder = comp.build(builder); diff --git a/src/template.rs b/src/template.rs index 5e52cc0..bdf86b3 100644 --- a/src/template.rs +++ b/src/template.rs @@ -1,48 +1,266 @@ use std::collections::HashMap; -use crate::parameter::Parameter; -use crate::compwrapper::CompWrapper; +use serde_json::Value; +use super::componentparameter::ComponentParameter; +use super::parameter::{Parameter, ParameterType}; +use super::componentwrapper::{ComponentWrapper, ComponentType}; +#[derive(Debug, PartialEq)] pub struct Template { - pub arguments: Vec, - pub components: Vec<(String, HashMap)> + pub arguments: Vec<(String, ParameterType, Option)>, + pub components: Vec<(ComponentType, HashMap)> } impl Template { - pub fn instantiate(&self, args: Vec, kwargs: HashMap) -> Option>{ - let mut components: Vec = Vec::new(); - for (compname, compparams) in &self.components { - let mut compargs: HashMap<&str, &Parameter> = HashMap::new(); + + + 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")?))); + } + } + 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 template = Template { + 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")?)? + }; + template.validate()?; + Ok(template) + } + + 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) + } + + 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 { - match param { - CompParam::Constant(val) => {compargs.insert(name.as_str(), &val); Some(())}, - CompParam::Argument(argname) => { - if let Some(argval) = kwargs.get(argname.as_str()) { - compargs.insert(name.as_str(), argval); - Some(()) - } else if let Some(idx) = self.arguments.iter().position(|x| x == name){ - if idx < args.len() { - compargs.insert(name.as_str(), &args[idx]); - Some(()) - } else { - println!("positional argument out of range"); - None - } - } else { - println!("can't find parameter value, compname: {}, name: {}, argname: {}", compname, name, argname); - None - } - } - }?; + compargs.insert(name.as_str(), param.evaluate(&arguments).ok_or("argument not found")?); } - components.push(CompWrapper::load_component(compname.as_str(), compargs)?); + components.push(ComponentWrapper::load_component(*comptype, compargs).ok_or("failed to load component")?); } - Some(components) + Ok(components) } } -pub enum CompParam { - Constant(Parameter), - Argument(String) + +#[cfg(test)] +mod tests { + use super::*; + use crate::hashmap; + use serde_json::json; + + + #[test] + fn empty_template_from_json() { + assert_eq!( + Template::from_json(&json!({ + "arguments": [], + "components": [] + })).unwrap(), + Template{ + arguments: vec![], + components: vec![] + } + ); + } + + #[test] + fn grass_from_json(){ + let result = Template::from_json(&json!({ + "arguments": [ + ["sprite", "string", "grass1"] + ], + "components": [ + ["Visible", { + "sprite": ["A", "sprite"], + "height": ["float", 0.1] + }] + ] + })).unwrap(); + let constructed = Template{ + 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 = Template::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 = Template::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 = Template::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 = Template::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 = Template::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 = Template::from_json(&json!({ + "arguments": [ + ["sprite", "string", null] + ], + "components": [ + ["Visible", { + "sprite": ["A", "sprite"], + "height": ["float", 0.1] + }] + ] + })).unwrap(); + let constructed = Template{ + 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