Skip to content

Commit

Permalink
better error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
marius851000 committed Jan 19, 2024
1 parent 301a79b commit 097d95b
Show file tree
Hide file tree
Showing 7 changed files with 138 additions and 70 deletions.
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,4 @@ map-macro = "0.2.1"
zip = "0.6.3"
fluent-bundle = "0.15.2"
arc-swap = "1.6.0"
display-error-chain = "0.2.0"
9 changes: 4 additions & 5 deletions server/src/extension.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,10 @@ impl HttpResponseBuilderExtension for HttpResponseBuilder {
//TODO: use a fallack (a.k.a english) language?
pub trait FluentLookupInfaillable: Loader {
fn lookup_infaillable(&self, lang: &LanguageIdentifier, text_id: &str) -> String {
self.lookup(lang, text_id)
.unwrap_or_else(|| {
warn!("Missing translation in {} without args: {}", lang, text_id);
format!("<missing translation for {}>", text_id)
})
self.lookup(lang, text_id).unwrap_or_else(|| {
warn!("Missing translation in {} without args: {}", lang, text_id);
format!("<missing translation for {}>", text_id)
})
}

fn lookup_with_args_infaillable<T: AsRef<str> + std::fmt::Debug>(
Expand Down
13 changes: 9 additions & 4 deletions server/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@ use actix_web::{web, App, HttpServer};
use arc_swap::ArcSwap;
use clap::Parser;
use database::HackClient;
use display_error_chain::DisplayErrorChain;
use fluent_templates::ArcLoader;
//use database::MongoDriver;
//use mongodb::options::ClientOptions;
use pmd_hack_storage::{Query, Storage, Tag};
use server::pages::{
connect_majority_token, create_majority_token, css, decompress, disconnect_majority_token,
Expand Down Expand Up @@ -48,8 +47,14 @@ async fn main() {
.build()
.unwrap();

let storage = Storage::load_from_folder(&opts.archive_folder).unwrap();
storage.warn_missing_tags();
let storage = Storage::load_from_folder(&opts.archive_folder);

if !storage.errors.is_empty() {
println!("There are errors that occured during the loading of the datas! :");
for error in &storage.errors {
println!("{}", DisplayErrorChain::new(error).to_string());
}
}

println!("hacks loaded");

Expand Down
2 changes: 1 addition & 1 deletion server/src/pages/tagged.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ pub async fn tagged(
request_data: RequestData,
) -> HttpResponse {
let storage = app_data.storage.load();

let tag_id = path.into_inner();

let base_query = Query::AtLeastOneOfTag(vec![Tag(tag_id.clone())]);
Expand Down
37 changes: 22 additions & 15 deletions storage/src/hack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@ pub enum HackLoadError {
CantOpenFile(#[source] io::Error, PathBuf),
#[error("Can't read or parse the file {1:?} as a hack data file")]
CantParseReadFile(#[source] serde_json::Error, PathBuf),
#[error("The file {0:?} was listed by the hack, but is non-existant")]
MissingFiles(String),
#[error("The file at {0:?} is present in the hacks folder’s, but seems to be not referenced. All files here are public.")]
UnreferencedFile(PathBuf),
#[error("The thing at {0:?} is not a file (an hack folder should only contain files)")]
FileNotFile(PathBuf),
}

pub struct Hack {
Expand All @@ -66,7 +72,10 @@ pub struct Hack {
}

impl Hack {
pub fn load_from_folder(folder: PathBuf, taginfo: &TagInfo) -> Result<Self, HackLoadError> {
pub fn load_from_folder(
folder: PathBuf,
taginfo: &TagInfo,
) -> Result<(Self, Vec<HackLoadError>), HackLoadError> {
let hack_data_path = folder.join("hack.json");
let json_file = File::open(&hack_data_path)
.map_err(|e| HackLoadError::CantOpenFile(e, hack_data_path.clone()))?;
Expand All @@ -84,12 +93,14 @@ impl Hack {
folder,
implied_tags,
};
result.check_files();
Ok(result)
let non_fatal_errors = result.check_files();
Ok((result, non_fatal_errors))
}

/// Check for missing files. Panic if one is found
pub fn check_files(&self) {
/// Check for missing files.
pub fn check_files(&self) -> Vec<HackLoadError> {
let mut errors = Vec::new();

let mut referenced_files = HashSet::new();
for screenshot in &self.data.screenshots {
referenced_files.insert(screenshot.to_string());
Expand All @@ -102,7 +113,7 @@ impl Hack {
for file in read_dir(&self.folder).unwrap().map(|e| e.unwrap()) {
let metadata = metadata(file.path()).unwrap();
if !metadata.is_file() {
panic!("{:?} isn't a file", file.path());
errors.push(HackLoadError::FileNotFile(file.path()))
};
let file_name = file.file_name().to_str().unwrap().to_string();
if file_name == "index.html" {
Expand All @@ -111,19 +122,15 @@ impl Hack {
if referenced_files.contains(&file_name) {
referenced_files.remove(&file_name);
} else {
println!(
"There is a file named {:?} existing, but that is not referenced.",
file.path()
);
errors.push(HackLoadError::UnreferencedFile(file.path()));
}
}

if !referenced_files.is_empty() {
panic!(
"the hack at {:?} references the files {:?} while there are non-existant",
self.folder, referenced_files
);
for missing_file in referenced_files.into_iter() {
errors.push(HackLoadError::MissingFiles(missing_file));
}

errors
}

pub fn all_tags(&self) -> HashSet<Tag> {
Expand Down
139 changes: 94 additions & 45 deletions storage/src/storage.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use super::{Hack, HackLoadError, TagInfoLoadError, Tags};
use crate::TagInfo;
use log::warn;
use std::{
collections::HashMap,
collections::{HashMap, HashSet},
fs::metadata,
io,
path::{Path, PathBuf},
Expand All @@ -19,81 +18,130 @@ pub enum StorageLoadError {
CantGetFileType(#[source] io::Error, PathBuf),
#[error("Cant load the hack in the folder {1:?}")]
CantLoadHack(#[source] HackLoadError, PathBuf),
#[error("A warning is present for the the hack in the folder {1:?}")]
NonFatalErrorLoadHack(#[source] HackLoadError, PathBuf),
#[error("The file name \"{0}\" is an invalid UTF-8 string (sub-file of {1:?})")]
InvalidFilename(String, PathBuf),
#[error("Cant load the tag info file at {1:?}")]
CantLoadTagInfo(#[source] TagInfoLoadError, PathBuf),
#[error("What was supposed to be an hack folder at {0:?} is not a folder")]
NotAFolderForHack(PathBuf),
#[error("The file {1} for the hack {0} does not contain any tag in the category {2}")]
TagForFileLacking(String, String, String),
#[error("The tag {0} is used by {1:?} but doesn't exist in the taginfo file")]
MissingTag(String, HashSet<String>),
}

#[derive(Default)]
pub struct Storage {
pub hacks: HashMap<String, Hack>,
pub tags: Tags,
pub taginfo: TagInfo,
pub errors: Vec<StorageLoadError>,
}

impl Storage {
pub fn load_from_folder(root_folder: &Path) -> Result<Self, StorageLoadError> {
pub fn load_from_folder(root_folder: &Path) -> Self {
let mut errors = Vec::new();
let taginfo_path = root_folder.join("taginfo.json");
let taginfo = TagInfo::load_from_path(&taginfo_path)
.map_err(|e| StorageLoadError::CantLoadTagInfo(e, taginfo_path.clone()))?;
let taginfo = match TagInfo::load_from_path(&taginfo_path)
.map_err(|e| StorageLoadError::CantLoadTagInfo(e, taginfo_path.clone()))
{
Ok(v) => v,
Err(err) => {
errors.push(err);
TagInfo::default()
}
};
let mut result = Self {
hacks: HashMap::new(),
tags: Tags::default(),
taginfo,
errors,
};
let hacks_folder = root_folder.join("hacks");
result.load_all_hacks_from_folder(&hacks_folder)?;
Ok(result)
result.load_all_hacks_from_folder(&hacks_folder);
result.warn_missing_tags();
result
}

pub fn load_all_hacks_from_folder(
&mut self,
hacks_folder: &Path,
) -> Result<(), StorageLoadError> {
for hack_folder_maybe in std::fs::read_dir(&hacks_folder)
.map_err(|e| StorageLoadError::CantListFolder(e, hacks_folder.to_path_buf()))?
{
let hack_folder = hack_folder_maybe
.map_err(|e| StorageLoadError::CantGetSubfile(e, hacks_folder.to_path_buf()))?;
pub fn load_all_hacks_from_folder(&mut self, hacks_folder: &Path) {
let dir_entry_iterator = match std::fs::read_dir(&hacks_folder) {
Ok(v) => v,
Err(e) => {
self.errors.push(StorageLoadError::CantListFolder(
e,
hacks_folder.to_path_buf(),
));
return;
}
};
for hack_folder_maybe in dir_entry_iterator {
let hack_folder = match hack_folder_maybe {
Ok(v) => v,
Err(e) => {
self.errors.push(StorageLoadError::CantGetSubfile(
e,
hacks_folder.to_path_buf(),
));
continue;
}
};

let hack_folder_path = hack_folder.path();
let hack_folder_metadata = metadata(&hack_folder_path)
.map_err(|e| StorageLoadError::CantGetFileType(e, hack_folder_path.clone()))?;
let hack_folder_metadata = match metadata(&hack_folder_path) {
Ok(v) => v,
Err(e) => {
self.errors.push(StorageLoadError::CantGetFileType(
e,
hack_folder_path.clone(),
));
continue;
}
};
if !hack_folder_metadata.is_dir() {
println!(
"warning: {:?} isn't a directory (in the hack list folder)",
hack_folder_path
);
self.errors
.push(StorageLoadError::NotAFolderForHack(hack_folder_path));
continue;
};
let hack_name = match hack_folder.file_name().to_str().map(|x| x.to_string()) {
Some(v) => v,
None => {
return Err(StorageLoadError::InvalidFilename(
self.errors.push(StorageLoadError::InvalidFilename(
hack_folder.file_name().to_string_lossy().to_string(),
hacks_folder.to_path_buf(),
))
));
continue;
}
};
self.load_hack_from_folder(&hack_folder_path, &hack_name)?;
self.load_hack_from_folder(&hack_folder_path, &hack_name);
}
Ok(())
}

fn load_hack_from_folder(
&mut self,
hack_folder_path: &Path,
hack_name: &str,
) -> Result<(), StorageLoadError> {
self.add_hack(
hack_name.to_string(),
Hack::load_from_folder(hack_folder_path.to_path_buf(), &self.taginfo)
.map_err(|e| StorageLoadError::CantLoadHack(e, hack_folder_path.to_path_buf()))?,
)?;
Ok(())
fn load_hack_from_folder(&mut self, hack_folder_path: &Path, hack_name: &str) {
let hack = match Hack::load_from_folder(hack_folder_path.to_path_buf(), &self.taginfo) {
Ok((v, errors)) => {
for error in errors {
self.errors.push(StorageLoadError::NonFatalErrorLoadHack(
error,
hack_folder_path.to_path_buf(),
));
}
v
}
Err(e) => {
self.errors.push(StorageLoadError::CantLoadHack(
e,
hack_folder_path.to_path_buf(),
));
return;
}
};

self.add_hack(hack_name.to_string(), hack);
}

fn add_hack(&mut self, name: String, hack: Hack) -> Result<(), StorageLoadError> {
fn add_hack(&mut self, name: String, hack: Hack) {
for tag in hack.all_tags() {
self.tags.add_hack_with_tag(&tag, name.clone());
}
Expand All @@ -111,24 +159,25 @@ impl Storage {
}
}
if !contain_appropriate_tag {
log::warn!("The file {} for the hack {} doesn't contain a tag with the category {}, as it is required by the category", file.filename, name, category_tag);
self.errors.push(StorageLoadError::TagForFileLacking(
name.to_string(),
file.filename.to_string(),
category_tag.to_string(),
));
}
}
}
}

// actually insert the hack
self.hacks.insert(name, hack);
Ok(())
}

pub fn warn_missing_tags(&self) {
fn warn_missing_tags(&mut self) {
for (tag, users) in &self.tags.tag_list {
if self.taginfo.get_tag(tag).is_none() {
warn!(
"The tag {} is used by {:?} but doesn't exist in the taginfo file",
tag, users
);
self.errors
.push(StorageLoadError::MissingTag(tag.to_string(), users.clone()));
}
}
}
Expand Down

0 comments on commit 097d95b

Please sign in to comment.