diff --git a/src/account/mod.rs b/src/account/mod.rs index a0b06c6..0960db8 100644 --- a/src/account/mod.rs +++ b/src/account/mod.rs @@ -1,4 +1,4 @@ -//! Module for handling user accounts +//! User accounts use serde::{ Serialize, Deserialize }; @@ -10,11 +10,11 @@ fn default_id() -> String { #[derive(Serialize, Deserialize)] pub struct Account { - /// User id, specific to used service + /// User identifier, specific to used service #[serde(default = "default_id")] pub id: String, - /// User balance, + /// User balance #[serde(default)] pub balance: u32, diff --git a/src/config/mod.rs b/src/config/mod.rs index 45aca46..178bb79 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -1,8 +1,10 @@ -//! Module for handling configuration +//! Configuration file that handles (de-)serializing other components -use std::path::PathBuf; +use std::{fs::{self, DirEntry},path::PathBuf}; use serde::Deserialize; +use crate::quest::{Quest,error::QuestError}; + /// Struct for containing paths to other (de-)serializable things #[derive(Deserialize)] pub struct Config { @@ -30,10 +32,35 @@ impl Default for Config { } } +fn handle_quest_entry(quest_entry: DirEntry) -> Result{ + let filetype = quest_entry.file_type(); + if let Err(error) = filetype { + return Err(QuestError::IoError(error)); + } + + let path = quest_entry.path(); + + let filetype = filetype.unwrap(); + if !filetype.is_file() { + return Err(QuestError::IsNotAFile(path)); + } + + Quest::load(path) +} + impl Config { /// Deserialize config from TOML + /// Logs all errors and returns default config if that happens + /// + /// # Examples + /// ```rust + /// use squad_quest::config::Config; + /// + /// let path = "cfg/config.toml".into(); + /// let config = Config::load(path); + /// ``` pub fn load(path: PathBuf) -> Self { - match std::fs::read_to_string(path) { + match fs::read_to_string(path) { Ok(string) => { match toml::from_str::(&string) { Ok(conf) => { @@ -52,4 +79,51 @@ impl Config { } } } + + /// Load [Vec]<[Quest]> from quests folder. + /// Also logs errors and counts successfully loaded quests. + /// + /// # Examples + /// ```rust + /// use squad_quest::{config::Config, quest::Quest}; + /// + /// + /// let path = "cfg/config.toml".into(); + /// let config = Config::load(path); + /// let quests = config.load_quests(); + /// + /// for quest in quests { + /// println!("Quest #{} {}", quest.id, quest.name); + /// } + /// ``` + pub fn load_quests(&self) -> Vec { + let mut out_vec = Vec::new(); + + match fs::read_dir(&self.quests_path) { + Ok(iter) => { + for entry in iter { + match entry { + Ok(quest_entry) => { + match handle_quest_entry(quest_entry) { + Ok(quest) => out_vec.push(quest), + Err(error) => { + eprintln!("Error on loading single quest: {error}"); + } + } + }, + Err(error) => { + eprintln!("Error on loading single quest: {error}"); + } + } + } + }, + Err(error) => { + eprintln!("Error on loading quests: {error}"); + } + } + + println!("Loaded {} quests successfully", out_vec.len()); + + out_vec + } } diff --git a/src/map/mod.rs b/src/map/mod.rs index 8ed0fd6..d4eb205 100644 --- a/src/map/mod.rs +++ b/src/map/mod.rs @@ -1,4 +1,5 @@ -//! Module for handling map, which is a graph of rooms - +//! Map, a.k.a. a graph of rooms #![allow(dead_code)] -struct Map; + +/// Graph for room nodes +pub struct Map; diff --git a/src/quest/error.rs b/src/quest/error.rs new file mode 100644 index 0000000..60c93f6 --- /dev/null +++ b/src/quest/error.rs @@ -0,0 +1,25 @@ +//! Module for handling quest loading errors + +use std::{fmt, path::PathBuf}; + +/// Error raised when trying to parse quest file +#[derive(Debug)] +#[non_exhaustive] +pub enum QuestError { + /// Given path is not a file + IsNotAFile(PathBuf), + /// std::io::Error happenned when loading + IoError(std::io::Error), + /// toml::de::Error happened when loading + TomlError(toml::de::Error) +} + +impl fmt::Display for QuestError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + 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}") + } + } +} diff --git a/src/quest/mod.rs b/src/quest/mod.rs index 4842709..fbd818b 100644 --- a/src/quest/mod.rs +++ b/src/quest/mod.rs @@ -1,6 +1,11 @@ -//! Module for handling text-based quests and user answers +//! Text-based quests and user solutions for them + +pub mod error; + +use std::path::PathBuf; use serde::{ Serialize, Deserialize }; +use error::QuestError; /// Difficulty of the quest #[derive(Serialize, Deserialize)] @@ -60,3 +65,18 @@ pub struct Quest { #[serde(default = "default_answer")] pub answer: String, } + +impl Quest { + /// Parse quest TOML or return error + 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::IoError(error)) + } + } +}