diff --git a/README.md b/README.md index 56c6ac6..98a92e2 100644 --- a/README.md +++ b/README.md @@ -199,4 +199,4 @@ GOTO label | 12 | Failed to open archive | Make sure the story file is a zip archive. | | 13 | Unable to setup the characters hashmap. | Make sure the characters.json file exists, is valid JSON and contains valid characters. | | 14 | Unable to read the main story file. | Make sure the story.ha file exists and is readable. | -| 15 | | | +| 15 | Unable to tokenise data | Make sure your code is correctly formatted | diff --git a/server/Cargo.lock b/server/Cargo.lock index e873a43..870c4ab 100644 --- a/server/Cargo.lock +++ b/server/Cargo.lock @@ -462,6 +462,7 @@ dependencies = [ "env_logger", "log", "serde", + "serde-patch", "serde_json", "tokio", "warp", @@ -969,6 +970,16 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-patch" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ba8dcbff6509fa9394810d943e0e9d486a4256c23f6fcea8a58865fbe8260c4" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "serde_core" version = "1.0.228" diff --git a/server/Cargo.toml b/server/Cargo.toml index 5e67b23..f01156e 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -26,6 +26,7 @@ unwrap_used = "warn" env_logger = "0.11.10" log = "0.4.29" serde = { version = "1.0.228", features = ["derive"] } +serde-patch = "0.2.3" serde_json = "1.0.149" tokio = { version = "1.51.0", features = ["rt-multi-thread","macros"] } warp = { version = "0.4.2", features = ["server"] } diff --git a/server/src/character.rs b/server/src/character.rs index 47996f9..b4a4a70 100644 --- a/server/src/character.rs +++ b/server/src/character.rs @@ -7,6 +7,7 @@ use crate::{ info, Deserialize, Serialize, + apply_mut, Mutex, Arc, }; @@ -27,7 +28,7 @@ pub struct Character torso_shape: String, arm_shape: String, leg_shape: String, - hair_color: String, + hair_color: String, // TODO RGB enum clothing: Clothing, } #[derive(Debug,Deserialize,Serialize,Clone,Default)] @@ -39,30 +40,11 @@ pub struct Clothing shoes: String, } impl Character { - // Big ass ugly function because rust doesn't support referencing struct entries by string - pub fn set_field(&mut self, field: &str, value: &str) -> Result<(), ()> { - match field { - "name" => self.name = value.to_string(), - "gender" => self.gender = value.to_string(), - "eye_color" => self.eye_color = value.to_string(), - "pronoun_subject" => self.pronoun_subject = value.to_string(), - "pronoun_object" => self.pronoun_object = value.to_string(), - "pronoun_deppos" => self.pronoun_deppos = value.to_string(), - "pronoun_indpos" => self.pronoun_indpos = value.to_string(), - "pronoun_reflex" => self.pronoun_reflex = value.to_string(), - "head_shape" => self.head_shape = value.to_string(), - "hair_style" => self.hair_style = value.to_string(), - "torso_shape" => self.torso_shape = value.to_string(), - "arm_shape" => self.arm_shape = value.to_string(), - "leg_shape" => self.leg_shape = value.to_string(), - "hair_color" => self.hair_color = value.to_string(), - "clothing.top" => self.clothing.top = value.to_string(), - "clothing.bottom" => self.clothing.bottom = value.to_string(), - "clothing.shoes" => self.clothing.shoes = value.to_string(), - - _ => return Err(()), - } - + // Big ass ugly match case + pub fn set_field(&mut self, field: &str, value: &str) -> Result<(), String> { + let patch = format!("{{ \"{field}\": \"{value}\" }}"); + apply_mut(self, patch) + .map_err(|_| "Invalid field".to_string())?; Ok(()) } } diff --git a/server/src/main.rs b/server/src/main.rs index 6d93a06..daccc71 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -27,6 +27,7 @@ use serde:: Serialize }; use serde_json::json; +use serde_patch::apply_mut; use zip:: { ZipArchive, @@ -51,17 +52,9 @@ async fn main() exit(10); }); let file = File::open(format!("../stories/{file_name}")) // Get the file - .unwrap_or_else - (|err| { - error!("Failed to open file: {err}"); - exit(11); - }); + .unwrap_or_exit("Failed to read file.", 11); let mut archive = ZipArchive::new(file) // Open the archive - .unwrap_or_else - (|err| { - error!("Failed to open archive: {err}"); - exit(12); - }); + .unwrap_or_exit("Failed to open archive", 12); // Setup the characters hashmap which will store each character in it as a Character struct let characters = match character::character_parse(&mut archive) { @@ -102,11 +95,7 @@ async fn main() // Read the file and split it into tokens // file read from a zip file /story.ha let mut story_file = archive.by_name("story.ha") - .unwrap_or_else - (|err| { - error!("Unable to read story file: {err}"); - exit(14); - }); + .unwrap_or_exit("Unable to read story file", 14); let mut file_contents = String::new(); story_file.read_to_string(&mut file_contents) .unwrap_or_else @@ -121,11 +110,13 @@ async fn main() { warn!("No END statement, story may exit unexpectedly"); } - let (tokens, labels,_) = tokenise::tokenise(&space_seperated, 0).unwrap(); + let (tokens, labels,_) = tokenise::tokenise(&space_seperated, 0) + .unwrap_or_exit("Unable to tokenise data", 15); debug!("{tokens:?}"); let data_clone2 = Arc::clone(&data_to_send); let characters_clone2 = Arc::clone(&characters); // Run the parsing process for the DSL + info!("DSL parsing begun"); match parsing::token_parse(&tokens, &characters_clone2, &data_clone2, &rx) { // Exit with error or success diff --git a/server/src/parsing.rs b/server/src/parsing.rs index fdf14aa..ad11fe5 100644 --- a/server/src/parsing.rs +++ b/server/src/parsing.rs @@ -20,13 +20,12 @@ mod character_parse; // Parse the tokens in a file // Returns success or an error string pub fn token_parse( - tokens: &Vec, + tokens: &[tokenise::Token], characters: &Arc>>, data_to_send: &Arc>, rx: &Receiver<(bool,usize,String)>, ) -> Result<(),String> { - info!("DSL parsing begun"); let mut index: usize = 0; if rx.recv().is_err() { diff --git a/server/src/parsing/character_parse.rs b/server/src/parsing/character_parse.rs index 143b785..b07d5cd 100644 --- a/server/src/parsing/character_parse.rs +++ b/server/src/parsing/character_parse.rs @@ -20,7 +20,7 @@ use crate:: pub fn character_parse ( index: usize, - tokens: &Vec, + tokens: &[tokenise::Token], character_name: String, characters: &Arc>>, data_to_send: &Arc>, @@ -44,7 +44,7 @@ pub fn character_parse let output = tokenise::get_string_token(tokens, sum_index) .map_err(|err| (err, index))?; debug!("Saying {output}"); - api::modify_data(data_to_send, "output".to_string(), output.to_string(), character_name, vec![]); + api::modify_data(data_to_send, "output".to_string(), output, character_name, vec![]); }, // Change the property of the selected character eg @tim CHANGE name "Bill Buffins" // will change the character with ID tim to "Bill Buffins"; a character's ID cannot change @@ -71,7 +71,7 @@ pub fn character_parse sum_index += 1; let content = tokenise::get_string_token(tokens, sum_index) .map_err(|err| (err, index))?; - api::modify_data(data_to_send, token.to_lowercase(), content.to_string(), character_name, vec![]); + api::modify_data(data_to_send, token.to_lowercase(), content, character_name, vec![]); }, // Catch all condition, if the instruction is unrecognised as a // character command diff --git a/server/src/tokenise.rs b/server/src/tokenise.rs index 5cc74ea..8fdcef9 100644 --- a/server/src/tokenise.rs +++ b/server/src/tokenise.rs @@ -6,28 +6,27 @@ use crate:: #[derive(Debug, Clone)] pub enum Token { - Null, String(String), - Object(Vec), + Object(Vec), } -pub fn get_string_token(tokens: &Vec, index: usize) +pub fn get_string_token(tokens: &[Token], index: usize) -> Result { match tokens.get(index) { - Some(Token::String(s)) => return Ok(s.clone()), - Some(_) => return Err("Unexpected token".to_string()), - None => return Err("File unexpectedly reached termination point".to_string()), + Some(Token::String(s)) => Ok(s.clone()), + Some(_) => Err("Unexpected token".to_string()), + None => Err("File unexpectedly reached termination point".to_string()), } } -pub fn get_object_token(tokens: &Vec, index: usize) +pub fn get_object_token(tokens: &[Token], index: usize) -> Result<&Vec, String> { match tokens.get(index) { - Some(Token::Object(v)) => return Ok(v), - Some(_) => return Err("Unexpected token".to_string()), - None => return Err("File unexpectedly reached termination point".to_string()), + Some(Token::Object(v)) => Ok(v), + Some(_) => Err("Unexpected token".to_string()), + None => Err("File unexpectedly reached termination point".to_string()), } } @@ -42,19 +41,19 @@ pub fn tokenise(space_seperated: &Vec<&str>, mut index: usize) while index < space_seperated.len() { let mut item = space_seperated[index].to_string(); - let mut object: Vec; - if item.starts_with("\"") + let object: Vec; + if item.starts_with('"') // TODO support ' { - (index, item) = match tokenise_string(&space_seperated, index) + (index, item) = match tokenise_string(space_seperated, index) { - Some((index,item)) => (index, item), + Some((index,item)) => (index,item), None => exit(1), }; tokenised_data.push(Token::String(item)); } else if item == "{" { - (object, labels, index) = match tokenise(&space_seperated, index+1) // !WARNING! recursive call + (object, labels, index) = match tokenise(space_seperated, index+1) // !WARNING! recursive call { Ok((object,labels,index)) => (object,labels,index), Err(err) => return Err(err), @@ -68,10 +67,10 @@ pub fn tokenise(space_seperated: &Vec<&str>, mut index: usize) else { tokenised_data.push(Token::String(item)); } index += 1; } - return Ok((tokenised_data, labels, 0)); + Ok((tokenised_data, labels, 0)) } -fn tokenise_string(space_seperated: &Vec<&str>, mut index: usize) +fn tokenise_string(space_seperated: &[&str], mut index: usize) -> Option<(usize, String)> { let mut string = String::new(); @@ -80,18 +79,15 @@ fn tokenise_string(space_seperated: &Vec<&str>, mut index: usize) string += chars.as_str(); for item in &space_seperated[index+1..] { - if item.ends_with("\"") + if item.ends_with('"') { let mut chars = item.chars(); chars.next_back(); string += format!(" {}", chars.as_str()).as_str(); - break - } - else - { - string += format!(" {}", item).as_str(); + return Some((index+1,string)) } + string += format!(" {item}").as_str(); index += 1; } - return Some((index+1,string)) + None } diff --git a/server/src/traits.rs b/server/src/traits.rs index c1b42e0..91dda8e 100644 --- a/server/src/traits.rs +++ b/server/src/traits.rs @@ -4,6 +4,8 @@ use crate:: exit, }; +// TODO support Options +// TODO pass error message back pub trait UnwrapOrExit { fn unwrap_or_exit(self, error_message: &str, error_code: i32) -> T; diff --git a/stories/story.ha b/stories/story.ha index 677b1be..298181a 100644 --- a/stories/story.ha +++ b/stories/story.ha @@ -1,5 +1,6 @@ @tim says "hello world, it's a good day" @tim change name "Timothy Fineshooter" +@tim change boop "Timothy Fineshooter" @tim to fr choice "choice numero uno" { @tim animate wave diff --git a/stories/test.zip b/stories/test.zip index 37dd0d4..3070a5f 100644 Binary files a/stories/test.zip and b/stories/test.zip differ