Merge branch 'bootstrap-storage-backends'
I guess this merge only happens because the branch gets way too messy. Further work on the storage backend will happen.
This commit is contained in:
commit
fe0ef09417
14 changed files with 305 additions and 62 deletions
6
Cargo.lock
generated
6
Cargo.lock
generated
|
@ -5,6 +5,7 @@ dependencies = [
|
||||||
"chrono 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
"chrono 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"clap 1.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"clap 1.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"config 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"config 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"glob 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"log 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"regex 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
|
"regex 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rustty 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rustty 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -71,6 +72,11 @@ dependencies = [
|
||||||
"nom 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
"nom 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "glob"
|
||||||
|
version = "0.2.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kernel32-sys"
|
name = "kernel32-sys"
|
||||||
version = "0.1.4"
|
version = "0.1.4"
|
||||||
|
|
|
@ -12,6 +12,7 @@ log = "0.3.2"
|
||||||
regex = "0.1.41"
|
regex = "0.1.41"
|
||||||
url = "0.2.37"
|
url = "0.2.37"
|
||||||
uuid = "0.1.18"
|
uuid = "0.1.18"
|
||||||
|
glob = "0.2.10"
|
||||||
|
|
||||||
config = "0.1.2"
|
config = "0.1.2"
|
||||||
|
|
||||||
|
|
|
@ -33,5 +33,9 @@ impl<'a> CliConfig<'a> {
|
||||||
pub fn is_debugging(&self) -> bool {
|
pub fn is_debugging(&self) -> bool {
|
||||||
self.cli_matches.is_present("debug")
|
self.cli_matches.is_present("debug")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_rtp(&self) -> Option<String> {
|
||||||
|
self.cli_matches.value_of("rtp").and_then(|s| Some(String::from(s)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -57,6 +57,10 @@ impl Configuration {
|
||||||
format!("{}{}", self.rtp, self.store_sub)
|
format!("{}{}", self.rtp, self.store_sub)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_rtp(&self) -> String {
|
||||||
|
self.rtp.clone()
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rtp_path(config: &CliConfig) -> String {
|
fn rtp_path(config: &CliConfig) -> String {
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
#[macro_use] extern crate log;
|
#[macro_use] extern crate log;
|
||||||
#[macro_use] extern crate serde;
|
#[macro_use] extern crate serde;
|
||||||
#[macro_use] extern crate serde_json;
|
#[macro_use] extern crate serde_json;
|
||||||
|
#[macro_use] extern crate glob;
|
||||||
|
#[macro_use] extern crate uuid;
|
||||||
extern crate config;
|
extern crate config;
|
||||||
extern crate regex;
|
extern crate regex;
|
||||||
|
|
||||||
|
|
11
src/module/command.rs
Normal file
11
src/module/command.rs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
use std::result::Result;
|
||||||
|
|
||||||
|
use super::ModuleError;
|
||||||
|
use storage::backend::{StorageBackend, StorageBackendError};
|
||||||
|
|
||||||
|
type CommandError = Result<ModuleError, StorageBackendError>;
|
||||||
|
type CommandResult = Result<(), Result<ModuleError, CommandError>>;
|
||||||
|
|
||||||
|
pub trait ExecutableCommand {
|
||||||
|
fn exec(StorageBackend) -> CommandResult;
|
||||||
|
}
|
|
@ -6,9 +6,9 @@ use std::fmt::Display;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::result::Result;
|
use std::result::Result;
|
||||||
|
|
||||||
use module::todo::TodoModule;
|
use storage::backend::{StorageBackend, StorageBackendError};
|
||||||
|
use self::command::ExecutableCommand;
|
||||||
mod todo;
|
mod command;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ModuleError {
|
pub struct ModuleError {
|
||||||
|
@ -52,5 +52,9 @@ pub trait Module {
|
||||||
fn execute(&self, rt : &Runtime) -> ModuleResult;
|
fn execute(&self, rt : &Runtime) -> ModuleResult;
|
||||||
fn shutdown(&self, rt : &Runtime) -> ModuleResult;
|
fn shutdown(&self, rt : &Runtime) -> ModuleResult;
|
||||||
|
|
||||||
|
fn getCommandBuilder<T, F>() -> F
|
||||||
|
where F: FnOnce(StorageBackend) -> T,
|
||||||
|
T: ExecutableCommand;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,36 +0,0 @@
|
||||||
use runtime::Runtime;
|
|
||||||
use module::Module;
|
|
||||||
use module::ModuleResult;
|
|
||||||
use std::path::Path;
|
|
||||||
use std::result::Result;
|
|
||||||
|
|
||||||
pub struct TodoModule {
|
|
||||||
path: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
const CALLNAMES : &'static [&'static str] = &[ "todo" ];
|
|
||||||
|
|
||||||
impl Module for TodoModule {
|
|
||||||
|
|
||||||
fn new(rt : &Runtime) -> TodoModule {
|
|
||||||
TodoModule {
|
|
||||||
path: None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn callnames() -> &'static [&'static str] {
|
|
||||||
CALLNAMES
|
|
||||||
}
|
|
||||||
|
|
||||||
fn name(&self) -> &'static str{
|
|
||||||
"Todo"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn execute(&self, rt : &Runtime) -> ModuleResult {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn shutdown(&self, rt : &Runtime) -> ModuleResult {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -72,4 +72,12 @@ impl<'a> Runtime<'a> {
|
||||||
self.config.is_debugging() || self.configuration.is_verbose()
|
self.config.is_debugging() || self.configuration.is_verbose()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_rtp(&self) -> String {
|
||||||
|
if let Some(rtp) = self.config.get_rtp() {
|
||||||
|
rtp
|
||||||
|
} else {
|
||||||
|
self.configuration.get_rtp()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
187
src/storage/backend.rs
Normal file
187
src/storage/backend.rs
Normal file
|
@ -0,0 +1,187 @@
|
||||||
|
use std::error::Error;
|
||||||
|
use std::fmt::Display;
|
||||||
|
use std::fmt::Formatter;
|
||||||
|
use std::fmt::Result as FMTResult;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::vec::Vec;
|
||||||
|
use std::fs::File as FSFile;
|
||||||
|
use std::io::Read;
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
use glob::glob;
|
||||||
|
use glob::Paths;
|
||||||
|
|
||||||
|
use storage::file::File;
|
||||||
|
use storage::file_id::*;
|
||||||
|
use storage::parser::{FileHeaderParser, Parser, ParserError};
|
||||||
|
|
||||||
|
use module::Module;
|
||||||
|
use runtime::Runtime;
|
||||||
|
|
||||||
|
pub type BackendOperationResult = Result<(), StorageBackendError>;
|
||||||
|
|
||||||
|
pub struct StorageBackend {
|
||||||
|
basepath: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StorageBackend {
|
||||||
|
|
||||||
|
pub fn new(basepath: String) -> StorageBackend {
|
||||||
|
StorageBackend {
|
||||||
|
basepath: basepath,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build<M: Module>(rt: &Runtime, m: &M) -> StorageBackend {
|
||||||
|
let path = rt.get_rtp() + m.name() + "/store";
|
||||||
|
StorageBackend::new(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_file_ids(&self) -> Option<Vec<FileID>> {
|
||||||
|
let list = glob(&self.basepath[..]);
|
||||||
|
|
||||||
|
if let Ok(globlist) = list {
|
||||||
|
let mut v = vec![];
|
||||||
|
for entry in globlist {
|
||||||
|
if let Ok(path) = entry {
|
||||||
|
v.push(from_pathbuf(&path));
|
||||||
|
} else {
|
||||||
|
// Entry is not a path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(v)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write a file to disk.
|
||||||
|
*
|
||||||
|
* The file is moved to this function as the file won't be edited afterwards
|
||||||
|
*/
|
||||||
|
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 {
|
||||||
|
let path = self.build_filepath(&f);
|
||||||
|
debug!("Writing file: {}", path);
|
||||||
|
Ok(Ok(()))
|
||||||
|
} else {
|
||||||
|
Err(written.err().unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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<'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() {
|
||||||
|
return Err(contents.err().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
let content = contents.unwrap();
|
||||||
|
|
||||||
|
let path = self.build_filepath(&f);
|
||||||
|
if let Err(_) = FSFile::open(&path) {
|
||||||
|
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"),
|
||||||
|
None)))
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(mut file) = FSFile::create(&path) {
|
||||||
|
if let Err(writeerr) = file.write_all(&content.clone().into_bytes()) {
|
||||||
|
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"),
|
||||||
|
Some(content))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(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, id: FileID, p: &Parser<HP>) -> Option<File>
|
||||||
|
where HP: FileHeaderParser<'a>
|
||||||
|
{
|
||||||
|
if let Ok(mut fs) = FSFile::open(self.build_filepath_with_id(id.clone())) {
|
||||||
|
let mut s = String::new();
|
||||||
|
fs.read_to_string(&mut s);
|
||||||
|
p.read(s).and_then(|(h, d)| Ok(File::from_parser_result(id, h, d))).ok()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_filepath(&self, f: &File) -> String {
|
||||||
|
self.build_filepath_with_id(f.id())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_filepath_with_id(&self, id: FileID) -> String {
|
||||||
|
self.basepath.clone() + &id[..]
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct StorageBackendError {
|
||||||
|
pub action: String, // The file system action in words
|
||||||
|
pub desc: String, // A short description
|
||||||
|
pub explanation: String, // A long, user friendly description
|
||||||
|
pub dataDump: Option<String> // Data dump, if any
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StorageBackendError {
|
||||||
|
fn new(action: String,
|
||||||
|
desc : String,
|
||||||
|
explan: String,
|
||||||
|
data : Option<String>) -> StorageBackendError
|
||||||
|
{
|
||||||
|
StorageBackendError {
|
||||||
|
action: action,
|
||||||
|
desc: desc,
|
||||||
|
explanation: explan,
|
||||||
|
dataDump: data,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for StorageBackendError {
|
||||||
|
|
||||||
|
fn description(&self) -> &str {
|
||||||
|
&self.desc[..]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cause(&self) -> Option<&Error> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for StorageBackendError {
|
||||||
|
fn fmt(&self, f: &mut Formatter) -> FMTResult {
|
||||||
|
write!(f, "StorageBackendError[{}]: {}\n\n{}",
|
||||||
|
self.action, self.desc, self.explanation)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -3,6 +3,9 @@ use std::fmt::{Debug, Display, Formatter};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use super::parser::{FileHeaderParser, Parser, ParserError};
|
use super::parser::{FileHeaderParser, Parser, ParserError};
|
||||||
|
use storage::file_id::*;
|
||||||
|
|
||||||
|
use std::fs::File as FSFile;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum FileHeaderSpec {
|
pub enum FileHeaderSpec {
|
||||||
|
@ -18,6 +21,7 @@ pub enum FileHeaderSpec {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
#[derive(Clone)]
|
||||||
pub enum FileHeaderData {
|
pub enum FileHeaderData {
|
||||||
Null,
|
Null,
|
||||||
Bool(bool),
|
Bool(bool),
|
||||||
|
@ -162,39 +166,69 @@ pub fn match_header_spec<'a>(spec: &'a FileHeaderSpec, data: &'a FileHeaderData)
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type FileID = String;
|
/*
|
||||||
|
* Internal abstract view on a file. Does not exist on the FS and is just kept
|
||||||
|
* internally until it is written to disk.
|
||||||
|
*/
|
||||||
pub struct File {
|
pub struct File {
|
||||||
header : FileHeaderData,
|
header : FileHeaderData,
|
||||||
data : String,
|
data : String,
|
||||||
id : String
|
id : FileID,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> File {
|
impl File {
|
||||||
|
|
||||||
fn new<HP>(prs: &Parser<HP>, path: &String) -> Result<File, ParserError>
|
pub fn new() -> File {
|
||||||
where HP: FileHeaderParser<'a>
|
File {
|
||||||
{
|
header: FileHeaderData::Null,
|
||||||
File::read_file(path).and_then(|p| prs.read(p))
|
data: String::from(""),
|
||||||
.and_then(|(h, d)|
|
id: File::get_new_file_id(),
|
||||||
Ok(File {
|
}
|
||||||
header: h,
|
|
||||||
data: d,
|
|
||||||
id: File::get_id_from_path(path),
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn getID(&self) -> FileID {
|
pub fn from_parser_result(id: FileID, header: FileHeaderData, data: String) -> File {
|
||||||
|
File {
|
||||||
|
header: header,
|
||||||
|
data: data,
|
||||||
|
id: id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_with_header(h: FileHeaderData) -> File {
|
||||||
|
File {
|
||||||
|
header: h,
|
||||||
|
data: String::from(""),
|
||||||
|
id: File::get_new_file_id(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_with_data(d: String) -> File {
|
||||||
|
File {
|
||||||
|
header: FileHeaderData::Null,
|
||||||
|
data: d,
|
||||||
|
id: File::get_new_file_id(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_with_content(h: FileHeaderData, d: String) -> File {
|
||||||
|
File {
|
||||||
|
header: h,
|
||||||
|
data: d,
|
||||||
|
id: File::get_new_file_id(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn contents(&self) -> (FileHeaderData, String) {
|
||||||
|
(self.header.clone(), self.data.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn id(&self) -> FileID {
|
||||||
self.id.clone()
|
self.id.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_id_from_path(p: &String) -> FileID {
|
fn get_new_file_id() -> FileID {
|
||||||
String::from("")
|
use uuid::Uuid;
|
||||||
|
Uuid::new_v4().to_hyphenated_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_file(p: &String) -> Result<String, ParserError> {
|
|
||||||
Ok(String::from(""))
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
16
src/storage/file_id.rs
Normal file
16
src/storage/file_id.rs
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
pub type FileID = String;
|
||||||
|
|
||||||
|
pub fn from_path_string(s: &String) -> FileID {
|
||||||
|
String::from("")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_path(p: &Path) -> FileID {
|
||||||
|
String::from("")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_pathbuf(p: &PathBuf) -> FileID {
|
||||||
|
from_path(p.as_path())
|
||||||
|
}
|
||||||
|
|
|
@ -5,7 +5,9 @@ pub use std::error::Error;
|
||||||
pub use runtime::Runtime;
|
pub use runtime::Runtime;
|
||||||
|
|
||||||
pub mod file;
|
pub mod file;
|
||||||
|
pub mod file_id;
|
||||||
pub mod parser;
|
pub mod parser;
|
||||||
|
pub mod backend;
|
||||||
|
|
||||||
pub mod json;
|
pub mod json;
|
||||||
|
|
||||||
|
|
|
@ -112,7 +112,7 @@ impl<'a, HP> Parser<HP> where
|
||||||
Ok((h_parseres, data.unwrap_or(String::new())))
|
Ok((h_parseres, data.unwrap_or(String::new())))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write(&self, tpl : (FileHeaderData, String)) -> Result<String, ParserError>
|
pub fn write(&self, tpl : (FileHeaderData, String)) -> Result<String, ParserError>
|
||||||
{
|
{
|
||||||
let (header, data) = tpl;
|
let (header, data) = tpl;
|
||||||
let h_text = try!(self.headerp.write(&header));
|
let h_text = try!(self.headerp.write(&header));
|
||||||
|
|
Loading…
Reference in a new issue