//! Text-based quests and user solutions for them pub mod error; use std::{fs, io::Write, path::PathBuf}; use serde::{ Serialize, Deserialize }; use error::QuestError; /// Difficulty of the quest #[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy)] pub enum QuestDifficulty { /// Easy quest Easy, /// Normal quest Normal, /// Hard quest Hard, /// Special case of hard quests. Also is a default value for enum Secret } impl Default for QuestDifficulty { fn default() -> Self { QuestDifficulty::Secret } } fn default_name() -> String { "Slay the dragon".to_string() } fn default_description() -> String { "Just do it in any way".to_string() } fn default_answer() -> String { "Attachment should show that the dragon was slain".to_string() } /// Quest struct #[derive(Serialize, Deserialize, Debug, PartialEq)] #[serde(default)] pub struct Quest { /// Quest identifier pub id: u16, /// Difficulty of this quest pub difficulty: QuestDifficulty, /// Reward for the quest pub reward: u32, /// Visible quest name pub name: String, /// Visible quest description pub description: String, /// Quest answer, available for admins pub answer: String, } impl Default for Quest { fn default() -> Self { Quest { id: u16::default(), difficulty: QuestDifficulty::default(), reward: u32::default(), name: default_name(), description: default_description(), answer: default_answer() } } } 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::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 mut full_path = path; full_path.push(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(()) } }