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:
Homu 2016-01-17 23:44:27 +09:00
commit 469bdd1d94
10 changed files with 517 additions and 3 deletions

51
Cargo.lock generated
View file

@ -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"

View file

@ -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.

View file

@ -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"

View file

@ -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"

View file

@ -0,0 +1 @@
pub type EntryContent = String;

11
libimagstore/src/entry.rs Normal file
View 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
View 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)
}
}

View 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
}
}

View file

@ -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
View 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());
}
}