From 15da8ce3af4e943939d6976ccfe5c46785638c7e Mon Sep 17 00:00:00 2001 From: troido Date: Wed, 8 Apr 2020 18:03:44 +0200 Subject: don't ignore loading errors anymore --- src/persistence.rs | 82 +++++++++++++++++++++++++++++++++++++----------------- src/world.rs | 33 ++++++++++++++++------ 2 files changed, 81 insertions(+), 34 deletions(-) (limited to 'src') diff --git a/src/persistence.rs b/src/persistence.rs index b97f857..1174ceb 100644 --- a/src/persistence.rs +++ b/src/persistence.rs @@ -2,6 +2,7 @@ use std::path::{PathBuf, Path}; use std::fs; use std::env; +use std::io::ErrorKind; use serde_json; use serde_json::{Value, json}; use crate::{ @@ -10,19 +11,30 @@ use crate::{ savestate::SaveState, playerstate::PlayerState, Timestamp, - Result, - aerr + aerr, + errors::AnyError }; + +pub enum LoaderError { + MissingResource(AnyError), + InvalidResource(AnyError) +} + +macro_rules! inv { + ($code:expr) => {($code).map_err(|err| LoaderError::InvalidResource(Box::new(err)))} +} + + pub trait PersistentStorage { - fn load_room(&self, id: RoomId) -> Result; - fn load_player(&self, id: PlayerId) -> Result; - fn load_world_meta(&self) -> Result; + fn load_room(&self, id: RoomId) -> Result; + fn load_player(&self, id: PlayerId) -> Result; + fn load_world_meta(&self) -> Result; - fn save_room(&self, id: RoomId, state: SaveState) -> Result<()>; - fn save_player(&self, id: PlayerId, sate: PlayerState) -> Result<()>; - fn save_world_meta(&self, time: Timestamp) -> Result<()>; + fn save_room(&self, id: RoomId, state: SaveState) -> Result<(), AnyError>; + fn save_player(&self, id: PlayerId, sate: PlayerState) -> Result<(), AnyError>; + fn save_world_meta(&self, time: Timestamp) -> Result<(), AnyError>; } @@ -56,43 +68,61 @@ impl FileStorage { impl PersistentStorage for FileStorage { - fn load_room(&self, id: RoomId) -> Result { + fn load_room(&self, id: RoomId) -> Result { let mut path = self.directory.clone(); path.push("rooms"); let fname = id.to_string() + ".save.json"; path.push(fname); - let text = fs::read_to_string(path)?; - let json: Value = serde_json::from_str(&text)?; - let state = SaveState::from_json(&json)?; + let text = fs::read_to_string(path).map_err(|err| { + if err.kind() == ErrorKind::NotFound { + LoaderError::MissingResource(Box::new(err)) + } else { + LoaderError::InvalidResource(Box::new(err)) + } + })?; + let json: Value = inv!(serde_json::from_str(&text))?; + let state = inv!(SaveState::from_json(&json))?; Ok(state) } - fn load_player(&self, id: PlayerId) -> Result { + fn load_player(&self, id: PlayerId) -> Result { let mut path = self.directory.clone(); path.push("players"); let fname = id.to_string() + ".save.json"; path.push(fname); - let text = fs::read_to_string(path)?; - let json: Value = serde_json::from_str(&text)?; - let state = PlayerState::from_json(&json)?; + let text = fs::read_to_string(path).map_err(|err| { + if err.kind() == ErrorKind::NotFound { + LoaderError::MissingResource(Box::new(err)) + } else { + LoaderError::InvalidResource(Box::new(err)) + } + })?; + let json: Value = inv!(serde_json::from_str(&text))?; + let state = inv!(PlayerState::from_json(&json))?; Ok(state) } - fn load_world_meta(&self) -> Result { + fn load_world_meta(&self) -> Result { let mut path = self.directory.clone(); path.push("world.save.json"); - let text = fs::read_to_string(path)?; - let json: Value = serde_json::from_str(&text)?; + let text = fs::read_to_string(path).map_err(|err| { + if err.kind() == ErrorKind::NotFound { + LoaderError::MissingResource(Box::new(err)) + } else { + LoaderError::InvalidResource(Box::new(err)) + } + })?; + let json: Value = inv!(serde_json::from_str(&text))?; Ok( Timestamp( - json - .get("steps").ok_or(aerr!("world data does not have steps"))? - .as_i64().ok_or(aerr!("timestamp not an int"))? + inv!(inv!(json + .get("steps").ok_or(aerr!("world data does not have steps")))? + .as_i64().ok_or(aerr!("timestamp not an int")))? ) ) } - fn save_room(&self, id: RoomId, state: SaveState) -> Result<()> { + fn save_room(&self, id: RoomId, state: SaveState) -> Result<(), AnyError> { let mut path = self.directory.clone(); path.push("rooms"); fs::create_dir_all(&path)?; @@ -103,7 +133,7 @@ impl PersistentStorage for FileStorage { Ok(()) } - fn save_player(&self, id: PlayerId, state: PlayerState) -> Result<()> { + fn save_player(&self, id: PlayerId, state: PlayerState) -> Result<(), AnyError> { let mut path = self.directory.clone(); path.push("players"); fs::create_dir_all(&path)?; @@ -114,7 +144,7 @@ impl PersistentStorage for FileStorage { Ok(()) } - fn save_world_meta(&self, time: Timestamp) -> Result<()> { + fn save_world_meta(&self, time: Timestamp) -> Result<(), AnyError> { let mut path = self.directory.clone(); fs::create_dir_all(&path)?; path.push("world.save.json"); @@ -123,7 +153,7 @@ impl PersistentStorage for FileStorage { } } -fn write_file_safe, C: AsRef<[u8]>>(path: P, contents: C) -> Result<()> { +fn write_file_safe, C: AsRef<[u8]>>(path: P, contents: C) -> Result<(), AnyError> { let temppath = path .as_ref() .with_file_name( diff --git a/src/world.rs b/src/world.rs index d7b5555..9de51d7 100644 --- a/src/world.rs +++ b/src/world.rs @@ -9,7 +9,7 @@ use crate::{ room::Room, room, worldloader::WorldLoader, - persistence::PersistentStorage, + persistence::{PersistentStorage, LoaderError}, playerstate::{PlayerState, RoomPos}, Encyclopedia, controls::Control, @@ -36,9 +36,18 @@ pub struct World<'a, 'b> { impl <'a, 'b>World<'a, 'b> { pub fn new(encyclopedia: Encyclopedia, template_loader: WorldLoader, persistence: Box, default_room: RoomId) -> Self { + let time = match persistence.load_world_meta() { + Ok(time) => {time} + Err(LoaderError::MissingResource(_)) => { + Timestamp(1000000) + } + Err(LoaderError::InvalidResource(err)) => { + panic!("Invalid world meta: {:?}", err) + } + }; World { template_loader, - time: persistence.load_world_meta().unwrap_or(Timestamp(1000000)), + time, persistence, default_room, encyclopedia: encyclopedia, @@ -69,8 +78,12 @@ impl <'a, 'b>World<'a, 'b> { room.load_from_template(&template)?; room }; - if let Ok(state) = self.persistence.load_room(id.clone()){ - room.load_saved(&state); + match self.persistence.load_room(id.clone()){ + Ok(state) => { + room.load_saved(&state); + } + Err(LoaderError::MissingResource(_)) => {} + Err(LoaderError::InvalidResource(err)) => {return Err(err);} } let last_time = self.time - 1; if room.get_time() < last_time { @@ -91,11 +104,15 @@ impl <'a, 'b>World<'a, 'b> { } pub fn add_player(&mut self, playerid: &PlayerId) -> Result<()> { - let mut state = self.persistence - .load_player(playerid.clone()) - .unwrap_or_else(|_err| // todo: what if player exists but can't be loaded for another reason? + let mut state = match self.persistence.load_player(playerid.clone()) { + Ok(state) => {state} + Err(LoaderError::MissingResource(_)) => { PlayerState::new(playerid.clone()) - ); + } + Err(LoaderError::InvalidResource(err)) => { + return Err(err) + } + }; state.id = playerid.clone(); if state.room == Some(purgatory::purgatory_id()){ state.respawn(); -- cgit