diff options
| author | troido <troido@protonmail.com> | 2020-02-06 21:00:13 +0100 |
|---|---|---|
| committer | troido <troido@protonmail.com> | 2020-02-06 21:00:13 +0100 |
| commit | c18970e8d1003a7b4b3b95b8ade07226bd235f0f (patch) | |
| tree | de0e1d331d0134ab8c7ecaf3569c589a0de07208 /src/template.rs | |
| parent | 837d5b3a2c70d240b053644ef2e5c3264d453756 (diff) | |
refactored into multiple classes
Diffstat (limited to 'src/template.rs')
| -rw-r--r-- | src/template.rs | 284 |
1 files changed, 251 insertions, 33 deletions
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<String>, - pub components: Vec<(String, HashMap<String, CompParam>)> + pub arguments: Vec<(String, ParameterType, Option<Parameter>)>, + pub components: Vec<(ComponentType, HashMap<String, ComponentParameter>)> } impl Template { - pub fn instantiate(&self, args: Vec<Parameter>, kwargs: HashMap<String, Parameter>) -> Option<Vec<CompWrapper>>{ - let mut components: Vec<CompWrapper> = Vec::new(); - for (compname, compparams) in &self.components { - let mut compargs: HashMap<&str, &Parameter> = HashMap::new(); + + + fn parse_definition_arguments(args: &Value) -> Result<Vec<(String, ParameterType, Option<Parameter>)>, &'static str> { + let mut arguments: Vec<(String, ParameterType, Option<Parameter>)> = 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<Vec<(ComponentType, HashMap<String, ComponentParameter>)>, &'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<String, ComponentParameter> = 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<Template, &'static str>{ + 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<Parameter>, kwargs: HashMap<String, Parameter>) -> Result<HashMap<&str, Parameter>, &str> { + let mut arguments: HashMap<&str, Parameter> = HashMap::new(); + for (idx, (name, typ, def)) in self.arguments.iter().enumerate() { + let value: Option<Parameter> = { + 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<Parameter>, kwargs: HashMap<String, Parameter>) -> Result<Vec<ComponentWrapper>, &str>{ + let mut components: Vec<ComponentWrapper> = 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); + } } |
