Auto merge of #99 - TheNeikos:add-libstore, r=matthiasbeyer
Add libimagstore Alright, do check the file and feel free to post comments/ask questions
This commit is contained in:
commit
469bdd1d94
10 changed files with 517 additions and 3 deletions
51
Cargo.lock
generated
51
Cargo.lock
generated
|
@ -8,6 +8,30 @@ dependencies = [
|
|||
"libimagutil 0.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fs2"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"kernel32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kernel32-sys"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "libimagmodule"
|
||||
version = "0.1.0"
|
||||
|
@ -19,8 +43,35 @@ version = "0.1.0"
|
|||
[[package]]
|
||||
name = "libimagstore"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"fs2 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"toml 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libimagutil"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-serialize"
|
||||
version = "0.3.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.1.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"rustc-serialize 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-build"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
|
|
|
@ -4,3 +4,67 @@
|
|||
Store functionality
|
||||
-->
|
||||
|
||||
The "libstore" MUST define the programming-language level interface to the
|
||||
store on the file system.
|
||||
The library therefor MUST define and export types which can be used to get data
|
||||
from the filesystem.
|
||||
|
||||
## Types {#sec:libstore:types}
|
||||
|
||||
The types in @tbl:libstore:types are exported by the library.
|
||||
|
||||
| Type | Meaning |
|
||||
| :------------ | :----------------------------------------------- |
|
||||
| Entry | Entity on the Filesystem, File |
|
||||
| EntryContent | User-Content of the Entry |
|
||||
| EntryHeader | Header of the Entry |
|
||||
| Store | Store interface |
|
||||
| FileLockEntry | Handle to an Entry |
|
||||
|
||||
Table: Types the store library exports {#tbl:libstore:types}
|
||||
|
||||
Each of these types MUST export functions to work with the data the objects of
|
||||
the types contain.
|
||||
|
||||
### Entry {#sec:libstore:types:entry}
|
||||
|
||||
The `Entry` type MUST hold the following content:
|
||||
|
||||
- A path where on the filesystem the acutal file lives
|
||||
- An instance of `EntryContent` as interface to the content of the file
|
||||
(@sec:libstore:types:entrycontent).
|
||||
- An instance of `EntryHeader` as interface to the header of the file
|
||||
(@sec:libstore:types:entryheader).
|
||||
|
||||
The entry type MUST export functions to get
|
||||
|
||||
- The content object
|
||||
- The header object
|
||||
- The path of the actual file
|
||||
|
||||
The entry type MUST export functions to set
|
||||
|
||||
- The header object
|
||||
- The content object
|
||||
|
||||
### EntryContent {#sec:libstore:types:entrycontent}
|
||||
|
||||
The `EntryContent` type is an type-alias for `String`.
|
||||
|
||||
### EntryHeader {#sec:libstore:types:entryheader}
|
||||
|
||||
The `EntryHeader` type is an wrapper around the type, the TOML-Parser library
|
||||
exports.
|
||||
|
||||
It SHOULD contain utility functions to work with the header in a convenient way.
|
||||
|
||||
### Store {#sec:libstore:types:store}
|
||||
|
||||
The `Store` type MUST represent the interface to the store on the filesystem.
|
||||
It MUST contain CRUD-functionality to work with the entries in the store.
|
||||
It MUST contain a variable which contains the path of the store on the
|
||||
filesystem which is represented by an object of this type.
|
||||
It also MUST contain a getter for this variable.
|
||||
It MUST NOT contain a setter for this variable, as changing the store while the
|
||||
programm is running is not allowed.
|
||||
|
||||
|
|
51
libimagstore/Cargo.lock
generated
51
libimagstore/Cargo.lock
generated
|
@ -1,4 +1,55 @@
|
|||
[root]
|
||||
name = "libimagstore"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"fs2 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"toml 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fs2"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"kernel32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kernel32-sys"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"winapi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-serialize"
|
||||
version = "0.3.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.1.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"rustc-serialize 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-build"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
|
|
|
@ -2,3 +2,9 @@
|
|||
name = "libimagstore"
|
||||
version = "0.1.0"
|
||||
authors = ["Matthias Beyer <mail@beyermatthias.de>"]
|
||||
|
||||
[dependencies]
|
||||
|
||||
fs2 = "0.2.2"
|
||||
toml = "0.1.25"
|
||||
|
||||
|
|
1
libimagstore/src/content.rs
Normal file
1
libimagstore/src/content.rs
Normal file
|
@ -0,0 +1 @@
|
|||
pub type EntryContent = String;
|
11
libimagstore/src/entry.rs
Normal file
11
libimagstore/src/entry.rs
Normal file
|
@ -0,0 +1,11 @@
|
|||
use header::EntryHeader;
|
||||
use content::EntryContent;
|
||||
use store::StoreId;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Entry {
|
||||
location: StoreId,
|
||||
header: EntryHeader,
|
||||
content: EntryContent,
|
||||
}
|
||||
|
77
libimagstore/src/error.rs
Normal file
77
libimagstore/src/error.rs
Normal file
|
@ -0,0 +1,77 @@
|
|||
use std::error::Error;
|
||||
use std::fmt::Debug;
|
||||
use std::fmt::Display;
|
||||
use std::fmt::Formatter;
|
||||
use std::fmt::Error as FmtError;
|
||||
use std::clone::Clone;
|
||||
use std::convert::From;
|
||||
|
||||
use std::io::Error as IOError;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum StoreErrorKind {
|
||||
IdNotFound,
|
||||
OutOfMemory,
|
||||
// maybe more
|
||||
}
|
||||
|
||||
fn store_error_type_as_str(e: &StoreErrorKind) -> &'static str {
|
||||
match e {
|
||||
&StoreErrorKind::IdNotFound => "ID not found",
|
||||
&StoreErrorKind::OutOfMemory => "Out of Memory",
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for StoreErrorKind {
|
||||
|
||||
fn fmt(&self, fmt: &mut Formatter) -> Result<(), FmtError> {
|
||||
try!(write!(fmt, "{}", store_error_type_as_str(self)));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct StoreError {
|
||||
err_type: StoreErrorKind,
|
||||
cause: Option<Box<Error>>,
|
||||
}
|
||||
|
||||
impl StoreError {
|
||||
|
||||
pub fn new(errtype: StoreErrorKind, cause: Option<Box<Error>>)
|
||||
-> StoreError
|
||||
{
|
||||
StoreError {
|
||||
err_type: errtype,
|
||||
cause: cause,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn err_type(&self) -> StoreErrorKind {
|
||||
self.err_type.clone()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl Display for StoreError {
|
||||
|
||||
fn fmt(&self, fmt: &mut Formatter) -> Result<(), FmtError> {
|
||||
try!(write!(fmt, "[{}]", store_error_type_as_str(&self.err_type.clone())));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl Error for StoreError {
|
||||
|
||||
fn description(&self) -> &str {
|
||||
store_error_type_as_str(&self.err_type.clone())
|
||||
}
|
||||
|
||||
fn cause(&self) -> Option<&Error> {
|
||||
self.cause.as_ref().map(|e| &**e)
|
||||
}
|
||||
|
||||
}
|
||||
|
20
libimagstore/src/header.rs
Normal file
20
libimagstore/src/header.rs
Normal file
|
@ -0,0 +1,20 @@
|
|||
use toml::Table;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EntryHeader {
|
||||
toml: Table,
|
||||
}
|
||||
|
||||
impl EntryHeader {
|
||||
|
||||
pub fn new(toml: Table) -> EntryHeader {
|
||||
EntryHeader {
|
||||
toml: toml,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn toml(&self) -> &Table {
|
||||
&self.toml
|
||||
}
|
||||
|
||||
}
|
|
@ -1,3 +1,9 @@
|
|||
#[test]
|
||||
fn it_works() {
|
||||
}
|
||||
extern crate fs2;
|
||||
extern crate toml;
|
||||
|
||||
pub mod content;
|
||||
pub mod entry;
|
||||
pub mod error;
|
||||
pub mod header;
|
||||
pub mod store;
|
||||
|
||||
|
|
227
libimagstore/src/store.rs
Normal file
227
libimagstore/src/store.rs
Normal file
|
@ -0,0 +1,227 @@
|
|||
use std::collections::HashMap;
|
||||
use std::fs::File;
|
||||
use std::ops::Drop;
|
||||
use std::path::PathBuf;
|
||||
use std::result::Result as RResult;
|
||||
use std::sync::Arc;
|
||||
use std::sync::{RwLock, Mutex};
|
||||
|
||||
use fs2::FileExt;
|
||||
|
||||
use entry::Entry;
|
||||
use error::StoreError;
|
||||
|
||||
/// The Result Type returned by any interaction with the store that could fail
|
||||
pub type Result<T> = RResult<T, StoreError>;
|
||||
|
||||
/// The Index into the Store
|
||||
pub type StoreId = PathBuf;
|
||||
|
||||
/// This Trait allows you to convert various representations to a single one
|
||||
/// suitable for usage in the Store
|
||||
trait IntoStoreId {
|
||||
fn into_storeid(self) -> StoreId;
|
||||
}
|
||||
|
||||
impl<'a> IntoStoreId for &'a str {
|
||||
fn into_storeid(self) -> StoreId {
|
||||
PathBuf::from(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoStoreId for &'a String{
|
||||
fn into_storeid(self) -> StoreId {
|
||||
PathBuf::from(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoStoreId for String{
|
||||
fn into_storeid(self) -> StoreId {
|
||||
PathBuf::from(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoStoreId for PathBuf {
|
||||
fn into_storeid(self) -> StoreId {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoStoreId for &'a PathBuf {
|
||||
fn into_storeid(self) -> StoreId {
|
||||
self.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl<ISI: IntoStoreId> IntoStoreId for (ISI, ISI) {
|
||||
fn into_storeid(self) -> StoreId {
|
||||
let (first, second) = self;
|
||||
let mut res : StoreId = first.into_storeid();
|
||||
res.push(second.into_storeid());
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
enum StoreEntryPresence {
|
||||
Present,
|
||||
Borrowed
|
||||
}
|
||||
|
||||
/// A store entry, depending on the option type it is either borrowed currently
|
||||
/// or not.
|
||||
struct StoreEntry {
|
||||
file: File,
|
||||
entry: StoreEntryPresence
|
||||
}
|
||||
|
||||
|
||||
impl StoreEntry {
|
||||
/// The entry is currently borrowed, meaning that some thread is currently
|
||||
/// mutating it
|
||||
fn is_borrowed(&self) -> bool {
|
||||
self.entry == StoreEntryPresence::Borrowed
|
||||
}
|
||||
|
||||
/// Flush the entry to disk
|
||||
fn set_entry(&mut self, entry: Entry) -> Result<()> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
/// We borrow the entry
|
||||
fn get_entry(&mut self) -> Result<Entry> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
/// We copy the entry
|
||||
fn copy_entry(&mut self) -> Result<Entry> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
/// The Store itself, through this object one can interact with IMAG's entries
|
||||
pub struct Store {
|
||||
location: PathBuf,
|
||||
|
||||
/**
|
||||
* Internal Path->File cache map
|
||||
*
|
||||
* Caches the files, so they remain flock()ed
|
||||
*
|
||||
* Could be optimized for a threadsafe HashMap
|
||||
*/
|
||||
entries: Arc<RwLock<HashMap<StoreId, StoreEntry>>>,
|
||||
}
|
||||
|
||||
impl Store {
|
||||
/// Creates the Entry at the given location (inside the entry)
|
||||
pub fn create(&self, entry: Entry) -> Result<()> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
/// Borrow a given Entry. When the `FileLockEntry` is either `update`d or
|
||||
/// dropped, the new Entry is written to disk
|
||||
pub fn retrieve<'a>(&'a self, id: StoreId) -> Result<FileLockEntry<'a>> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
/// Return the `FileLockEntry` and write to disk
|
||||
pub fn update<'a>(&'a self, entry: FileLockEntry<'a>) -> Result<()> {
|
||||
self._update(&entry)
|
||||
}
|
||||
|
||||
/// Internal method to write to the filesystem store.
|
||||
///
|
||||
/// # Assumptions
|
||||
/// This method assumes that entry is dropped _right after_ the call, hence
|
||||
/// it is not public.
|
||||
fn _update<'a>(&'a self, entry: &FileLockEntry<'a>) -> Result<()> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
/// Retrieve a copy of a given entry, this cannot be used to mutate
|
||||
/// the one on disk
|
||||
pub fn retrieve_copy(&self, id: StoreId) -> Result<Entry> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
/// Delete an entry
|
||||
pub fn delete(&self, id: StoreId) -> Result<()> {
|
||||
unimplemented!();
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Store {
|
||||
|
||||
/**
|
||||
* Unlock all files on drop
|
||||
*
|
||||
* TODO: Error message when file cannot be unlocked?
|
||||
*/
|
||||
fn drop(&mut self) {
|
||||
self.entries.write().unwrap()
|
||||
.iter().map(|f| f.1.file.unlock());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// A struct that allows you to borrow an Entry
|
||||
pub struct FileLockEntry<'a> {
|
||||
store: &'a Store,
|
||||
entry: Entry,
|
||||
key: StoreId,
|
||||
}
|
||||
|
||||
impl<'a> FileLockEntry<'a, > {
|
||||
fn new(store: &'a Store, entry: Entry, key: StoreId) -> FileLockEntry<'a> {
|
||||
FileLockEntry {
|
||||
store: store,
|
||||
entry: entry,
|
||||
key: key,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ::std::ops::Deref for FileLockEntry<'a> {
|
||||
type Target = Entry;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.entry
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ::std::ops::DerefMut for FileLockEntry<'a> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.entry
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Drop for FileLockEntry<'a> {
|
||||
fn drop(&mut self) {
|
||||
self.store._update(self).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::path::PathBuf;
|
||||
use store::{StoreId, IntoStoreId};
|
||||
|
||||
#[test]
|
||||
fn into_storeid_trait() {
|
||||
let buf = PathBuf::from("abc/def");
|
||||
|
||||
let test = ("abc", "def");
|
||||
assert_eq!(buf, test.into_storeid());
|
||||
|
||||
let test = "abc/def";
|
||||
assert_eq!(buf, test.into_storeid());
|
||||
|
||||
let test = String::from("abc/def");
|
||||
assert_eq!(buf, test.into_storeid());
|
||||
|
||||
let test = PathBuf::from("abc/def");
|
||||
assert_eq!(buf, test.into_storeid());
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in a new issue