imag/src/storage/backend.rs

240 lines
7.1 KiB
Rust
Raw Normal View History

2015-11-10 18:27:54 +00:00
use std::error::Error;
use std::fmt::Display;
2015-11-10 18:51:43 +00:00
use std::fmt::Formatter;
use std::fmt::Result as FMTResult;
use std::path::Path;
2015-11-23 17:22:02 +00:00
use std::path::PathBuf;
use std::vec::Vec;
use std::fs::File as FSFile;
use std::io::Read;
2015-11-24 09:48:30 +00:00
use std::io::Write;
2015-12-02 10:41:49 +00:00
use std::vec::IntoIter;
use glob::glob;
2015-11-23 17:22:02 +00:00
use glob::Paths;
2015-11-10 18:27:54 +00:00
2015-11-23 17:42:55 +00:00
use storage::file::File;
use storage::file_id::*;
2015-11-24 09:27:42 +00:00
use storage::parser::{FileHeaderParser, Parser, ParserError};
2015-11-10 18:27:54 +00:00
2015-11-23 18:54:08 +00:00
use module::Module;
use runtime::Runtime;
2015-11-23 18:25:27 +00:00
pub type BackendOperationResult = Result<(), StorageBackendError>;
2015-11-10 18:27:54 +00:00
2015-11-23 17:45:31 +00:00
pub struct StorageBackend {
2015-11-10 18:27:54 +00:00
basepath: String,
}
2015-11-23 17:45:31 +00:00
impl StorageBackend {
2015-11-10 18:27:54 +00:00
2015-11-23 18:54:08 +00:00
pub fn new(basepath: String) -> StorageBackend {
2015-11-10 19:53:35 +00:00
StorageBackend {
basepath: basepath,
}
2015-11-10 18:27:54 +00:00
}
2015-11-23 18:54:08 +00:00
fn build<M: Module>(rt: &Runtime, m: &M) -> StorageBackend {
let path = rt.get_rtp() + m.name() + "/store";
// TODO: Don't use "/store" but value from configuration
debug!("Building StorageBackend for {}", path);
2015-11-23 18:54:08 +00:00
StorageBackend::new(path)
}
2015-11-23 18:26:02 +00:00
fn get_file_ids(&self) -> Option<Vec<FileID>> {
debug!("Getting files from {}", self.basepath);
2015-11-23 17:22:02 +00:00
let list = glob(&self.basepath[..]);
if let Ok(globlist) = list {
let mut v = vec![];
for entry in globlist {
if let Ok(path) = entry {
debug!(" - File: {:?}", path);
2015-11-23 17:42:55 +00:00
v.push(from_pathbuf(&path));
2015-11-23 17:22:02 +00:00
} else {
// Entry is not a path
}
}
Some(v)
} else {
None
}
2015-11-10 18:27:54 +00:00
}
2015-12-02 10:41:49 +00:00
pub fn iter_ids(&self, m: &Module) -> Option<IntoIter<FileID>>
{
glob(&self.prefix_of_files_for_module(m)[..]).and_then(|globlist| {
let v = globlist.filter_map(Result::ok)
.map(|pbuf| from_pathbuf(&pbuf))
.collect::<Vec<FileID>>()
.into_iter();
Ok(v)
}).ok()
}
2015-12-02 10:42:06 +00:00
pub fn iter_files<'a, HP>(&self, m: &'a Module, p: &Parser<HP>)
-> Option<IntoIter<File<'a>>>
where HP: FileHeaderParser
{
self.iter_ids(m).and_then(|ids| {
Some(ids.filter_map(|id| self.get_file_by_id(m, &id, p))
.collect::<Vec<File>>()
.into_iter())
})
}
2015-11-23 18:25:27 +00:00
/*
* Write a file to disk.
*
* The file is moved to this function as the file won't be edited afterwards
*/
2015-11-24 09:27:42 +00:00
pub fn put_file<'a, HP>(&self, f: File, p: &Parser<HP>) ->
Result<BackendOperationResult, ParserError>
where HP: FileHeaderParser<'a>
{
let written = p.write(f.contents());
if let Ok(string) = written {
2015-11-24 09:30:52 +00:00
let path = self.build_filepath(&f);
2015-11-24 09:27:42 +00:00
debug!("Writing file: {}", path);
debug!(" contents: {}", string);
2015-11-24 09:27:42 +00:00
Ok(Ok(()))
} else {
debug!("Error parsing : {:?}", f.contents());
2015-11-24 09:27:42 +00:00
Err(written.err().unwrap())
}
2015-11-10 18:27:54 +00:00
}
2015-11-23 18:25:27 +00:00
/*
* Update a file. We have the UUID and can find the file on FS with it and
* then replace its contents with the contents of the passed file object
*/
2015-11-24 09:48:30 +00:00
pub fn update_file<'a, HP>(&self, f: File, p: &Parser<HP>)
-> Result<BackendOperationResult, ParserError>
where HP: FileHeaderParser<'a>
{
let contents = p.write(f.contents());
if contents.is_err() {
debug!("Error parsing contents: {:?}", f.contents());
2015-11-24 09:48:30 +00:00
return Err(contents.err().unwrap());
}
let content = contents.unwrap();
debug!("Success parsing content : {}", content);
2015-11-24 09:48:30 +00:00
let path = self.build_filepath(&f);
debug!("Trying to write to file at {}", path);
2015-11-24 18:37:22 +00:00
if let Err(_) = FSFile::open(&path) {
debug!("Error opening {}", path);
2015-11-24 09:48:30 +00:00
return Ok(Err(StorageBackendError::new(
String::from("File::open()"),
format!("Tried to open '{}'", path),
String::from("Tried to update contents of this file, though file doesn't exist"),
2015-11-24 09:48:30 +00:00
None)))
}
2015-11-24 18:37:22 +00:00
if let Ok(mut file) = FSFile::create(&path) {
if let Err(writeerr) = file.write_all(&content.clone().into_bytes()) {
debug!("Error writing to {}", path);
2015-11-24 09:48:30 +00:00
return Ok(Err(StorageBackendError::new(
String::from("File::write()"),
format!("Tried to write '{}'", path),
String::from("Tried to write contents of this file, though operation did not succeed"),
2015-11-24 09:48:30 +00:00
Some(content))))
}
}
debug!("Successfully written to file.");
2015-11-24 09:48:30 +00:00
Ok(Ok(()))
2015-11-10 18:27:54 +00:00
}
2015-11-23 18:25:27 +00:00
/*
* Find a file by its ID and return it if found. Return nothing if not
* found, of course.
*
* TODO: Needs refactoring, as there might be an error when reading from
* disk OR the id just does not exist.
*/
2015-11-24 09:59:30 +00:00
pub fn get_file_by_id<'a, HP>(&self, id: FileID, p: &Parser<HP>) -> Option<File>
where HP: FileHeaderParser<'a>
{
debug!("Searching for file with id '{}'", id);
2015-11-24 18:37:22 +00:00
if let Ok(mut fs) = FSFile::open(self.build_filepath_with_id(id.clone())) {
2015-11-24 09:59:30 +00:00
let mut s = String::new();
fs.read_to_string(&mut s);
debug!("Success reading file with id '{}'", id);
debug!("Parsing to internal structure now");
p.read(s).and_then(|(h, d)| Ok(File::from_parser_result(id, h, d))).ok()
2015-11-24 09:59:30 +00:00
} else {
debug!("No file with id '{}'", id);
2015-11-24 09:59:30 +00:00
None
}
}
2015-11-24 09:30:52 +00:00
fn build_filepath(&self, f: &File) -> String {
self.build_filepath_with_id(f.id())
}
fn build_filepath_with_id(&self, id: FileID) -> String {
debug!("Building filepath for id '{}'", id);
2015-11-24 18:37:22 +00:00
self.basepath.clone() + &id[..]
2015-11-24 09:30:52 +00:00
}
2015-11-10 18:27:54 +00:00
}
#[derive(Debug)]
2015-11-10 18:51:43 +00:00
pub struct StorageBackendError {
pub action: String, // The file system action in words
pub desc: String, // A short description
pub data_dump: Option<String>, // Data dump, if any
pub caused_by: Option<Box<Error>>, // caused from this error
2015-11-10 18:51:43 +00:00
}
2015-11-10 18:27:54 +00:00
impl StorageBackendError {
fn new(action: String,
desc : String,
2015-11-10 18:51:43 +00:00
data : Option<String>) -> StorageBackendError
{
StorageBackendError {
action: action,
desc: desc,
data_dump: data,
caused_by: None,
2015-11-10 18:51:43 +00:00
}
}
fn build(action: &'static str,
desc: &'static str,
data : Option<String>) -> StorageBackendError
{
StorageBackendError {
action: String::from(action),
desc: String::from(desc),
dataDump: data,
caused_by: None,
}
}
2015-11-10 18:27:54 +00:00
}
impl Error for StorageBackendError {
2015-11-10 18:51:43 +00:00
fn description(&self) -> &str {
&self.desc[..]
}
fn cause(&self) -> Option<&Error> {
self.caused_by.as_ref().map(|e| &**e)
2015-11-10 18:51:43 +00:00
}
2015-11-10 18:27:54 +00:00
}
impl Display for StorageBackendError {
2015-11-10 18:51:43 +00:00
fn fmt(&self, f: &mut Formatter) -> FMTResult {
write!(f, "StorageBackendError[{}]: {}",
self.action, self.desc)
2015-11-10 18:51:43 +00:00
}
2015-11-10 18:27:54 +00:00
}