use log::warn; use serde_json; use std::error::Error; use std::fs; use std::path; extern crate custom_error; use custom_error::custom_error; custom_error! {DatabaseError NotDirectory{path: String} = "Path to database should point at the directory: {path}", } struct File { name: String, contents: serde_json::Value, } struct Group { name: String, files: Vec, } struct Category { name: String, groups: Vec, } pub struct Database { storage: path::PathBuf, contents: Vec, } impl Database { pub fn new(storage: &path::Path) -> Result> { if !storage.is_dir() { return Err(Box::new(DatabaseError::NotDirectory { path: storage.display().to_string(), })); } let mut contents = Vec::new(); for entry in fs::read_dir(storage)? { let entry = entry?; let path = entry.path(); if !path.is_dir() { warn!( r#"File {} found where only category directories are supposed to be"#, path.display() ); } else { let category = load_category(&path)?; contents.push(category); } } Ok(Database { storage: storage.to_path_buf(), contents, }) } } fn load_category(category_path: &path::Path) -> Result> { let mut groups = Vec::new(); for entry in fs::read_dir(category_path)? { let entry = entry?; let path = entry.path(); if !path.is_dir() { warn!( r#"File {} found where only group directories are supposed to be"#, path.display() ); } else { let group = load_group(&path)?; groups.push(group); } } Ok(Category { name: get_file_name(category_path), groups, }) } fn load_group(group_path: &path::Path) -> Result> { let mut files = Vec::new(); for entry in fs::read_dir(group_path)? { let entry = entry?; let path = entry.path(); if path.is_dir() { warn!( r#"Directory {} found where only data files are supposed to be"#, path.display() ); } else { let file_contents = fs::read_to_string(&path)?; files.push(File { name: get_file_name(path.as_path()), contents: serde_json::from_str(&file_contents)?, }); } } Ok(Group { name: get_file_name(group_path), files, }) } fn get_file_name(path: &path::Path) -> String { path.file_stem() .and_then(|x| x.to_str()) .unwrap_or_default() .to_string() }