Remove code
This commit is contained in:
parent
e2972a527e
commit
dd7b412976
10 changed files with 10 additions and 714 deletions
14
src/error.rs
14
src/error.rs
|
@ -1,14 +0,0 @@
|
||||||
use std::error::Error;
|
|
||||||
|
|
||||||
struct ApplicationError {
|
|
||||||
str: String
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Error for ApplicationError {
|
|
||||||
|
|
||||||
fn description(&self) -> &str {
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cause(&self) -> Option<&Error> {
|
|
||||||
}
|
|
||||||
}
|
|
35
src/main.rs
35
src/main.rs
|
@ -16,10 +16,6 @@ use configuration::Configuration;
|
||||||
use runtime::{ImagLogger, Runtime};
|
use runtime::{ImagLogger, Runtime};
|
||||||
use clap::App;
|
use clap::App;
|
||||||
use module::Module;
|
use module::Module;
|
||||||
use module::ModuleError;
|
|
||||||
use module::CommandEnv;
|
|
||||||
use module::bm::BMModule;
|
|
||||||
use storage::Storage;
|
|
||||||
|
|
||||||
mod cli;
|
mod cli;
|
||||||
mod configuration;
|
mod configuration;
|
||||||
|
@ -44,39 +40,12 @@ fn main() {
|
||||||
|
|
||||||
debug!("Runtime : {:?}", &rt);
|
debug!("Runtime : {:?}", &rt);
|
||||||
|
|
||||||
let backend = Storage::new(&rt).unwrap_or_else(|e| {
|
|
||||||
error!("Error: {}", e);
|
|
||||||
exit(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
if let Some(matches) = rt.config.cli_matches.subcommand_matches("bm") {
|
if let Some(matches) = rt.config.cli_matches.subcommand_matches("bm") {
|
||||||
let module = BMModule::new(&rt);
|
let res = BM::new(&rt).exec(matches.subcommand_matches("bm"));
|
||||||
let commands = module.get_commands(&rt);
|
info!("BM exited with {}", res);
|
||||||
if let Some(command) = matches.subcommand_name() {
|
|
||||||
debug!("Subcommand: {}", command);
|
|
||||||
|
|
||||||
let cmdenv = CommandEnv {
|
|
||||||
rt: &rt,
|
|
||||||
bk: &backend,
|
|
||||||
matches: matches.subcommand_matches(command).unwrap(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let result = match commands.get(command) {
|
|
||||||
Some(f) => f(&module, cmdenv),
|
|
||||||
None => Err(ModuleError::new("No subcommand found")),
|
|
||||||
};
|
|
||||||
|
|
||||||
debug!("Result of command: {:?}", result);
|
|
||||||
} else {
|
} else {
|
||||||
debug!("No subcommand");
|
|
||||||
}
|
|
||||||
|
|
||||||
module.shutdown(&rt);
|
|
||||||
} else {
|
|
||||||
// Err(ModuleError::mk("No commandline call"))
|
|
||||||
info!("No commandline call...")
|
info!("No commandline call...")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
info!("Hello, world!");
|
info!("Hello, world!");
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,189 +0,0 @@
|
||||||
use std::vec::IntoIter;
|
|
||||||
|
|
||||||
use clap::ArgMatches;
|
|
||||||
use regex::Regex;
|
|
||||||
|
|
||||||
use module::{CommandEnv, CommandResult, Module, ModuleError};
|
|
||||||
use module::bm::header::{build_header, get_tags_from_header};
|
|
||||||
use runtime::Runtime;
|
|
||||||
use storage::StorageError;
|
|
||||||
use storage::file::File;
|
|
||||||
use storage::json::parser::JsonHeaderParser;
|
|
||||||
use storage::parser::Parser;
|
|
||||||
use ui::file::{FilePrinter, TablePrinter};
|
|
||||||
|
|
||||||
pub fn add_command(module: &Module, env: CommandEnv) -> CommandResult {
|
|
||||||
use url::Url;
|
|
||||||
use module::helpers::utils::cli::get_tags;
|
|
||||||
|
|
||||||
let url = env.matches.value_of("url").unwrap();
|
|
||||||
|
|
||||||
if let Err(e) = Url::parse(url) {
|
|
||||||
info!("Not an URL: '{}'", url);
|
|
||||||
info!(" this will turn into an hard error before 0.1.0");
|
|
||||||
debug!("URL parsing error: {:?}", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
let tags = get_tags(env.rt, env.matches);
|
|
||||||
info!("Adding url '{}' with tags '{:?}'", url, tags);
|
|
||||||
|
|
||||||
let header = build_header(&String::from(url), &tags);
|
|
||||||
let file = File::new_with_header(module, header);
|
|
||||||
let parser = Parser::new(JsonHeaderParser::new(None));
|
|
||||||
let putres = env.bk.put_file(file, &parser);
|
|
||||||
|
|
||||||
putres.map_err(|sberr| {
|
|
||||||
let mut err = ModuleError::new("Storage Backend Error");
|
|
||||||
err.caused_by = Some(Box::new(sberr));
|
|
||||||
err
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn list_command(module: &Module, env: CommandEnv) -> CommandResult {
|
|
||||||
let printer = TablePrinter::new(env.rt.is_verbose(), env.rt.is_debugging());
|
|
||||||
let files = get_filtered_files_from_backend(module, &env);
|
|
||||||
|
|
||||||
debug!("Printing files now");
|
|
||||||
files.map(|f| printer.print_files(f));
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn remove_command(module: &Module, env: CommandEnv) -> CommandResult {
|
|
||||||
fn remove_by_id(module: &Module, env: CommandEnv, id: String, checked: bool) -> CommandResult {
|
|
||||||
let parser = Parser::new(JsonHeaderParser::new(None));
|
|
||||||
let file = env.bk
|
|
||||||
.get_file_by_id(module, &id.into(), &parser)
|
|
||||||
.unwrap_or({
|
|
||||||
info!("No files found");
|
|
||||||
return Ok(())
|
|
||||||
});
|
|
||||||
|
|
||||||
debug!("Remove file: {:?}", file);
|
|
||||||
|
|
||||||
if let Err(e) = env.bk.remove_file(module, file, checked) {
|
|
||||||
debug!("Remove failed");
|
|
||||||
let mut err = ModuleError::new("Removing file failed");
|
|
||||||
err.caused_by = Some(Box::new(e));
|
|
||||||
Err(err)
|
|
||||||
} else {
|
|
||||||
info!("Remove worked");
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn remove_by_filtering(module: &Module, env: CommandEnv, checked: bool) -> CommandResult {
|
|
||||||
get_filtered_files_from_backend(module, &env).and_then(|files| {
|
|
||||||
let nfiles = files.len();
|
|
||||||
info!("Removing {} Files", nfiles);
|
|
||||||
|
|
||||||
let errs = files.map(|file| {
|
|
||||||
debug!("Remove file: {:?}", file);
|
|
||||||
env.bk.remove_file(module, file, checked)
|
|
||||||
})
|
|
||||||
.filter(|e| e.is_err())
|
|
||||||
.map(|e| {
|
|
||||||
let err = e.err().unwrap();
|
|
||||||
warn!("Error occured in Filesystem operation: {}", err);
|
|
||||||
err
|
|
||||||
})
|
|
||||||
.collect::<Vec<StorageError>>();
|
|
||||||
|
|
||||||
if errs.len() != 0 {
|
|
||||||
warn!("{} Errors occured while removing {} files", errs.len(), nfiles);
|
|
||||||
let moderr = ModuleError::new("File removal failed");
|
|
||||||
|
|
||||||
// TODO : Collect StorageErrors
|
|
||||||
|
|
||||||
Err(moderr)
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
let checked : bool = run_removal_checking(&env);
|
|
||||||
debug!("Checked mode: {}", checked);
|
|
||||||
|
|
||||||
if let Some(id) = get_id(env.rt, env.matches) {
|
|
||||||
debug!("Remove by id: {}", id);
|
|
||||||
remove_by_id(module, env, id, checked)
|
|
||||||
} else {
|
|
||||||
debug!("Remove more than one file");
|
|
||||||
remove_by_filtering(module, env, checked)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
*
|
|
||||||
* Private helpers
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
fn get_filtered_files_from_backend<'a>(module: &'a Module,
|
|
||||||
env: &CommandEnv)
|
|
||||||
-> Result<IntoIter<File<'a>>, ModuleError>
|
|
||||||
{
|
|
||||||
use module::helpers::utils::cli::get_tags;
|
|
||||||
|
|
||||||
fn check_tags(tags: &Vec<String>, file: &File) -> bool {
|
|
||||||
if tags.len() != 0 {
|
|
||||||
debug!("Checking tags of: {:?}", file.id());
|
|
||||||
get_tags_from_header(&file.header())
|
|
||||||
.iter()
|
|
||||||
.any(|t| tags.contains(t))
|
|
||||||
} else {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let parser = Parser::new(JsonHeaderParser::new(None));
|
|
||||||
let tags = get_tags(env.rt, env.matches);
|
|
||||||
debug!("Tags: {:?}", tags);
|
|
||||||
env.bk
|
|
||||||
.iter_files(module, &parser)
|
|
||||||
.map(|files| {
|
|
||||||
files.filter(|file| {
|
|
||||||
debug!("Backend returns file: {:?}", file);
|
|
||||||
check_tags(&tags, file)
|
|
||||||
}).filter(|file| {
|
|
||||||
debug!("Checking matches of: {:?}", file.id());
|
|
||||||
get_matcher(env.rt, env.matches)
|
|
||||||
.map(|r| file.matches_with(&r))
|
|
||||||
.unwrap_or(true)
|
|
||||||
})
|
|
||||||
.collect::<Vec<File>>()
|
|
||||||
.into_iter()
|
|
||||||
}).map_err(|e| {
|
|
||||||
debug!("Error from Backend: {:?}", e);
|
|
||||||
let mut merr = ModuleError::new("Could not filter files");
|
|
||||||
merr.caused_by = Some(Box::new(e));
|
|
||||||
merr
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_matcher<'a>(rt: &Runtime, sub: &ArgMatches<'a, 'a>) -> Option<Regex> {
|
|
||||||
debug!("Fetching matcher from commandline");
|
|
||||||
if let Some(s) = sub.value_of("match") {
|
|
||||||
if let Ok(r) = Regex::new(s) {
|
|
||||||
return Some(r)
|
|
||||||
} else {
|
|
||||||
error!("Regex error, continuing without regex");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_id<'a>(rt: &Runtime, sub: &ArgMatches<'a, 'a>) -> Option<String> {
|
|
||||||
debug!("Fetching id from commandline");
|
|
||||||
sub.value_of("id").and_then(|s| Some(String::from(s)))
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Checks whether the commandline call was set to run the removal "checked",
|
|
||||||
* so if another entry from the store refers to this ID, do not remove the file.
|
|
||||||
*/
|
|
||||||
fn run_removal_checking(env: &CommandEnv) -> bool {
|
|
||||||
env.matches.is_present("check")
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
use module::helpers::header as headerhelpers;
|
|
||||||
use storage::file::header::data::FileHeaderData as FHD;
|
|
||||||
use storage::file::header::spec::FileHeaderSpec as FHS;
|
|
||||||
|
|
||||||
pub fn get_spec() -> FHS {
|
|
||||||
FHS::Map {
|
|
||||||
keys: vec![ headerhelpers::tags::spec::url_key(),
|
|
||||||
headerhelpers::tags::spec::tags_key() ]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn build_header(url: &String, tags: &Vec<String>) -> FHD {
|
|
||||||
FHD::Map {
|
|
||||||
keys: vec![
|
|
||||||
FHD::Key {
|
|
||||||
name: String::from("URL"),
|
|
||||||
value: Box::new(FHD::Text(url.clone()))
|
|
||||||
},
|
|
||||||
FHD::Key {
|
|
||||||
name: String::from("TAGS"),
|
|
||||||
value: Box::new(headerhelpers::tags::data::build_tag_array(tags))
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_tags_from_header(header: &FHD) -> Vec<String> {
|
|
||||||
headerhelpers::tags::data::get_tags_from_header(header)
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,57 +0,0 @@
|
||||||
mod header;
|
|
||||||
mod commands;
|
|
||||||
|
|
||||||
use std::fmt::{Debug, Formatter};
|
|
||||||
use std::fmt::Result as FMTResult;
|
|
||||||
|
|
||||||
use module::{CommandMap, Module, ModuleResult};
|
|
||||||
use runtime::Runtime;
|
|
||||||
use self::commands::*;
|
|
||||||
|
|
||||||
pub struct BMModule {
|
|
||||||
path: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
const CALLNAMES : &'static [&'static str] = &[ "bm", "bookmark" ];
|
|
||||||
|
|
||||||
impl BMModule {
|
|
||||||
|
|
||||||
pub fn new(rt : &Runtime) -> BMModule {
|
|
||||||
BMModule {
|
|
||||||
path: None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Module for BMModule {
|
|
||||||
|
|
||||||
fn callnames(&self) -> &'static [&'static str] {
|
|
||||||
CALLNAMES
|
|
||||||
}
|
|
||||||
|
|
||||||
fn name(&self) -> &'static str{
|
|
||||||
"bookmark"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn shutdown(&self, rt : &Runtime) -> ModuleResult {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_commands(&self, rt: &Runtime) -> CommandMap {
|
|
||||||
let mut hm = CommandMap::new();
|
|
||||||
hm.insert("add", add_command);
|
|
||||||
hm.insert("list", list_command);
|
|
||||||
hm.insert("remove", remove_command);
|
|
||||||
hm
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Debug for BMModule {
|
|
||||||
|
|
||||||
fn fmt(&self, fmt: &mut Formatter) -> FMTResult {
|
|
||||||
write!(fmt, "[Module][BM]");
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -7,61 +7,12 @@ use std::result::Result;
|
||||||
use clap::ArgMatches;
|
use clap::ArgMatches;
|
||||||
|
|
||||||
use runtime::Runtime;
|
use runtime::Runtime;
|
||||||
use storage::Storage;
|
|
||||||
|
|
||||||
pub mod bm;
|
pub mod bm;
|
||||||
pub mod helpers;
|
pub mod helpers;
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct ModuleError {
|
|
||||||
desc: String,
|
|
||||||
caused_by: Option<Box<Error>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ModuleError {
|
|
||||||
pub fn new(desc: &'static str) -> ModuleError {
|
|
||||||
ModuleError {
|
|
||||||
desc: desc.to_owned().to_string(),
|
|
||||||
caused_by: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Error for ModuleError {
|
|
||||||
|
|
||||||
fn description(&self) -> &str {
|
|
||||||
&self.desc[..]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cause(&self) -> Option<&Error> {
|
|
||||||
self.caused_by.as_ref().map(|e| &**e)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for ModuleError {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> FMTResult {
|
|
||||||
write!(f, "ModuleError: {}", self.description())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct CommandEnv<'a> {
|
|
||||||
pub rt: &'a Runtime<'a>,
|
|
||||||
pub bk: &'a Storage,
|
|
||||||
pub matches: &'a ArgMatches<'a, 'a>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type ModuleResult = Result<(), ModuleError>;
|
|
||||||
pub type CommandResult = ModuleResult;
|
|
||||||
pub type CommandMap<'a> = HashMap<&'a str, fn(&Module, CommandEnv) -> CommandResult>;
|
|
||||||
|
|
||||||
pub trait Module : Debug {
|
pub trait Module : Debug {
|
||||||
|
fn new(rt: &Runtime) -> Self;
|
||||||
fn callnames(&self) -> &'static [&'static str];
|
fn exec(&self, matches: &ArgMatches) -> bool;
|
||||||
fn name(&self) -> &'static str;
|
|
||||||
fn shutdown(&self, rt : &Runtime) -> ModuleResult;
|
|
||||||
|
|
||||||
fn get_commands(&self, rt: &Runtime) -> CommandMap;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -64,10 +64,6 @@ impl FileID {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_valid(&self) -> bool {
|
|
||||||
self.id.is_some()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_type(&self) -> FileIDType {
|
pub fn get_type(&self) -> FileIDType {
|
||||||
self.id_type.clone()
|
self.id_type.clone()
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ use regex::Regex;
|
||||||
|
|
||||||
pub mod id;
|
pub mod id;
|
||||||
pub mod header;
|
pub mod header;
|
||||||
|
pub mod hash;
|
||||||
|
|
||||||
use module::Module;
|
use module::Module;
|
||||||
use storage::file::id::*;
|
use storage::file::id::*;
|
||||||
|
|
|
@ -1,324 +0,0 @@
|
||||||
use std::error::Error;
|
|
||||||
use std::fmt::Result as FMTResult;
|
|
||||||
use std::fmt::{Display, Formatter};
|
|
||||||
use std::fs::File as FSFile;
|
|
||||||
use std::fs::{create_dir_all, remove_file};
|
|
||||||
use std::io::{Read, Write};
|
|
||||||
use std::path::Path;
|
|
||||||
use std::vec::{Vec, IntoIter};
|
|
||||||
|
|
||||||
pub mod file;
|
|
||||||
pub mod parser;
|
|
||||||
pub mod json;
|
|
||||||
|
|
||||||
use glob::glob;
|
|
||||||
use glob::Paths;
|
|
||||||
|
|
||||||
use module::Module;
|
|
||||||
use runtime::Runtime;
|
|
||||||
use storage::file::File;
|
|
||||||
use storage::file::id::*;
|
|
||||||
use storage::parser::{FileHeaderParser, Parser};
|
|
||||||
|
|
||||||
pub type BackendOperationResult<T = ()> = Result<T, StorageError>;
|
|
||||||
|
|
||||||
pub struct Storage {
|
|
||||||
basepath: String,
|
|
||||||
storepath: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Storage {
|
|
||||||
|
|
||||||
pub fn new(rt: &Runtime) -> BackendOperationResult<Storage> {
|
|
||||||
use self::StorageError as SBE;
|
|
||||||
|
|
||||||
let storepath = rt.get_rtp() + "/store/";
|
|
||||||
debug!("Trying to create {}", storepath);
|
|
||||||
create_dir_all(&storepath).and_then(|_| {
|
|
||||||
debug!("Creating succeeded, constructing backend instance");
|
|
||||||
Ok(Storage {
|
|
||||||
basepath: rt.get_rtp(),
|
|
||||||
storepath: storepath.clone(),
|
|
||||||
})
|
|
||||||
}).or_else(|e| {
|
|
||||||
debug!("Creating failed, constructing error instance");
|
|
||||||
Err(SBE::new("create_dir_all()", "Could not create store directories",
|
|
||||||
Some(storepath), Some(Box::new(e))))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn iter_ids(&self, m: &Module) -> Result<IntoIter<FileID>, StorageError>
|
|
||||||
{
|
|
||||||
use self::StorageError as SBE;
|
|
||||||
|
|
||||||
let globstr = self.prefix_of_files_for_module(m) + "*.imag";
|
|
||||||
debug!("Globstring = {}", globstr);
|
|
||||||
glob(&globstr[..])
|
|
||||||
.and_then(|globlist| {
|
|
||||||
debug!("Iterating over globlist");
|
|
||||||
Ok(globlist_to_file_id_vec(globlist).into_iter())
|
|
||||||
})
|
|
||||||
.map_err(|e| {
|
|
||||||
debug!("glob() returned error: {:?}", e);
|
|
||||||
SBE::new("iter_ids()", "Cannot iter on file ids", None, None)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn iter_files<'a, HP>(&self, m: &'a Module, p: &Parser<HP>)
|
|
||||||
-> Result<IntoIter<File<'a>>, StorageError>
|
|
||||||
where HP: FileHeaderParser
|
|
||||||
{
|
|
||||||
use self::StorageError as SBE;
|
|
||||||
|
|
||||||
self.iter_ids(m)
|
|
||||||
.and_then(|ids| {
|
|
||||||
debug!("Iterating ids and building files from them");
|
|
||||||
debug!(" number of ids = {}", ids.len());
|
|
||||||
Ok(self.filter_map_ids_to_files(m, p, ids).into_iter())
|
|
||||||
})
|
|
||||||
.map_err(|e| {
|
|
||||||
debug!("Storage::iter_ids() returned error = {:?}", e);
|
|
||||||
SBE::new("iter_files()", "Cannot iter on files", None,
|
|
||||||
Some(Box::new(e)))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Write a file to disk.
|
|
||||||
*
|
|
||||||
* The file is moved to this function as the file won't be edited afterwards
|
|
||||||
*/
|
|
||||||
pub fn put_file<HP>(&self, f: File, p: &Parser<HP>) -> BackendOperationResult
|
|
||||||
where HP: FileHeaderParser
|
|
||||||
{
|
|
||||||
use self::StorageError as SBE;
|
|
||||||
|
|
||||||
let written = write_with_parser(&f, p);
|
|
||||||
if written.is_err() { return Err(written.err().unwrap()); }
|
|
||||||
let string = written.unwrap();
|
|
||||||
|
|
||||||
let path = self.build_filepath(&f);
|
|
||||||
debug!("Writing file: {}", path);
|
|
||||||
debug!(" string: {}", string);
|
|
||||||
|
|
||||||
FSFile::create(&path).map(|mut file| {
|
|
||||||
debug!("Created file at '{}'", path);
|
|
||||||
file.write_all(&string.clone().into_bytes())
|
|
||||||
.map_err(|ioerr| {
|
|
||||||
debug!("Could not write file");
|
|
||||||
SBE::new("File::write_all()",
|
|
||||||
"Could not write out File contents",
|
|
||||||
None, Some(Box::new(ioerr)))
|
|
||||||
})
|
|
||||||
}).map_err(|writeerr| {
|
|
||||||
debug!("Could not create file at '{}'", path);
|
|
||||||
SBE::new("File::create()", "Creating file on disk failed", None,
|
|
||||||
Some(Box::new(writeerr)))
|
|
||||||
}).and(Ok(()))
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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
|
|
||||||
*/
|
|
||||||
pub fn update_file<HP>(&self, f: File, p: &Parser<HP>) -> BackendOperationResult
|
|
||||||
where HP: FileHeaderParser
|
|
||||||
{
|
|
||||||
use self::StorageError as SBE;
|
|
||||||
|
|
||||||
let contents = write_with_parser(&f, p);
|
|
||||||
if contents.is_err() { return Err(contents.err().unwrap()); }
|
|
||||||
let string = contents.unwrap();
|
|
||||||
|
|
||||||
let path = self.build_filepath(&f);
|
|
||||||
debug!("Writing file: {}", path);
|
|
||||||
debug!(" string: {}", string);
|
|
||||||
|
|
||||||
FSFile::open(&path).map(|mut file| {
|
|
||||||
debug!("Open file at '{}'", path);
|
|
||||||
file.write_all(&string.clone().into_bytes())
|
|
||||||
.map_err(|ioerr| {
|
|
||||||
debug!("Could not write file");
|
|
||||||
SBE::new("File::write()",
|
|
||||||
"Tried to write contents of this file, though operation did not succeed",
|
|
||||||
Some(string), Some(Box::new(ioerr)))
|
|
||||||
})
|
|
||||||
}).map_err(|writeerr| {
|
|
||||||
debug!("Could not write file at '{}'", path);
|
|
||||||
SBE::new("File::open()",
|
|
||||||
"Tried to update contents of this file, though file doesn't exist",
|
|
||||||
None, Some(Box::new(writeerr)))
|
|
||||||
}).and(Ok(()))
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
pub fn get_file_by_id<'a, HP>(&self, m: &'a Module, id: &FileID, p: &Parser<HP>) -> Option<File<'a>>
|
|
||||||
where HP: FileHeaderParser
|
|
||||||
{
|
|
||||||
debug!("Searching for file with id '{}'", id);
|
|
||||||
|
|
||||||
if id.get_type() == FileIDType::NONE {
|
|
||||||
// We don't know the hash type, so we glob() around a bit.
|
|
||||||
debug!("Having FileIDType::NONE, so we glob() for the raw ID");
|
|
||||||
|
|
||||||
let id_str = id.get_id().unwrap_or(String::from("INVALID"));
|
|
||||||
let globstr = self.prefix_of_files_for_module(m) + "*" + &id_str[..] + ".imag";
|
|
||||||
debug!("Globbing with globstr = '{}'", globstr);
|
|
||||||
glob(&globstr[..]).map(|globlist| {
|
|
||||||
let idvec = globlist_to_file_id_vec(globlist).into_iter();
|
|
||||||
let mut vec = self.filter_map_ids_to_files(m, p, idvec);
|
|
||||||
vec.reverse();
|
|
||||||
vec.pop()
|
|
||||||
}).unwrap_or({
|
|
||||||
debug!("No glob matches, actually. We can't do anything at this point");
|
|
||||||
None
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
// The (hash)type is already in the FileID object, so we can just
|
|
||||||
// build a path from the information we already have
|
|
||||||
debug!("We know FileIDType, so we build the path directly now");
|
|
||||||
let filepath = self.build_filepath_with_id(m, id.clone());
|
|
||||||
if let Ok(mut fs) = FSFile::open(filepath) {
|
|
||||||
let mut s = String::new();
|
|
||||||
fs.read_to_string(&mut s);
|
|
||||||
|
|
||||||
debug!("Success opening file with id '{}'", id);
|
|
||||||
debug!("Parsing to internal structure now");
|
|
||||||
p.read(s).and_then(|(h, d)| {
|
|
||||||
Ok(File::from_parser_result(m, id.clone(), h, d))
|
|
||||||
}).ok()
|
|
||||||
} else {
|
|
||||||
debug!("No file with id '{}'", id);
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn remove_file(&self, m: &Module, file: File, checked: bool) -> BackendOperationResult {
|
|
||||||
use self::StorageError as SBE;
|
|
||||||
|
|
||||||
if checked {
|
|
||||||
error!("Checked remove not implemented yet. I will crash now");
|
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
debug!("Doing unchecked remove");
|
|
||||||
info!("Going to remove file: {}", file);
|
|
||||||
|
|
||||||
let fp = self.build_filepath(&file);
|
|
||||||
remove_file(fp).map_err(|e| {
|
|
||||||
SBE::new("remove_file()", "File removal failed",
|
|
||||||
Some(format!("{}", file)), Some(Box::new(e)))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn build_filepath(&self, f: &File) -> String {
|
|
||||||
self.build_filepath_with_id(f.owner(), f.id())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn build_filepath_with_id(&self, owner: &Module, id: FileID) -> String {
|
|
||||||
let idstr : String = id.clone().into();
|
|
||||||
let idtype : FileIDType = id.into();
|
|
||||||
let typestr : String = idtype.into();
|
|
||||||
|
|
||||||
debug!("Building filepath with id");
|
|
||||||
debug!(" basepath: '{}'", self.basepath);
|
|
||||||
debug!(" storepath: '{}'", self.storepath);
|
|
||||||
debug!(" id: '{}'", idstr);
|
|
||||||
debug!(" type: '{}'", typestr);
|
|
||||||
|
|
||||||
self.prefix_of_files_for_module(owner) +
|
|
||||||
"-" + &typestr[..] +
|
|
||||||
"-" + &idstr[..] +
|
|
||||||
".imag"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn prefix_of_files_for_module(&self, m: &Module) -> String {
|
|
||||||
self.storepath.clone() + m.name()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn filter_map_ids_to_files<'a, HP>(&self,
|
|
||||||
m: &'a Module,
|
|
||||||
p: &Parser<HP>,
|
|
||||||
ids: IntoIter<FileID>)
|
|
||||||
-> Vec<File<'a>>
|
|
||||||
where HP: FileHeaderParser
|
|
||||||
{
|
|
||||||
ids.filter_map(|id| self.get_file_by_id(m, &id, p))
|
|
||||||
.collect::<Vec<File>>()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct StorageError {
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
impl StorageError {
|
|
||||||
|
|
||||||
fn new<S>(action: S,
|
|
||||||
desc: S,
|
|
||||||
data: Option<String>,
|
|
||||||
cause: Option<Box<Error>>)
|
|
||||||
-> StorageError
|
|
||||||
where S: Into<String>
|
|
||||||
{
|
|
||||||
StorageError {
|
|
||||||
action: action.into(),
|
|
||||||
desc: desc.into(),
|
|
||||||
data_dump: data,
|
|
||||||
caused_by: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Error for StorageError {
|
|
||||||
|
|
||||||
fn description(&self) -> &str {
|
|
||||||
&self.desc[..]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cause(&self) -> Option<&Error> {
|
|
||||||
self.caused_by.as_ref().map(|e| &**e)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Display for StorageError {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> FMTResult {
|
|
||||||
write!(f, "StorageError[{}]: {}",
|
|
||||||
self.action, self.desc)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fn write_with_parser<'a, HP>(f: &File, p: &Parser<HP>) -> Result<String, StorageError>
|
|
||||||
where HP: FileHeaderParser
|
|
||||||
{
|
|
||||||
use self::StorageError as SBE;
|
|
||||||
|
|
||||||
p.write(f.contents())
|
|
||||||
.or_else(|err| {
|
|
||||||
Err(SBE::new("Parser::write()",
|
|
||||||
"Cannot translate internal representation of file contents into on-disk representation",
|
|
||||||
None, Some(Box::new(err))))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn globlist_to_file_id_vec(globlist: Paths) -> Vec<FileID> {
|
|
||||||
globlist.filter_map(Result::ok)
|
|
||||||
.map(|pbuf| FileID::from(&pbuf))
|
|
||||||
.collect::<Vec<FileID>>()
|
|
||||||
}
|
|
||||||
|
|
|
@ -88,16 +88,11 @@ pub trait FileHeaderParser : Sized {
|
||||||
fn write(&self, data: &FileHeaderData) -> Result<String, ParserError>;
|
fn write(&self, data: &FileHeaderData) -> Result<String, ParserError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
type TextTpl = (Option<String>, Option<String>);
|
pub struct Parser<HP> {
|
||||||
|
|
||||||
pub struct Parser<HP>
|
|
||||||
{
|
|
||||||
headerp : HP,
|
headerp : HP,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<HP> Parser<HP> where
|
impl<HP: FileHeaderParser> Parser<HP> {
|
||||||
HP: FileHeaderParser,
|
|
||||||
{
|
|
||||||
|
|
||||||
pub fn new(headerp: HP) -> Parser<HP> {
|
pub fn new(headerp: HP) -> Parser<HP> {
|
||||||
Parser {
|
Parser {
|
||||||
|
@ -105,8 +100,7 @@ impl<HP> Parser<HP> where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read(&self, s: String) -> Result<(FileHeaderData, String), ParserError>
|
pub fn read(&self, s: String) -> Result<(FileHeaderData, String), ParserError> {
|
||||||
{
|
|
||||||
debug!("Reading into internal datastructure: '{}'", s);
|
debug!("Reading into internal datastructure: '{}'", s);
|
||||||
let divided = self.divide_text(&s);
|
let divided = self.divide_text(&s);
|
||||||
|
|
||||||
|
@ -128,8 +122,7 @@ impl<HP> Parser<HP> where
|
||||||
Ok((h_parseres, data.unwrap_or(String::new())))
|
Ok((h_parseres, data.unwrap_or(String::new())))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write(&self, tpl : (FileHeaderData, String)) -> Result<String, ParserError>
|
pub fn write(&self, tpl : (FileHeaderData, String)) -> Result<String, ParserError> {
|
||||||
{
|
|
||||||
debug!("Parsing internal datastructure to String");
|
debug!("Parsing internal datastructure to String");
|
||||||
let (header, data) = tpl;
|
let (header, data) = tpl;
|
||||||
let h_text = try!(self.headerp.write(&header));
|
let h_text = try!(self.headerp.write(&header));
|
||||||
|
@ -139,7 +132,7 @@ impl<HP> Parser<HP> where
|
||||||
Ok(text)
|
Ok(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn divide_text(&self, text: &String) -> Result<TextTpl, ParserError> {
|
fn divide_text(&self, text: &String) -> Result<(Option<String>, Option<String>), ParserError> {
|
||||||
let re = Regex::new(r"(?sm)^---$(.*)^---$(.*)").unwrap();
|
let re = Regex::new(r"(?sm)^---$(.*)^---$(.*)").unwrap();
|
||||||
|
|
||||||
debug!("Splitting: '{}'", text);
|
debug!("Splitting: '{}'", text);
|
||||||
|
|
Loading…
Reference in a new issue