use std::fs::File;
use std::{fs, io};
use std::collections::HashMap;
use std::path::{Path, PathBuf};
use crate::{StaticDegree, Degrees, Lecture};
use crate::datasource::{LoadResult, SaveResult, ReadWriteDataSource, ReadOnlyDataSource};
pub struct FSDataSource {
caches: HashMap<&'static StaticDegree, FileSystemCache>
}
impl ReadOnlyDataSource for FSDataSource {
fn load_lectures(&self, degree: &'static StaticDegree) -> LoadResult {
self.cache_for_degree(degree)
.load_lectures()
.map_err(|err| format!("Could not load cache for degree {} due to {}", degree.id, err))
}
}
impl ReadWriteDataSource for FSDataSource {
fn save_lectures(&mut self, degree: &'static StaticDegree, lectures: &[Lecture]) -> SaveResult {
self.cache_for_degree(degree)
.save_lectures(lectures)
.map_err(|err| format!("Could not save cache for degree {} due to {}", degree.id, err))
}
}
impl FSDataSource {
pub fn new(path: String) -> Self {
let mut caches = HashMap::new();
for degree in Degrees::all() {
caches.insert(degree, FileSystemCache { path: Path::join(path.as_ref(), degree.id)});
}
FSDataSource { caches }
}
fn cache_for_degree(&self, degree: &'static StaticDegree) -> &FileSystemCache {
&self.caches[degree]
}
}
struct FileSystemCache {
path: PathBuf
}
impl FileSystemCache {
pub fn load_lectures(&self) -> io::Result<Vec<Lecture>> {
load_cache_from(&self.path)
}
pub fn save_lectures(&self, lectures: &[Lecture]) -> io::Result<()> {
save_cache_to(&self.path, lectures)
}
}
fn load_cache_from<P: AsRef<Path>>(path: &P) -> io::Result<Vec<Lecture>> {
let file = open_cache(path)?;
let cache = serde_json::from_reader(file)?;
Ok(cache)
}
fn save_cache_to<P: AsRef<Path>>(path: &P, cache: &[Lecture]) -> io::Result<()> {
let file = create_cache(path)?;
serde_json::to_writer(file, cache)?;
Ok(())
}
#[allow(dead_code)]
fn save_cache_pretty<P: AsRef<Path>>(path: &P, cache: &[Lecture]) -> io::Result<()> {
let file = create_cache(path)?;
serde_json::to_writer_pretty(file, cache)?;
Ok(())
}
fn create_cache<P: AsRef<Path>>(path: &P) -> io::Result<File> {
create_parent_directory(path)?;
File::create(ensure_extension(path, "json"))
}
fn open_cache<P: AsRef<Path>>(path: &P) -> io::Result<File> {
create_parent_directory(path)?;
File::open(ensure_extension(path, "json"))
}
fn create_parent_directory<P: AsRef<Path>>(path: &P) -> io::Result<()> {
if let Some(directories) = path.as_ref().parent() {
fs::create_dir_all(directories)?;
}
Ok(())
}
fn ensure_extension<P: AsRef<Path>>(path: &P, extension: &str) -> Box<Path> {
let mut buf = path.as_ref().to_path_buf();
buf.set_extension(extension);
buf.into_boxed_path()
}