From d949cddc650fb2fd0f87d81307b11815e8230db0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Sun, 17 Jan 2016 16:58:58 +0100 Subject: [PATCH 1/8] Implement Store::retrieve --- libimagstore/src/error.rs | 3 +++ libimagstore/src/store.rs | 9 ++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/libimagstore/src/error.rs b/libimagstore/src/error.rs index 9dc48bb5..eabb26b7 100644 --- a/libimagstore/src/error.rs +++ b/libimagstore/src/error.rs @@ -17,6 +17,7 @@ pub enum StoreErrorKind { FileNotCreated, StorePathExists, StorePathCreate, + LockPoisoned, // maybe more } @@ -30,6 +31,8 @@ fn store_error_type_as_str(e: &StoreErrorKind) -> &'static str { &StoreErrorKind::FileNotCreated => "File corresponding to ID could not be created", &StoreErrorKind::StorePathExists => "Store path exists", &StoreErrorKind::StorePathCreate => "Store path create", + &StoreErrorKind::LockPoisoned + => "The internal Store Lock has been poisoned", } } diff --git a/libimagstore/src/store.rs b/libimagstore/src/store.rs index 74ffcc58..9c92980a 100644 --- a/libimagstore/src/store.rs +++ b/libimagstore/src/store.rs @@ -83,7 +83,14 @@ impl Store { /// 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> { - unimplemented!(); + let hsmap = self.entries.write(); + if hsmap.is_err() { + return Err(StoreError::new(StoreErrorKind::LockPoisoned, None)) + } + hsmap.unwrap().get_mut(&id) + .ok_or(StoreError::new(StoreErrorKind::IdNotFound, None)) + .and_then(|store_entry| store_entry.get_entry()) + .and_then(|entry| Ok(FileLockEntry::new(self, entry, id))) } /// Return the `FileLockEntry` and write to disk From ba2e52788bd8239d4100f005e24b35ac35a66f4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Sat, 23 Jan 2016 16:15:34 +0100 Subject: [PATCH 2/8] Add entry_creation --- libimagstore/src/entry.rs | 8 +++++++ libimagstore/src/error.rs | 2 ++ libimagstore/src/header.rs | 18 ++++++++++++++++ libimagstore/src/store.rs | 43 +++++++++++++++++++++++++++----------- 4 files changed, 59 insertions(+), 12 deletions(-) diff --git a/libimagstore/src/entry.rs b/libimagstore/src/entry.rs index b463a81e..5bcd760d 100644 --- a/libimagstore/src/entry.rs +++ b/libimagstore/src/entry.rs @@ -16,6 +16,14 @@ pub struct Entry { impl Entry { + fn new(loc: StoreId) -> Entry { + Entry { + location: loc, + header: EntryHeader::new_current(), + content: EntryContent::new() + } + } + pub fn get_location(&self) -> &StoreId { &self.location } diff --git a/libimagstore/src/error.rs b/libimagstore/src/error.rs index eabb26b7..7774a9f8 100644 --- a/libimagstore/src/error.rs +++ b/libimagstore/src/error.rs @@ -18,6 +18,7 @@ pub enum StoreErrorKind { StorePathExists, StorePathCreate, LockPoisoned, + EntryAlreadyBorrowed // maybe more } @@ -33,6 +34,7 @@ fn store_error_type_as_str(e: &StoreErrorKind) -> &'static str { &StoreErrorKind::StorePathCreate => "Store path create", &StoreErrorKind::LockPoisoned => "The internal Store Lock has been poisoned", + &StoreErrorKind::EntryAlreadyBorrowed => "Entry is already borrowed", } } diff --git a/libimagstore/src/header.rs b/libimagstore/src/header.rs index dc432da2..657182d5 100644 --- a/libimagstore/src/header.rs +++ b/libimagstore/src/header.rs @@ -1,6 +1,7 @@ use std::collections::BTreeMap; use std::error::Error; use std::result::Result as RResult; +use std::collections::BTreeMap; use toml::{Array, Table, Value}; use version; @@ -102,6 +103,23 @@ impl EntryHeader { } } + pub fn new_current() -> EntryHeader { + EntryHeader { + toml: { + let mut header = BTreeMap::new(); + let sub = { + let mut sub = BTreeMap::new(); + sub.insert("version".into(), Value::String(version!())); + + Value::Table(sub) + }; + + header.insert("imag".into(), sub); + header + } + } + } + /** * Get the table which lives in the background */ diff --git a/libimagstore/src/store.rs b/libimagstore/src/store.rs index 9c92980a..593ab19c 100644 --- a/libimagstore/src/store.rs +++ b/libimagstore/src/store.rs @@ -11,21 +11,29 @@ use fs2::FileExt; use entry::Entry; use error::{StoreError, StoreErrorKind}; use storeid::StoreId; +use lazyfile::LazyFile; /// The Result Type returned by any interaction with the store that could fail pub type Result = RResult; -#[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 +enum StoreEntry { + Present(StoreId, LazyFile), + Borrowed +} + +impl PartialEq for StoreEntry { + fn eq(&self, other: &StoreEntry) -> bool { + use store::StoreEntry::*; + match (*self, *other) { + (Borrowed, Borrowed) => true, + (Borrowed, Present(_,_)) => false, + (Present(_,_), Borrowed) => false, + (Present(ref a,_), Present(ref b, _)) => a == b + } + } } @@ -33,7 +41,20 @@ impl StoreEntry { /// The entry is currently borrowed, meaning that some thread is currently /// mutating it fn is_borrowed(&self) -> bool { - self.entry == StoreEntryPresence::Borrowed + *self == StoreEntry::Borrowed + } + + fn get_entry(&self) -> Result { + if let &StoreEntry::Present(ref id, ref file) = self { + let file = file.get_file(); + if let Err(StoreError{err_type: StoreErrorKind::FileNotFound, ..}) = file { + Ok(Entry::new(id.clone())) + } else { + unimplemented!() + } + } else { + return Err(StoreError::new(StoreErrorKind::EntryAlreadyBorrowed, None)) + } } } @@ -134,11 +155,9 @@ impl Drop for Store { /** * Unlock all files on drop * - * TODO: Error message when file cannot be unlocked? + * TODO: Unlock them */ fn drop(&mut self) { - self.entries.write().unwrap() - .iter().map(|f| f.1.file.unlock()); } } From 50413101c4534272f046cd98aa0647e14d99138a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Sat, 23 Jan 2016 16:26:02 +0100 Subject: [PATCH 3/8] Move entry, content and header into store --- libimagstore/src/content.rs | 4 - libimagstore/src/entry.rs | 48 ------ libimagstore/src/error.rs | 77 ++++++++- libimagstore/src/header.rs | 325 ------------------------------------ libimagstore/src/lib.rs | 3 - libimagstore/src/store.rs | 290 +++++++++++++++++++++++++++++++- 6 files changed, 357 insertions(+), 390 deletions(-) delete mode 100644 libimagstore/src/content.rs delete mode 100644 libimagstore/src/entry.rs delete mode 100644 libimagstore/src/header.rs diff --git a/libimagstore/src/content.rs b/libimagstore/src/content.rs deleted file mode 100644 index ade530f1..00000000 --- a/libimagstore/src/content.rs +++ /dev/null @@ -1,4 +0,0 @@ -/** - * EntryContent type - */ -pub type EntryContent = String; diff --git a/libimagstore/src/entry.rs b/libimagstore/src/entry.rs deleted file mode 100644 index 5bcd760d..00000000 --- a/libimagstore/src/entry.rs +++ /dev/null @@ -1,48 +0,0 @@ -use header::EntryHeader; -use content::EntryContent; -use storeid::StoreId; - -/** - * An Entry of the store - * - * Contains location, header and content part. - */ -#[derive(Debug, Clone)] -pub struct Entry { - location: StoreId, - header: EntryHeader, - content: EntryContent, -} - -impl Entry { - - fn new(loc: StoreId) -> Entry { - Entry { - location: loc, - header: EntryHeader::new_current(), - content: EntryContent::new() - } - } - - pub fn get_location(&self) -> &StoreId { - &self.location - } - - pub fn get_header(&self) -> &EntryHeader { - &self.header - } - - pub fn get_header_mut(&mut self) -> &mut EntryHeader { - &mut self.header - } - - pub fn get_content(&self) -> &EntryContent { - &self.content - } - - pub fn get_content_mut(&mut self) -> &mut EntryContent { - &mut self.content - } - -} - diff --git a/libimagstore/src/error.rs b/libimagstore/src/error.rs index 7774a9f8..f893b48a 100644 --- a/libimagstore/src/error.rs +++ b/libimagstore/src/error.rs @@ -1,8 +1,9 @@ use std::error::Error; -use std::fmt::Display; -use std::fmt::Formatter; use std::fmt::Error as FmtError; use std::clone::Clone; +use std::fmt::{Debug, Display, Formatter}; +use std::fmt; +use toml; /** * Kind of store error @@ -19,7 +20,7 @@ pub enum StoreErrorKind { StorePathCreate, LockPoisoned, EntryAlreadyBorrowed - // maybe more + // maybe more } fn store_error_type_as_str(e: &StoreErrorKind) -> &'static str { @@ -34,7 +35,7 @@ fn store_error_type_as_str(e: &StoreErrorKind) -> &'static str { &StoreErrorKind::StorePathCreate => "Store path create", &StoreErrorKind::LockPoisoned => "The internal Store Lock has been poisoned", - &StoreErrorKind::EntryAlreadyBorrowed => "Entry is already borrowed", + &StoreErrorKind::EntryAlreadyBorrowed => "Entry is already borrowed", } } @@ -63,12 +64,12 @@ impl StoreError { */ pub fn new(errtype: StoreErrorKind, cause: Option>) -> StoreError - { - StoreError { - err_type: errtype, - cause: cause, + { + StoreError { + err_type: errtype, + cause: cause, + } } - } /** * Get the error type of this StoreError @@ -100,3 +101,61 @@ impl Error for StoreError { } + +#[derive(Clone)] +pub enum ParserErrorKind { + TOMLParserErrors, + MissingMainSection, + MissingVersionInfo, +} + +pub struct ParserError { + kind: ParserErrorKind, + cause: Option>, +} + +impl ParserError { + + pub fn new(k: ParserErrorKind, cause: Option>) -> ParserError { + ParserError { + kind: k, + cause: cause, + } + } + +} + +impl Debug for ParserError { + + fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { + try!(write!(f, "{:?}", self.description())); + Ok(()) + } + +} + +impl Display for ParserError { + + fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { + try!(write!(f, "{}", self.description())); + Ok(()) + } + +} + +impl Error for ParserError { + + fn description(&self) -> &str { + match self.kind { + ParserErrorKind::TOMLParserErrors => "Several TOML-Parser-Errors", + ParserErrorKind::MissingMainSection => "Missing main section", + ParserErrorKind::MissingVersionInfo => "Missing version information in main section", + } + } + + fn cause(&self) -> Option<&Error> { + self.cause.as_ref().map(|e| &**e) + } + +} + diff --git a/libimagstore/src/header.rs b/libimagstore/src/header.rs deleted file mode 100644 index 657182d5..00000000 --- a/libimagstore/src/header.rs +++ /dev/null @@ -1,325 +0,0 @@ -use std::collections::BTreeMap; -use std::error::Error; -use std::result::Result as RResult; -use std::collections::BTreeMap; - -use toml::{Array, Table, Value}; -use version; - -use self::error::ParserErrorKind; -use self::error::ParserError; - -pub mod error { - use std::fmt::{Debug, Display, Formatter}; - use std::fmt; - use std::error::Error; - use toml; - - #[derive(Clone)] - pub enum ParserErrorKind { - TOMLParserErrors, - MissingMainSection, - MissingVersionInfo, - } - - pub struct ParserError { - kind: ParserErrorKind, - cause: Option>, - } - - impl ParserError { - - pub fn new(k: ParserErrorKind, cause: Option>) -> ParserError { - ParserError { - kind: k, - cause: cause, - } - } - - } - - impl Debug for ParserError { - - fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { - try!(write!(f, "{:?}", self.description())); - Ok(()) - } - - } - - impl Display for ParserError { - - fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { - try!(write!(f, "{}", self.description())); - Ok(()) - } - - } - - impl Error for ParserError { - - fn description(&self) -> &str { - match self.kind { - ParserErrorKind::TOMLParserErrors => "Several TOML-Parser-Errors", - ParserErrorKind::MissingMainSection => "Missing main section", - ParserErrorKind::MissingVersionInfo => "Missing version information in main section", - } - } - - fn cause(&self) -> Option<&Error> { - self.cause.as_ref().map(|e| &**e) - } - - } - -} - -/** - * EntryHeader - * - * This is basically a wrapper around toml::Table which provides convenience to the user of the - * librray. - */ -#[derive(Debug, Clone)] -pub struct EntryHeader { - toml: Table, -} - -pub type Result = RResult; - -/** - * Wrapper type around file header (TOML) object - */ -impl EntryHeader { - - /** - * Get a new header object with a already-filled toml table - * - * Default header values are inserted into the header object by default. - */ - pub fn new() -> EntryHeader { - EntryHeader { - toml: build_default_header(), - } - } - - pub fn new_current() -> EntryHeader { - EntryHeader { - toml: { - let mut header = BTreeMap::new(); - let sub = { - let mut sub = BTreeMap::new(); - sub.insert("version".into(), Value::String(version!())); - - Value::Table(sub) - }; - - header.insert("imag".into(), sub); - header - } - } - } - - /** - * Get the table which lives in the background - */ - pub fn toml(&self) -> &Table { - &self.toml - } - - pub fn parse(s: &str) -> Result { - use toml::Parser; - - let mut parser = Parser::new(s); - parser.parse() - .ok_or(ParserError::new(ParserErrorKind::TOMLParserErrors, None)) - .and_then(|t| verify_header_consistency(t)) - .map(|t| { - EntryHeader { - toml: t - } - }) - } - -} - -fn verify_header_consistency(t: Table) -> Result { - if !has_main_section(&t) { - Err(ParserError::new(ParserErrorKind::MissingMainSection, None)) - } else if !has_imag_version_in_main_section(&t) { - Err(ParserError::new(ParserErrorKind::MissingVersionInfo, None)) - } else { - Ok(t) - } -} - -fn has_main_section(t: &Table) -> bool { - t.contains_key("imag") && - match t.get("imag") { - Some(&Value::Table(_)) => true, - Some(_) => false, - None => false, - } -} - -fn has_imag_version_in_main_section(t: &Table) -> bool { - use semver::Version; - - match t.get("imag").unwrap() { - &Value::Table(ref sec) => { - sec.get("version") - .and_then(|v| { - match v { - &Value::String(ref s) => { - Some(Version::parse(&s[..]).is_ok()) - }, - _ => Some(false), - } - }) - .unwrap_or(false) - } - _ => false, - } -} - - -#[cfg(test)] -mod test { - use std::collections::BTreeMap; - - use toml::Value; - - #[test] - fn test_imag_section() { - use super::has_main_section; - - let mut map = BTreeMap::new(); - map.insert("imag".into(), Value::Table(BTreeMap::new())); - - assert!(has_main_section(&map)); - } - - #[test] - fn test_imag_invalid_section_type() { - use super::has_main_section; - - let mut map = BTreeMap::new(); - map.insert("imag".into(), Value::Boolean(false)); - - assert!(!has_main_section(&map)); - } - - #[test] - fn test_imag_abscent_main_section() { - use super::has_main_section; - - let mut map = BTreeMap::new(); - map.insert("not_imag".into(), Value::Boolean(false)); - - assert!(!has_main_section(&map)); - } - - #[test] - fn test_main_section_without_version() { - use super::has_imag_version_in_main_section; - - let mut map = BTreeMap::new(); - map.insert("imag".into(), Value::Table(BTreeMap::new())); - - assert!(!has_imag_version_in_main_section(&map)); - } - - #[test] - fn test_main_section_with_version() { - use super::has_imag_version_in_main_section; - - let mut map = BTreeMap::new(); - let mut sub = BTreeMap::new(); - sub.insert("version".into(), Value::String("0.0.0".into())); - map.insert("imag".into(), Value::Table(sub)); - - assert!(has_imag_version_in_main_section(&map)); - } - - #[test] - fn test_main_section_with_version_in_wrong_type() { - use super::has_imag_version_in_main_section; - - let mut map = BTreeMap::new(); - let mut sub = BTreeMap::new(); - sub.insert("version".into(), Value::Boolean(false)); - map.insert("imag".into(), Value::Table(sub)); - - assert!(!has_imag_version_in_main_section(&map)); - } - - #[test] - fn test_verification_good() { - use super::verify_header_consistency; - - let mut header = BTreeMap::new(); - let sub = { - let mut sub = BTreeMap::new(); - sub.insert("version".into(), Value::String(String::from("0.0.0"))); - - Value::Table(sub) - }; - - header.insert("imag".into(), sub); - - assert!(verify_header_consistency(header).is_ok()); - } - - #[test] - fn test_verification_invalid_versionstring() { - use super::verify_header_consistency; - - let mut header = BTreeMap::new(); - let sub = { - let mut sub = BTreeMap::new(); - sub.insert("version".into(), Value::String(String::from("000"))); - - Value::Table(sub) - }; - - header.insert("imag".into(), sub); - - assert!(!verify_header_consistency(header).is_ok()); - } - - - #[test] - fn test_verification_current_version() { - use version; - - use super::verify_header_consistency; - - let mut header = BTreeMap::new(); - let sub = { - let mut sub = BTreeMap::new(); - sub.insert("version".into(), Value::String(version!())); - - Value::Table(sub) - }; - - header.insert("imag".into(), sub); - - assert!(verify_header_consistency(header).is_ok()); - } -} - -fn build_default_header() -> BTreeMap { - let mut m = BTreeMap::new(); - - m.insert(String::from("imag"), { - let mut imag_map = BTreeMap::::new(); - - imag_map.insert(String::from("version"), Value::String(version!())); - imag_map.insert(String::from("links"), Value::Array(vec![])); - - Value::Table(imag_map) - }); - - m -} - diff --git a/libimagstore/src/lib.rs b/libimagstore/src/lib.rs index 69665320..b6152caa 100644 --- a/libimagstore/src/lib.rs +++ b/libimagstore/src/lib.rs @@ -6,10 +6,7 @@ extern crate toml; extern crate semver; pub mod storeid; -pub mod content; -pub mod entry; pub mod error; -pub mod header; pub mod store; mod lazyfile; diff --git a/libimagstore/src/store.rs b/libimagstore/src/store.rs index 593ab19c..99bdb1ae 100644 --- a/libimagstore/src/store.rs +++ b/libimagstore/src/store.rs @@ -5,10 +5,14 @@ use std::path::PathBuf; use std::result::Result as RResult; use std::sync::Arc; use std::sync::RwLock; +use std::error::Error; +use std::collections::BTreeMap; + use fs2::FileExt; +use toml::{Table, Value}; -use entry::Entry; +use error::{ParserErrorKind, ParserError}; use error::{StoreError, StoreErrorKind}; use storeid::StoreId; use lazyfile::LazyFile; @@ -198,3 +202,287 @@ impl<'a> Drop for FileLockEntry<'a> { self.store._update(self).unwrap() } } + +/** + * EntryContent type + */ +pub type EntryContent = String; + +/** + * EntryHeader + * + * This is basically a wrapper around toml::Table which provides convenience to the user of the + * librray. + */ +#[derive(Debug, Clone)] +pub struct EntryHeader { + toml: Table, +} + +pub type EntryResult = RResult; + +/** + * Wrapper type around file header (TOML) object + */ +impl EntryHeader { + + /** + * Get a new header object with a already-filled toml table + */ + pub fn new(toml: Table) -> EntryHeader { + EntryHeader { + toml: toml, + } + } + + pub fn new_current() -> EntryHeader { + EntryHeader { + toml: build_default_header() + } + } + + /** + * Get the table which lives in the background + */ + pub fn toml(&self) -> &Table { + &self.toml + } + + pub fn parse(s: &str) -> EntryResult { + use toml::Parser; + + let mut parser = Parser::new(s); + parser.parse() + .ok_or(ParserError::new(ParserErrorKind::TOMLParserErrors, None)) + .and_then(|t| verify_header_consistency(t)) + .map(|t| EntryHeader::new(t)) + } + +} + +fn build_default_header() -> BTreeMap { + let mut m = BTreeMap::new(); + + m.insert(String::from("imag"), { + let mut imag_map = BTreeMap::::new(); + + imag_map.insert(String::from("version"), Value::String(version!())); + imag_map.insert(String::from("links"), Value::Array(vec![])); + + Value::Table(imag_map) + }); + + m +} + +fn verify_header_consistency(t: Table) -> EntryResult
{ + if !has_main_section(&t) { + Err(ParserError::new(ParserErrorKind::MissingMainSection, None)) + } else if !has_imag_version_in_main_section(&t) { + Err(ParserError::new(ParserErrorKind::MissingVersionInfo, None)) + } else { + Ok(t) + } +} + +fn has_main_section(t: &Table) -> bool { + t.contains_key("imag") && + match t.get("imag") { + Some(&Value::Table(_)) => true, + Some(_) => false, + None => false, + } +} + +fn has_imag_version_in_main_section(t: &Table) -> bool { + use semver::Version; + + match t.get("imag").unwrap() { + &Value::Table(ref sec) => { + sec.get("version") + .and_then(|v| { + match v { + &Value::String(ref s) => { + Some(Version::parse(&s[..]).is_ok()) + }, + _ => Some(false), + } + }) + .unwrap_or(false) + } + _ => false, + } +} + +/** + * An Entry of the store + * + * Contains location, header and content part. + */ +#[derive(Debug, Clone)] +pub struct Entry { + location: StoreId, + header: EntryHeader, + content: EntryContent, +} + +impl Entry { + + fn new(loc: StoreId) -> Entry { + Entry { + location: loc, + header: EntryHeader::new_current(), + content: EntryContent::new() + } + } + + pub fn get_location(&self) -> &StoreId { + &self.location + } + + pub fn get_header(&self) -> &EntryHeader { + &self.header + } + + pub fn get_header_mut(&mut self) -> &mut EntryHeader { + &mut self.header + } + + pub fn get_content(&self) -> &EntryContent { + &self.content + } + + pub fn get_content_mut(&mut self) -> &mut EntryContent { + &mut self.content + } + +} + + +#[cfg(test)] +mod test { + use std::collections::BTreeMap; + + use toml::Value; + + #[test] + fn test_imag_section() { + use super::has_main_section; + + let mut map = BTreeMap::new(); + map.insert("imag".into(), Value::Table(BTreeMap::new())); + + assert!(has_main_section(&map)); + } + + #[test] + fn test_imag_invalid_section_type() { + use super::has_main_section; + + let mut map = BTreeMap::new(); + map.insert("imag".into(), Value::Boolean(false)); + + assert!(!has_main_section(&map)); + } + + #[test] + fn test_imag_abscent_main_section() { + use super::has_main_section; + + let mut map = BTreeMap::new(); + map.insert("not_imag".into(), Value::Boolean(false)); + + assert!(!has_main_section(&map)); + } + + #[test] + fn test_main_section_without_version() { + use super::has_imag_version_in_main_section; + + let mut map = BTreeMap::new(); + map.insert("imag".into(), Value::Table(BTreeMap::new())); + + assert!(!has_imag_version_in_main_section(&map)); + } + + #[test] + fn test_main_section_with_version() { + use super::has_imag_version_in_main_section; + + let mut map = BTreeMap::new(); + let mut sub = BTreeMap::new(); + sub.insert("version".into(), Value::String("0.0.0".into())); + map.insert("imag".into(), Value::Table(sub)); + + assert!(has_imag_version_in_main_section(&map)); + } + + #[test] + fn test_main_section_with_version_in_wrong_type() { + use super::has_imag_version_in_main_section; + + let mut map = BTreeMap::new(); + let mut sub = BTreeMap::new(); + sub.insert("version".into(), Value::Boolean(false)); + map.insert("imag".into(), Value::Table(sub)); + + assert!(!has_imag_version_in_main_section(&map)); + } + + #[test] + fn test_verification_good() { + use super::verify_header_consistency; + + let mut header = BTreeMap::new(); + let sub = { + let mut sub = BTreeMap::new(); + sub.insert("version".into(), Value::String(String::from("0.0.0"))); + + Value::Table(sub) + }; + + header.insert("imag".into(), sub); + + assert!(verify_header_consistency(header).is_ok()); + } + + #[test] + fn test_verification_invalid_versionstring() { + use super::verify_header_consistency; + + let mut header = BTreeMap::new(); + let sub = { + let mut sub = BTreeMap::new(); + sub.insert("version".into(), Value::String(String::from("000"))); + + Value::Table(sub) + }; + + header.insert("imag".into(), sub); + + assert!(!verify_header_consistency(header).is_ok()); + } + + + #[test] + fn test_verification_current_version() { + use version; + + use super::verify_header_consistency; + + let mut header = BTreeMap::new(); + let sub = { + let mut sub = BTreeMap::new(); + sub.insert("version".into(), Value::String(version!())); + + Value::Table(sub) + }; + + header.insert("imag".into(), sub); + + assert!(verify_header_consistency(header).is_ok()); + } +} + + + From 97b7090824a83b5350513d697da69122b9fdd93a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Sat, 23 Jan 2016 17:03:21 +0100 Subject: [PATCH 4/8] Partially implement get_entry --- libimagstore/src/error.rs | 2 +- libimagstore/src/store.rs | 41 +++++++++++++++++++-------------------- 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/libimagstore/src/error.rs b/libimagstore/src/error.rs index f893b48a..ed3140ba 100644 --- a/libimagstore/src/error.rs +++ b/libimagstore/src/error.rs @@ -8,7 +8,7 @@ use toml; /** * Kind of store error */ -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, PartialEq)] pub enum StoreErrorKind { FileError, IdLocked, diff --git a/libimagstore/src/store.rs b/libimagstore/src/store.rs index 99bdb1ae..0badb99a 100644 --- a/libimagstore/src/store.rs +++ b/libimagstore/src/store.rs @@ -21,39 +21,38 @@ use lazyfile::LazyFile; pub type Result = RResult; -/// A store entry, depending on the option type it is either borrowed currently -/// or not. -enum StoreEntry { - Present(StoreId, LazyFile), +#[derive(PartialEq)] +enum StoreEntryStatus { + Present, Borrowed } -impl PartialEq for StoreEntry { - fn eq(&self, other: &StoreEntry) -> bool { - use store::StoreEntry::*; - match (*self, *other) { - (Borrowed, Borrowed) => true, - (Borrowed, Present(_,_)) => false, - (Present(_,_), Borrowed) => false, - (Present(ref a,_), Present(ref b, _)) => a == b - } - } +/// A store entry, depending on the option type it is either borrowed currently +/// or not. +struct StoreEntry { + id: StoreId, + file: LazyFile, + status: StoreEntryStatus, } - impl StoreEntry { /// The entry is currently borrowed, meaning that some thread is currently /// mutating it fn is_borrowed(&self) -> bool { - *self == StoreEntry::Borrowed + self.status == StoreEntryStatus::Borrowed } - fn get_entry(&self) -> Result { - if let &StoreEntry::Present(ref id, ref file) = self { - let file = file.get_file(); - if let Err(StoreError{err_type: StoreErrorKind::FileNotFound, ..}) = file { - Ok(Entry::new(id.clone())) + fn get_entry(&mut self) -> Result { + if !self.is_borrowed() { + let file = self.file.get_file(); + if let Err(err) = file { + if err.err_type() == StoreErrorKind::FileNotFound { + Ok(Entry::new(self.id.clone())) + } else { + Err(err) + } } else { + // TODO: unimplemented!() } } else { From a64ffdfc5633fdd9bcc8628f67ae01e40095badb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Sat, 23 Jan 2016 17:30:01 +0100 Subject: [PATCH 5/8] Implement entry reading --- libimagstore/src/error.rs | 15 +++++++++++-- libimagstore/src/store.rs | 44 ++++++++++++++++++++++++++++++++++++--- 2 files changed, 54 insertions(+), 5 deletions(-) diff --git a/libimagstore/src/error.rs b/libimagstore/src/error.rs index ed3140ba..41d90c13 100644 --- a/libimagstore/src/error.rs +++ b/libimagstore/src/error.rs @@ -3,6 +3,7 @@ use std::fmt::Error as FmtError; use std::clone::Clone; use std::fmt::{Debug, Display, Formatter}; use std::fmt; +use std::convert::From; use toml; /** @@ -19,7 +20,8 @@ pub enum StoreErrorKind { StorePathExists, StorePathCreate, LockPoisoned, - EntryAlreadyBorrowed + EntryAlreadyBorrowed, + MalformedEntry, // maybe more } @@ -35,7 +37,8 @@ fn store_error_type_as_str(e: &StoreErrorKind) -> &'static str { &StoreErrorKind::StorePathCreate => "Store path create", &StoreErrorKind::LockPoisoned => "The internal Store Lock has been poisoned", - &StoreErrorKind::EntryAlreadyBorrowed => "Entry is already borrowed", + &StoreErrorKind::EntryAlreadyBorrowed => "Entry is already borrowed", + &StoreErrorKind::MalformedEntry => "Entry has invalid formatting, missing header", } } @@ -101,6 +104,14 @@ impl Error for StoreError { } +impl From for StoreError { + fn from(ps: ParserError) -> StoreError { + StoreError { + err_type: StoreErrorKind::MalformedEntry, + cause: Some(Box::new(ps)), + } + } +} #[derive(Clone)] pub enum ParserErrorKind { diff --git a/libimagstore/src/store.rs b/libimagstore/src/store.rs index 0badb99a..0ffbed42 100644 --- a/libimagstore/src/store.rs +++ b/libimagstore/src/store.rs @@ -11,6 +11,7 @@ use std::collections::BTreeMap; use fs2::FileExt; use toml::{Table, Value}; +use regex::Regex; use error::{ParserErrorKind, ParserError}; use error::{StoreError, StoreErrorKind}; @@ -44,7 +45,7 @@ impl StoreEntry { fn get_entry(&mut self) -> Result { if !self.is_borrowed() { - let file = self.file.get_file(); + let file = self.file.get_file_mut(); if let Err(err) = file { if err.err_type() == StoreErrorKind::FileNotFound { Ok(Entry::new(self.id.clone())) @@ -53,7 +54,7 @@ impl StoreEntry { } } else { // TODO: - unimplemented!() + Entry::from_file(self.id.clone(), file.unwrap()) } } else { return Err(StoreError::new(StoreErrorKind::EntryAlreadyBorrowed, None)) @@ -307,7 +308,7 @@ fn has_imag_version_in_main_section(t: &Table) -> bool { _ => Some(false), } }) - .unwrap_or(false) + .unwrap_or(false) } _ => false, } @@ -335,6 +336,43 @@ impl Entry { } } + fn from_file(loc: StoreId, file: &mut File) -> Result { + use std::io::Read; + let file = { + let mut buff = String::new(); + file.read_to_string(&mut buff); + buff + }; + + let re = Regex::new(r"(?smx) + ^---$ + (?P
.*) # Header + ^---$ + (?P.*) # Content + ").unwrap(); + + let matches = re.captures(&file[..]); + + if matches.is_none() { + return Err(StoreError::new(StoreErrorKind::MalformedEntry, None)); + } + + let matches = matches.unwrap(); + + let header = matches.name("header"); + let content = matches.name("content").unwrap_or(""); + + if header.is_none() { + return Err(StoreError::new(StoreErrorKind::MalformedEntry, None)); + } + + Ok(Entry { + location: loc, + header: try!(EntryHeader::parse(header.unwrap())), + content: content.into(), + }) + } + pub fn get_location(&self) -> &StoreId { &self.location } From 15931d3471c732480613a73e55c81fcbb831bc45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Sat, 23 Jan 2016 17:40:39 +0100 Subject: [PATCH 6/8] Remove EntryHeader::new_current --- libimagstore/src/store.rs | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/libimagstore/src/store.rs b/libimagstore/src/store.rs index 0ffbed42..bd74cd36 100644 --- a/libimagstore/src/store.rs +++ b/libimagstore/src/store.rs @@ -226,16 +226,7 @@ pub type EntryResult = RResult; */ impl EntryHeader { - /** - * Get a new header object with a already-filled toml table - */ - pub fn new(toml: Table) -> EntryHeader { - EntryHeader { - toml: toml, - } - } - - pub fn new_current() -> EntryHeader { + pub fn new() -> EntryHeader { EntryHeader { toml: build_default_header() } From fdc3dde95b1f3312d031ce05a8fb4b79fef93767 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Sat, 23 Jan 2016 17:48:45 +0100 Subject: [PATCH 7/8] Change names to reflect changes --- libimagstore/src/store.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/libimagstore/src/store.rs b/libimagstore/src/store.rs index bd74cd36..245e591c 100644 --- a/libimagstore/src/store.rs +++ b/libimagstore/src/store.rs @@ -232,6 +232,12 @@ impl EntryHeader { } } + fn from_table(t: Table) -> EntryHeader { + EntryHeader { + toml: t + } + } + /** * Get the table which lives in the background */ @@ -246,7 +252,7 @@ impl EntryHeader { parser.parse() .ok_or(ParserError::new(ParserErrorKind::TOMLParserErrors, None)) .and_then(|t| verify_header_consistency(t)) - .map(|t| EntryHeader::new(t)) + .map(|t| EntryHeader::from_table(t)) } } @@ -322,7 +328,7 @@ impl Entry { fn new(loc: StoreId) -> Entry { Entry { location: loc, - header: EntryHeader::new_current(), + header: EntryHeader::new(), content: EntryContent::new() } } From c7f584a81a49c981d4d143348345b2ea6e5dc237 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Sun, 24 Jan 2016 16:01:37 +0100 Subject: [PATCH 8/8] Add from_str to Entry --- libimagstore/src/error.rs | 11 +++++++++++ libimagstore/src/store.rs | 36 +++++++++++++++++++++++++++++------- 2 files changed, 40 insertions(+), 7 deletions(-) diff --git a/libimagstore/src/error.rs b/libimagstore/src/error.rs index 41d90c13..9cd19814 100644 --- a/libimagstore/src/error.rs +++ b/libimagstore/src/error.rs @@ -17,6 +17,7 @@ pub enum StoreErrorKind { OutOfMemory, FileNotFound, FileNotCreated, + IoError, StorePathExists, StorePathCreate, LockPoisoned, @@ -33,6 +34,7 @@ fn store_error_type_as_str(e: &StoreErrorKind) -> &'static str { &StoreErrorKind::OutOfMemory => "Out of Memory", &StoreErrorKind::FileNotFound => "File corresponding to ID not found", &StoreErrorKind::FileNotCreated => "File corresponding to ID could not be created", + &StoreErrorKind::IoError => "File Error", &StoreErrorKind::StorePathExists => "Store path exists", &StoreErrorKind::StorePathCreate => "Store path create", &StoreErrorKind::LockPoisoned @@ -113,6 +115,15 @@ impl From for StoreError { } } +impl From<::std::io::Error> for StoreError { + fn from(ps: ::std::io::Error) -> StoreError { + StoreError { + err_type: StoreErrorKind::IoError, + cause: Some(Box::new(ps)), + } + } +} + #[derive(Clone)] pub enum ParserErrorKind { TOMLParserErrors, diff --git a/libimagstore/src/store.rs b/libimagstore/src/store.rs index 245e591c..f6e78611 100644 --- a/libimagstore/src/store.rs +++ b/libimagstore/src/store.rs @@ -334,21 +334,24 @@ impl Entry { } fn from_file(loc: StoreId, file: &mut File) -> Result { - use std::io::Read; - let file = { - let mut buff = String::new(); - file.read_to_string(&mut buff); - buff + let text = { + use std::io::Read; + let mut s = String::new(); + try!(file.read_to_string(&mut s)); + s }; + Self::from_str(loc, &text[..]) + } + fn from_str(loc: StoreId, s: &str) -> Result { let re = Regex::new(r"(?smx) ^---$ (?P
.*) # Header - ^---$ + ^---$\n (?P.*) # Content ").unwrap(); - let matches = re.captures(&file[..]); + let matches = re.captures(s); if matches.is_none() { return Err(StoreError::new(StoreErrorKind::MalformedEntry, None)); @@ -516,6 +519,25 @@ mod test { assert!(verify_header_consistency(header).is_ok()); } + + static TEST_ENTRY : &'static str = "--- +[imag] +version = '0.0.3' +--- +Hai"; + + #[test] + fn test_entry_from_str() { + use super::Entry; + use std::path::PathBuf; + println!("{}", TEST_ENTRY); + let entry = Entry::from_str(PathBuf::from("/test/foo~1.3"), + TEST_ENTRY).unwrap(); + + assert_eq!(entry.content, "Hai"); + } + + }