summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authortroido <troido@protonmail.com>2020-02-05 23:58:15 +0100
committertroido <troido@protonmail.com>2020-02-05 23:58:15 +0100
commit7a4de27a97dc1e3cc2e517d96683e3c4ba9f7508 (patch)
treedc1aa5dff6ba5ebe958335daade2ecd937bf0fa1 /src
parent323cd679cd29a8475c3b7486ce54ecd37620dbea (diff)
parse assemblages from json
Diffstat (limited to 'src')
-rw-r--r--src/compwrapper.rs356
-rw-r--r--src/main.rs12
-rw-r--r--src/template.rs10
3 files changed, 350 insertions, 28 deletions
diff --git a/src/compwrapper.rs b/src/compwrapper.rs
index 8028b49..35986ce 100644
--- a/src/compwrapper.rs
+++ b/src/compwrapper.rs
@@ -2,7 +2,10 @@
use std::collections::HashMap;
use specs::{Builder, EntityBuilder};
use serde_json::Value;
-use super::components::{Visible, Blocking, Played};
+
+use crate::components::{Visible, Blocking, Played};
+use crate::pos::Pos;
+use crate::hashmap;
#[derive(Clone)]
@@ -22,27 +25,348 @@ impl CompWrapper {
}
}
- pub fn parse_component(data: Value) -> Option<CompWrapper> {
- let a = data.as_array()?;
- if a.len() != 2 {
- return None
- }
- let typename = a[0].as_str()?;
- let params: HashMap<&str, &Value> = a[1].as_object()?.into_iter().map(|(key, val)| (key.as_str(), val)).collect();
- Self::load_component(typename, params)
- }
+// pub fn parse_component(data: Value) -> Option<CompWrapper> {
+// let a = data.as_array()?;
+// if a.len() != 2 {
+// return None
+// }
+// let typename = a[0].as_str()?;
+// let params: HashMap<&str, &Parameter> = a[1].as_object()?.into_iter().map(|(key, val)| (key.as_str(), val)).collect();
+// Self::load_component(typename, params)
+// }
- pub fn load_component(typename: &str, mut parameters: HashMap<&str, &Value>) -> Option<CompWrapper> {
- match typename {
- "Visible" => Some(CompWrapper::Visible(Visible{
+ pub fn load_component(comptype: ComponentType, mut parameters: HashMap<&str, &Parameter>) -> Option<CompWrapper> {
+ match comptype {
+ ComponentType::Visible => Some(CompWrapper::Visible(Visible{
sprite: parameters.remove("sprite")?.as_str()?.to_string(),
height: parameters.remove("height")?.as_f64()?
})),
- "Blocking" => Some(CompWrapper::Blocking(Blocking)),
- "Player" => Some(CompWrapper::Player(Played::new(
+ 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<ComponentType>{
+ 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)]
+pub enum Parameter {
+ String(String),
+ Int(i64),
+// Pos(Pos),
+ Float(f64)
+}
+
+impl Parameter {
+
+ pub fn from_typed_json(typ: ParamType, val: &Value) -> Option<Parameter>{
+ 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> {
+ 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<String> {
+ Some(self.as_str()?.to_string())
+ }
+
+ pub fn as_i64(&self) -> Option<i64> {
+ if let Parameter::Int(num) = self {
+ Some(*num)
+ } else {
+ None
+ }
+ }
+
+ pub fn as_f64(&self) -> Option<f64> {
+ if let Parameter::Float(num) = self {
+ Some(*num)
+ } else {
+ None
+ }
+ }
+}
+
+#[derive(Clone, Copy, PartialEq)]
+pub enum ParamType {
+ String,
+// Pos,
+ Int,
+ Float
+}
+
+impl ParamType {
+
+ pub fn from_str(typename: &str) -> Option<ParamType>{
+ 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, Option<Parameter>)>,
+ pub components: Vec<(ComponentType, HashMap<String, CompParam>)>
+}
+
+impl Template {
+
+ pub fn from_json(val: Value) -> Result<Template, &'static str>{
+ let mut arguments: Vec<(String, Option<Parameter>)> = Vec::new();
+ for arg in val.get("arguments").ok_or("property 'arguments' not found")?.as_array().ok_or("arguments is not a json object")? {
+ let tup = arg.as_array().ok_or("argument is not an array")?;
+ let key = tup.get(0).ok_or("argument has no index name")?.as_str().ok_or("argument name is not a string")?.to_string();
+ let value = tup.get(1).ok_or("argument has only name")?;
+ if value.is_null() {
+ arguments.push((key.clone(), None));
+ } else {
+ arguments.push((key.clone(), Some(Parameter::from_json(value).ok_or("invalid argument default")?)));
+ }
+ }
+ let mut components = Vec::new();
+ for tup in val.get("components").ok_or("property 'arguments' not found")?.as_array().ok_or("arguments is not a json object")? {
+ 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, CompParam> = 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 paramtype: ParamType = comptype.parameters().remove(key.as_str()).ok_or("unknown parameter name")?;
+ let paramvalue = value.get(1).ok_or("index 0 not in component parameter")?;
+ let param = match value.get(0).ok_or("index 0 not in component parameter")?.as_str().ok_or("compparam type not a string")? {
+ "C" => Ok(CompParam::Constant(
+ Parameter::from_typed_json(paramtype, paramvalue).ok_or("failed to parse parameter constant")?
+ )),
+ "A" => {
+ let argname = paramvalue.as_str().ok_or("argument parameter not a string")?.to_string();
+ let arg = arguments.iter().find(|(a, d)| a == &argname).ok_or("unknown argument name")?;
+ match &arg.1 {
+ Some(param) => {
+ if param.paramtype() == paramtype {
+ Ok(CompParam::Argument(argname))
+ } else {
+ Err("wrong argument type")
+ }
+ },
+ None => Ok(CompParam::Argument(argname))
+ }
+ },
+ _ => Err("unknown compparam type")
+ };
+ parameters.insert(key.clone(), param?);
+ }
+ components.push((comptype, parameters));
+ }
+ Ok(Template {
+ arguments,
+ components
+ })
+ }
+
+ pub fn instantiate(&self, args: Vec<Parameter>, kwargs: HashMap<String, Parameter>) -> Option<Vec<CompWrapper>>{
+ let mut components: Vec<CompWrapper> = Vec::new();
+ 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, d)| 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, comptype: {:?}, name: {}, argname: {}", comptype, name, argname);
+ None
+ }
+ }
+ }?;
+ }
+ components.push(CompWrapper::load_component(*comptype, compargs)?);
+ }
+ Some(components)
+ }
+}
+
+
+#[derive(Debug, PartialEq)]
+pub enum CompParam {
+ Constant(Parameter),
+ Argument(String)
+}
+
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use serde_json::json;
+ use std::collections::HashMap;
+
+
+ #[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": ["C", 0.1]
+ }]
+ ]
+ })).unwrap();
+ let constructed = Template{
+ arguments: vec![("sprite".to_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", "grass1"]]
+ ],
+ "components": [
+ ["visible", { // no capital so invalid
+ "sprite": ["A", "sprite"],
+ "height": ["C", 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": ["C", "0.1"]
+ }]
+ ]
+ })).unwrap_err();
+ assert_eq!(result, "failed to parse parameter constant");
+ }
+
+ #[test]
+ fn unknown_argument_name(){
+ let result = Template::from_json(json!({
+ "arguments": [
+ ["sprite", ["string", "grass1"]]
+ ],
+ "components": [
+ ["Visible", {
+ "sprite": ["A", "sprits"],
+ "height": ["C", 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": ["C", 0.1]
+ }]
+ ]
+ })).unwrap_err();
+ assert_eq!(result, "wrong argument type");
+ }
+}
+
+
+
diff --git a/src/main.rs b/src/main.rs
index f5d2394..524d70f 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -5,7 +5,6 @@ use std::time::Duration;
use std::path::Path;
use std::collections::HashMap;
-use serde_json::json;
mod server;
mod gameserver;
@@ -21,7 +20,6 @@ mod pos;
mod assemblage;
// mod load;
mod compwrapper;
-mod template;
use self::gameserver::GameServer;
use self::server::unixserver::UnixServer;
@@ -29,8 +27,8 @@ use self::server::tcpserver::TcpServer;
use self::server::Server;
use self::assemblages::{Wall, Grass};
use self::room::Room;
-use self::template::{Template, CompParam};
use self::util::ToJson;
+use self::compwrapper::{Parameter, Template, CompParam, ComponentType};
@@ -68,10 +66,10 @@ fn gen_room<'a, 'b>(width: i32, height: i32) -> Room<'a, 'b> {
let wall = Template{
arguments: Vec::new(),
components: vec![
- ("Blocking".to_string(), HashMap::new()),
- ("Visible".to_string(), hashmap!(
- "sprite".to_string() => CompParam::Constant(json!("wall")),
- "height".to_string() => CompParam::Constant(json!(1))
+ (ComponentType::from_str("Blocking").unwrap(), HashMap::new()),
+ (ComponentType::from_str("Visible").unwrap(), hashmap!(
+ "sprite".to_string() => CompParam::Constant(Parameter::String("wall".to_string())),
+ "height".to_string() => CompParam::Constant(Parameter::Float(1.0))
))
]
}.instantiate(Vec::new(), HashMap::new()).unwrap();
diff --git a/src/template.rs b/src/template.rs
index d6f5c81..5e52cc0 100644
--- a/src/template.rs
+++ b/src/template.rs
@@ -1,7 +1,7 @@
use std::collections::HashMap;
-use serde_json::Value;
-use super::compwrapper::CompWrapper;
+use crate::parameter::Parameter;
+use crate::compwrapper::CompWrapper;
pub struct Template {
pub arguments: Vec<String>,
@@ -9,10 +9,10 @@ pub struct Template {
}
impl Template {
- pub fn instantiate(&self, args: Vec<Value>, kwargs: HashMap<String, Value>) -> Option<Vec<CompWrapper>>{
+ 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, &Value> = HashMap::new();
+ 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(())},
@@ -43,6 +43,6 @@ impl Template {
pub enum CompParam {
- Constant(Value),
+ Constant(Parameter),
Argument(String)
}