diff --git a/src/quest/error.rs b/src/quest/error.rs index 60c93f6..55ddbcf 100644 --- a/src/quest/error.rs +++ b/src/quest/error.rs @@ -10,8 +10,10 @@ pub enum QuestError { IsNotAFile(PathBuf), /// std::io::Error happenned when loading IoError(std::io::Error), + /// toml::ser::Error happened when loading + TomlSerializeError(toml::ser::Error), /// toml::de::Error happened when loading - TomlError(toml::de::Error) + TomlDeserializeError(toml::de::Error), } impl fmt::Display for QuestError { @@ -19,7 +21,8 @@ impl fmt::Display for QuestError { match self { QuestError::IsNotAFile(path) => write!(f, "{:?} is not a file", path), QuestError::IoError(error) => write!(f, "io error: {error}"), - QuestError::TomlError(error) => write!(f, "parse error: {error}") + QuestError::TomlSerializeError(error) => write!(f, "serialize error: {error}"), + QuestError::TomlDeserializeError(error) => write!(f, "parse error: {error}") } } } diff --git a/src/quest/mod.rs b/src/quest/mod.rs index 64607cb..d675cc1 100644 --- a/src/quest/mod.rs +++ b/src/quest/mod.rs @@ -2,7 +2,7 @@ pub mod error; -use std::path::PathBuf; +use std::{fs, io::Write, path::PathBuf}; use serde::{ Serialize, Deserialize }; use error::QuestError; @@ -76,15 +76,104 @@ impl Default for Quest { impl Quest { /// Parse quest TOML or return error + /// + /// # Examples + /// ```rust + /// use squad_quest::quest::{Quest,error::QuestError}; + /// # fn main() { + /// # let _ = wrapper(); + /// # } + /// + /// # fn wrapper() -> Result<(), QuestError> { + /// let path = "quests/0.toml".into(); + /// + /// let quest = Quest::load(path)?; + /// # + /// # Ok(()) + /// # } + /// ``` pub fn load(path: PathBuf) -> Result { match std::fs::read_to_string(path) { Ok(string) => { match toml::from_str::(&string) { Ok(quest) => Ok(quest), - Err(error) => Err(QuestError::TomlError(error)) + Err(error) => Err(QuestError::TomlDeserializeError(error)) } }, Err(error) => Err(QuestError::IoError(error)) } } + + /// Check if given file is a quest, then delete it or raise an error. + /// If file is not a quest, raises [QuestError::TomlDeserializeError] + /// + /// # Examples + /// ```rust + /// use squad_quest::quest::{Quest,error::QuestError}; + /// + /// let path = "quests/0.toml".into(); + /// + /// if let Err(error) = Quest::delete(path) { + /// // handle the error + /// } + /// ``` + pub fn delete(path: PathBuf) -> Result<(), QuestError> { + match Quest::load(path.clone()) { + Ok(_) => { + if let Err(error) = fs::remove_file(path) { + return Err(QuestError::IoError(error)); + } + + Ok(()) + }, + Err(error) => Err(error) + } + } + + /// Save quest to given folder in TOML format. + /// File will be saved as `{id}.toml`. + /// If file exists, this method will override it. + /// + /// # Examples + /// ```rust + /// # fn main() { + /// use squad_quest::quest::{Quest,error::QuestError}; + /// use std::path::PathBuf; + /// + /// let quest = Quest::default(); + /// + /// let path: PathBuf = "quests".into(); + /// # let path2 = path.clone(); + /// + /// if let Err(error) = quest.save(path) { + /// // handle the error + /// } + /// # let filename = format!("{}.toml", quest.id); + /// # let _ = Quest::delete(path2.with_file_name(filename)); + /// # } + /// ``` + pub fn save(&self, path: PathBuf) -> Result<(), QuestError> { + let filename = format!("{}.toml", self.id); + let full_path = path.with_file_name(filename); + + let str = match toml::to_string_pretty(&self) { + Ok(string) => string, + Err(error) => { + return Err(QuestError::TomlSerializeError(error)); + } + }; + + let mut file = match fs::File::create(full_path) { + Ok(f) => f, + Err(error) => { + return Err(QuestError::IoError(error)); + } + }; + + if let Err(error) = file.write_all(str.as_bytes()) { + return Err(QuestError::IoError(error)); + } + + Ok(()) + } } diff --git a/tests/io.rs b/tests/io.rs new file mode 100644 index 0000000..c186ad0 --- /dev/null +++ b/tests/io.rs @@ -0,0 +1,29 @@ +use squad_quest::{config::Config,quest::{error::{QuestError}, Quest}}; +use std::path::PathBuf; + +const CONFIG_PATH: &str = "tests/io/config.toml"; + +// Note: Quest::delete uses Quest::load underneath, +// and Quest::save can override files, +// so this test covers full quest CRUD +#[test] +fn quest_crud() -> Result<(), QuestError> { + let config = Config::load(CONFIG_PATH.into()); + + let mut quests_path = PathBuf::from(CONFIG_PATH).parent().unwrap().to_owned(); + quests_path.push(config.quests_path); + + let quest = Quest::default(); + + println!("{:?}", quests_path.clone()); + + quest.save(quests_path.clone())?; + + let filename = format!("{}.toml", quest.id); + + let delete_path = quests_path.with_file_name(filename); + + Quest::delete(delete_path)?; + + Ok(()) +} diff --git a/tests/cfg/config.toml b/tests/io/config.toml similarity index 100% rename from tests/cfg/config.toml rename to tests/io/config.toml diff --git a/tests/io/quests/.placeholder b/tests/io/quests/.placeholder new file mode 100644 index 0000000..aa442f6 --- /dev/null +++ b/tests/io/quests/.placeholder @@ -0,0 +1 @@ +this file exists because git requires directory to have a file in order to commit it diff --git a/tests/main.rs b/tests/main.rs index af0742d..321674a 100644 --- a/tests/main.rs +++ b/tests/main.rs @@ -1,8 +1,10 @@ use squad_quest::{config::Config, quest::Quest}; +static CONFIG_PATH: &str = "./tests/main/config.toml"; + #[test] fn load_quests() { - let config = Config::load("./tests/cfg/config.toml".into()); + let config = Config::load(CONFIG_PATH.into()); let quests = config.load_quests(); assert_eq!(quests.len(), 2); @@ -11,7 +13,7 @@ fn load_quests() { #[test] fn empty_quest_is_default() { // First loaded quest should be 0.toml, which is empty - let config = Config::load("./tests/cfg/config.toml".into()); + let config = Config::load(CONFIG_PATH.into()); let mut quests = config.load_quests(); quests.sort_by(|a,b| a.id.cmp(&b.id)); let quest = quests.first().unwrap(); @@ -23,7 +25,7 @@ fn empty_quest_is_default() { #[test] fn quest_one() { - let config = Config::load("./tests/cfg/config.toml".into()); + let config = Config::load(CONFIG_PATH.into()); let quests = config.load_quests(); let quest = quests.iter().find(|q| q.id == 1).unwrap(); diff --git a/tests/main/config.toml b/tests/main/config.toml new file mode 100644 index 0000000..87858ef --- /dev/null +++ b/tests/main/config.toml @@ -0,0 +1,10 @@ +# Default config + +# Path to quests folder relative to config +quests_path = "quests" + +# Path to accounts folder relative to config +accounts_path = "accounts" + +# Path to map .toml file relative to config +map = "map.toml" diff --git a/tests/cfg/quests/0.toml b/tests/main/quests/0.toml similarity index 100% rename from tests/cfg/quests/0.toml rename to tests/main/quests/0.toml diff --git a/tests/cfg/quests/1.toml b/tests/main/quests/1.toml similarity index 100% rename from tests/cfg/quests/1.toml rename to tests/main/quests/1.toml diff --git a/tests/cfg/quests/2.toml b/tests/main/quests/2.toml similarity index 100% rename from tests/cfg/quests/2.toml rename to tests/main/quests/2.toml