15 Commits cd99873f17 ... 2c97d6f194

Author SHA1 Message Date
  Matthias Beyer 2c97d6f194 Merge pull request #974 from matthiasbeyer/libimagstore/embellishments 6 years ago
  Matthias Beyer f8ed6794c2 Merge pull request #975 from matthiasbeyer/libimagstore/backend-replacement 6 years ago
  Matthias Beyer a672e4db21 Remove TODO from Store::reset_backend() 6 years ago
  Matthias Beyer d33b435031 Merge pull request #922 from matthiasbeyer/libimagstore/all-entries 6 years ago
  Matthias Beyer bee97c8758 Add Store::entries() 7 years ago
  Matthias Beyer a4188a3100 Add proper impl of Store::reset_backend() 6 years ago
  Matthias Beyer c814171875 Add backend draining 6 years ago
  Matthias Beyer b572e7e8b9 Abstract away the output of the IO backend 6 years ago
  Matthias Beyer caf77012d9 Add tests to test backend replacing 6 years ago
  Matthias Beyer 41f3b10976 Add Runtime::store_backend_to_stdio() function 6 years ago
  Matthias Beyer c0936a0273 Add Store::reset_backend() function for re-setting the backend 6 years ago
  Matthias Beyer 97741fd4ee Remove unnecessary clone() call 6 years ago
  Matthias Beyer e75c37fbb2 Merge pull request #973 from matthiasbeyer/libimagstore/io-backend-knows-format 6 years ago
  Matthias Beyer 266311d743 Change backends to do less ser-/deserialization 6 years ago
  Matthias Beyer 52011a59b2 Move serialize/deserialize calls of Entry to backend 6 years ago

+ 22 - 51
doc/src/02000-store.md

@@ -201,12 +201,6 @@ The `InMemoryFileAbstractionInstance` implementation is corrosponding to
 the `InMemoryFileAbstraction` implementation - for the in-memory
 "filesystem".
 
-The implementation of the `get_file_content()` function had to be
-changed to return a `String` rather than a `&mut Read` because of
-lifetime issues.
-This change is store-internally and the API of the store itself was not
-affected.
-
 ## The StdIo backend {#sec:thestore:backends:stdio}
 
 Sidenote: The name is "StdIo" because its main purpose is Stdin/Stdio, but it
@@ -231,42 +225,29 @@ implementation are possible.
 
 The following section assumes a JSON mapper.
 
-The backends themselves do not know "header" and "content" - they know only
-blobs which live in pathes.
-Indeed, this "backend" code does not serve "content" or "header" to the `Store`
-implementation, but only a blob of bytes.
-Anyways, the JSON-protocol for passing a store around _does_ know about content
-and header (see @sec:thestore:backends:stdio:json for the JSON format).
-
-So the mapper reads the JSON, parses it (thanks serde!) and translates it to
-TOML, because TOML is the Store representation of a header.
-But because the backend does not serve header and content, but only a blob,
-this TOML is then translated (with the content of the respective file) to a
-blob.
+The mapper reads the JSON, parses it (thanks serde!) and translates it to
+a `Entry`, which is the in-memory representation of the files.
+The `Entry` contains a `Header` part and a `Content` part.
 
 This is then made available to the store codebase.
-This is complex and probably slow, we know.
 
 To summarize what we do right now, lets have a look at the awesome ascii-art
 below:
 
 ```
-                            libimag*
-                               |
-                               v
- IO     Mapper        FS     Store      FS         Mapper     IO
-+--+-------------+---------+--------+---------+--------------+--+
-|  |             |         |        |         |              |  |
-    JSON -> TOML -> String -> Entry -> String -> TOML -> JSON
-                              + TOML
-                              + Content
+                    libimag*
+                       |
+                       v
+ IO   Mapper         Store      Mapper  IO
++--+---------+----------------+--------+--+
+|  |         |                |        |  |
+    JSON     ->    Entry     ->  JSON
+                   + Header
+                   + Content
 ```
 
 This is what gets translated where for one imag call with a stdio store backend.
 
-The rationale behind this implementation is that this is the best implementation
-we can have in a relatively short amount of time.
-
 ### The JSON Mapper {#sec:thestore:backends:stdio:json}
 
 The JSON mapper maps JSON which is read from a source into a HashMap which
@@ -278,7 +259,7 @@ The strucure is as follows:
 {
     "version": "0.3.0",
     "store": {
-        "/example": {
+        "example": {
             "header": {
                 "imag": {
                     "version": "0.3.0",
@@ -292,24 +273,14 @@ The strucure is as follows:
 
 ### TODO {#sec:thestore:backends:todo}
 
-Of course, the above is not optimal.
-The TODO here is almost visible: Implement a proper backend where we do not need
-to translate between types all the time.
-
-The first goal would be to reduce the above figure to:
-
-```
-           libimag*
-              |
-              v
- IO Mapper  Store    Mapper IO
-+--+------+--------+-------+--+
-|  |      |        |       |  |
-    JSON -> Entry -> JSON
-            + TOML
-            + Content
-```
+If you look at the version history of this file you will see that this
+implementation has grown from something complex and probably slow to what we
+have today.
 
-and the second step would be to abstract all the things away so the `libimag*`
-crates handle the header without knowing whether it is JSON or TOML.
+Still, there's one improvement we could make: abstract all the things away so
+the `libimag*` crates handle the header without knowing whether it is JSON or
+TOML.
+With this, we would not even have to translate JSON to TOML anymore.
+We should measure whether this would have actually any performance impact before
+implementing it.
 

+ 25 - 0
libimagrt/src/runtime.rs

@@ -343,6 +343,31 @@ impl<'a> Runtime<'a> {
         &self.store
     }
 
+    /// Change the store backend to stdout
+    ///
+    /// For the documentation on purpose and cavecats, have a look at the documentation of the
+    /// `Store::reset_backend()` function.
+    ///
+    pub fn store_backend_to_stdio(&mut self) -> Result<(), RuntimeError> {
+        use libimagstore::file_abstraction::stdio::*;
+        use libimagstore::file_abstraction::stdio::mapper::json::JsonMapper;
+        use std::rc::Rc;
+        use std::cell::RefCell;
+
+        let mut input = ::std::io::empty();
+        let output    = ::std::io::stdout();
+        let output    = Rc::new(RefCell::new(output));
+        let mapper    = JsonMapper::new();
+
+        StdIoFileAbstraction::new(&mut input, output, mapper)
+            .map_err_into(RuntimeErrorKind::Instantiate)
+            .and_then(|backend| {
+                self.store
+                    .reset_backend(Box::new(backend))
+                    .map_err_into(RuntimeErrorKind::Instantiate)
+            })
+    }
+
     /// Get a editor command object which can be called to open the $EDITOR
     pub fn editor(&self) -> Option<Command> {
         self.cli()

+ 26 - 4
libimagstore/src/file_abstraction/fs.rs

@@ -25,6 +25,9 @@ use error::{MapErrInto, StoreError as SE, StoreErrorKind as SEK};
 
 use super::FileAbstraction;
 use super::FileAbstractionInstance;
+use super::Drain;
+use store::Entry;
+use storeid::StoreId;
 
 #[derive(Debug)]
 pub enum FSFileAbstractionInstance {
@@ -37,7 +40,7 @@ impl FileAbstractionInstance for FSFileAbstractionInstance {
     /**
      * Get the content behind this file
      */
-    fn get_file_content(&mut self) -> Result<String, SE> {
+    fn get_file_content(&mut self, id: StoreId) -> Result<Entry, SE> {
         debug!("Getting lazy file: {:?}", self);
         let (file, path) = match *self {
             FSFileAbstractionInstance::File(ref mut f, _) => return {
@@ -50,6 +53,7 @@ impl FileAbstractionInstance for FSFileAbstractionInstance {
                 f.read_to_string(&mut s)
                     .map_err_into(SEK::IoError)
                     .map(|_| s)
+                    .and_then(|s| Entry::from_str(id, &s))
             },
             FSFileAbstractionInstance::Absent(ref p) =>
                 (try!(open_file(p).map_err_into(SEK::FileNotFound)), p.clone()),
@@ -60,6 +64,7 @@ impl FileAbstractionInstance for FSFileAbstractionInstance {
             f.read_to_string(&mut s)
                 .map_err_into(SEK::IoError)
                 .map(|_| s)
+                .and_then(|s| Entry::from_str(id, &s))
         } else {
             unreachable!()
         }
@@ -68,22 +73,25 @@ impl FileAbstractionInstance for FSFileAbstractionInstance {
     /**
      * Write the content of this file
      */
-    fn write_file_content(&mut self, buf: &[u8]) -> Result<(), SE> {
+    fn write_file_content(&mut self, buf: &Entry) -> Result<(), SE> {
         use std::io::Write;
+
+        let buf = buf.to_str().into_bytes();
+
         let (file, path) = match *self {
             FSFileAbstractionInstance::File(ref mut f, _) => return {
                 // We seek to the beginning of the file since we expect each
                 // access to the file to be in a different context
                 try!(f.seek(SeekFrom::Start(0))
                     .map_err_into(SEK::FileNotCreated));
-                f.write_all(buf).map_err_into(SEK::FileNotWritten)
+                f.write_all(&buf).map_err_into(SEK::FileNotWritten)
             },
             FSFileAbstractionInstance::Absent(ref p) =>
                 (try!(create_file(p).map_err_into(SEK::FileNotCreated)), p.clone()),
         };
         *self = FSFileAbstractionInstance::File(file, path);
         if let FSFileAbstractionInstance::File(ref mut f, _) = *self {
-            return f.write_all(buf).map_err_into(SEK::FileNotWritten);
+            return f.write_all(&buf).map_err_into(SEK::FileNotWritten);
         }
         unreachable!();
     }
@@ -124,6 +132,20 @@ impl FileAbstraction for FSFileAbstraction {
     fn new_instance(&self, p: PathBuf) -> Box<FileAbstractionInstance> {
         Box::new(FSFileAbstractionInstance::Absent(p))
     }
+
+    /// We return nothing from the FS here.
+    fn drain(&self) -> Result<Drain, SE> {
+        Ok(Drain::empty())
+    }
+
+    /// FileAbstraction::fill implementation that consumes the Drain and writes everything to the
+    /// filesystem
+    fn fill(&mut self, mut d: Drain) -> Result<(), SE> {
+        d.iter()
+            .fold(Ok(()), |acc, (path, element)| {
+                acc.and_then(|_| self.new_instance(path).write_file_content(&element))
+            })
+    }
 }
 
 fn open_file<A: AsRef<Path>>(p: A) -> ::std::io::Result<File> {

+ 34 - 23
libimagstore/src/file_abstraction/inmemory.rs

@@ -19,21 +19,22 @@
 
 use error::StoreError as SE;
 use error::StoreErrorKind as SEK;
-use std::io::Read;
-use std::io::Cursor;
 use std::path::PathBuf;
 use std::collections::HashMap;
 use std::sync::Mutex;
 use std::cell::RefCell;
 use std::sync::Arc;
+use std::ops::Deref;
 
 use libimagerror::into::IntoError;
 
 use super::FileAbstraction;
 use super::FileAbstractionInstance;
-use error::MapErrInto;
+use super::Drain;
+use store::Entry;
+use storeid::StoreId;
 
-type Backend = Arc<Mutex<RefCell<HashMap<PathBuf, Cursor<Vec<u8>>>>>>;
+type Backend = Arc<Mutex<RefCell<HashMap<PathBuf, Entry>>>>;
 
 /// `FileAbstraction` type, this is the Test version!
 ///
@@ -60,41 +61,27 @@ impl FileAbstractionInstance for InMemoryFileAbstractionInstance {
     /**
      * Get the mutable file behind a InMemoryFileAbstraction object
      */
-    fn get_file_content(&mut self) -> Result<String, SE> {
+    fn get_file_content(&mut self, _: StoreId) -> Result<Entry, SE> {
         debug!("Getting lazy file: {:?}", self);
 
-        let p = self.absent_path.clone();
         match self.fs_abstraction.lock() {
             Ok(mut mtx) => {
                 mtx.get_mut()
-                    .get_mut(&p)
+                    .get(&self.absent_path)
+                    .cloned()
                     .ok_or(SEK::FileNotFound.into_error())
-                    .and_then(|t| {
-                        let mut s = String::new();
-                        t.read_to_string(&mut s)
-                            .map_err_into(SEK::IoError)
-                            .map(|_| s)
-                    })
             }
 
             Err(_) => Err(SEK::LockError.into_error())
         }
     }
 
-    fn write_file_content(&mut self, buf: &[u8]) -> Result<(), SE> {
+    fn write_file_content(&mut self, buf: &Entry) -> Result<(), SE> {
         match *self {
             InMemoryFileAbstractionInstance { ref absent_path, .. } => {
                 let mut mtx = self.fs_abstraction.lock().expect("Locking Mutex failed");
                 let mut backend = mtx.get_mut();
-
-                if let Some(ref mut cur) = backend.get_mut(absent_path) {
-                    let mut vec = cur.get_mut();
-                    vec.clear();
-                    vec.extend_from_slice(buf);
-                    return Ok(());
-                }
-                let vec = Vec::from(buf);
-                backend.insert(absent_path.clone(), Cursor::new(vec));
+                let _ = backend.insert(absent_path.clone(), buf.clone());
                 return Ok(());
             },
         };
@@ -118,6 +105,13 @@ impl InMemoryFileAbstraction {
         &self.virtual_filesystem
     }
 
+    fn backend_cloned<'a>(&'a self) -> Result<HashMap<PathBuf, Entry>, SE> {
+        self.virtual_filesystem
+            .lock()
+            .map_err(|_| SEK::LockError.into_error())
+            .map(|mtx| mtx.deref().borrow().clone())
+    }
+
 }
 
 impl FileAbstraction for InMemoryFileAbstraction {
@@ -162,5 +156,22 @@ impl FileAbstraction for InMemoryFileAbstraction {
     fn new_instance(&self, p: PathBuf) -> Box<FileAbstractionInstance> {
         Box::new(InMemoryFileAbstractionInstance::new(self.backend().clone(), p))
     }
+
+    fn drain(&self) -> Result<Drain, SE> {
+        self.backend_cloned().map(Drain::new)
+    }
+
+    fn fill<'a>(&'a mut self, mut d: Drain) -> Result<(), SE> {
+        debug!("Draining into : {:?}", self);
+        let mut mtx = try!(self.backend().lock().map_err(|_| SEK::LockError.into_error()));
+        let mut backend = mtx.get_mut();
+
+        for (path, element) in d.iter() {
+            debug!("Drain into {:?}: {:?}", self, path);
+            backend.insert(path, element);
+        }
+
+        Ok(())
+    }
 }
 

+ 58 - 9
libimagstore/src/file_abstraction/mod.rs

@@ -19,9 +19,11 @@
 
 use std::path::PathBuf;
 use std::fmt::Debug;
+use std::collections::HashMap;
 
 use error::StoreError as SE;
-
+use store::Entry;
+use storeid::StoreId;
 
 mod fs;
 mod inmemory;
@@ -40,31 +42,78 @@ pub trait FileAbstraction : Debug {
     fn create_dir_all(&self, _: &PathBuf) -> Result<(), SE>;
 
     fn new_instance(&self, p: PathBuf) -> Box<FileAbstractionInstance>;
+
+    fn drain(&self) -> Result<Drain, SE>;
+    fn fill<'a>(&'a mut self, d: Drain) -> Result<(), SE>;
 }
 
 /// An abstraction trait over actions on files
 pub trait FileAbstractionInstance : Debug {
-    fn get_file_content(&mut self) -> Result<String, SE>;
-    fn write_file_content(&mut self, buf: &[u8]) -> Result<(), SE>;
+
+    /// Get the contents of the FileAbstractionInstance, as Entry object.
+    ///
+    /// The `StoreId` is passed because the backend does not know where the Entry lives, but the
+    /// Entry type itself must be constructed with the id.
+    fn get_file_content(&mut self, id: StoreId) -> Result<Entry, SE>;
+    fn write_file_content(&mut self, buf: &Entry) -> Result<(), SE>;
+}
+
+pub struct Drain(HashMap<PathBuf, Entry>);
+
+impl Drain {
+
+    pub fn new(hm: HashMap<PathBuf, Entry>) -> Drain {
+        Drain(hm)
+    }
+
+    pub fn empty() -> Drain {
+        Drain::new(HashMap::new())
+    }
+
+    pub fn iter<'a>(&'a mut self) -> DrainIter<'a> {
+        DrainIter(self.0.drain())
+    }
+
+}
+
+pub struct DrainIter<'a>(::std::collections::hash_map::Drain<'a, PathBuf, Entry>);
+
+impl<'a> Iterator for DrainIter<'a> {
+    type Item = (PathBuf, Entry);
+
+    fn next(&mut self) -> Option<Self::Item> {
+        self.0.next()
+    }
 }
 
 #[cfg(test)]
 mod test {
+    use std::path::PathBuf;
+
     use super::FileAbstractionInstance;
     use super::inmemory::InMemoryFileAbstraction;
     use super::inmemory::InMemoryFileAbstractionInstance;
-    use std::path::PathBuf;
+    use storeid::StoreId;
+    use store::Entry;
 
     #[test]
     fn lazy_file() {
         let fs = InMemoryFileAbstraction::new();
 
-        let mut path = PathBuf::from("/tests");
+        let mut path = PathBuf::from("tests");
         path.set_file_name("test1");
-        let mut lf = InMemoryFileAbstractionInstance::new(fs.backend().clone(), path);
-        lf.write_file_content(b"Hello World").unwrap();
-        let bah = lf.get_file_content().unwrap();
-        assert_eq!(bah, "Hello World");
+        let mut lf = InMemoryFileAbstractionInstance::new(fs.backend().clone(), path.clone());
+
+        let loca = StoreId::new_baseless(path).unwrap();
+        let file = Entry::from_str(loca.clone(), r#"---
+[imag]
+version = "0.3.0"
+---
+Hello World"#).unwrap();
+
+        lf.write_file_content(&file).unwrap();
+        let bah = lf.get_file_content(loca).unwrap();
+        assert_eq!(bah.get_content(), "Hello World");
     }
 
 }

+ 37 - 34
libimagstore/src/file_abstraction/stdio/mapper/json.rs

@@ -18,7 +18,6 @@
 //
 
 use std::collections::HashMap;
-use std::io::Cursor;
 use std::io::{Read, Write};
 use std::path::PathBuf;
 
@@ -29,16 +28,18 @@ use error::StoreErrorKind as SEK;
 use error::MapErrInto;
 use super::Mapper;
 use store::Result;
+use store::Entry;
+use storeid::StoreId;
 
 use libimagerror::into::IntoError;
 
 #[derive(Deserialize, Serialize)]
-struct Entry {
+struct BackendEntry {
     header: serde_json::Value,
     content: String,
 }
 
-impl Entry {
+impl BackendEntry {
 
     fn to_string(self) -> Result<String> {
         toml::to_string(&self.header)
@@ -55,7 +56,7 @@ impl Entry {
 #[derive(Deserialize, Serialize)]
 struct Document {
     version: String,
-    store: HashMap<PathBuf, Entry>,
+    store: HashMap<PathBuf, BackendEntry>,
 }
 
 pub struct JsonMapper;
@@ -69,7 +70,7 @@ impl JsonMapper {
 }
 
 impl Mapper for JsonMapper {
-    fn read_to_fs<R: Read>(&self, r: &mut R, hm: &mut HashMap<PathBuf, Cursor<Vec<u8>>>)   -> Result<()> {
+    fn read_to_fs<R: Read>(&self, r: &mut R, hm: &mut HashMap<PathBuf, Entry>)   -> Result<()> {
         let mut document = {
             let mut s = String::new();
             try!(r.read_to_string(&mut s).map_err_into(SEK::IoError));
@@ -93,7 +94,11 @@ impl Mapper for JsonMapper {
         for (key, val) in document.store.drain() {
             let res = val
                 .to_string()
-                .map(|vals| hm.insert(key, Cursor::new(vals.into_bytes())))
+                .and_then(|vals| {
+                    StoreId::new_baseless(key.clone())
+                        .and_then(|id| Entry::from_str(id, &vals))
+                        .map(|entry| hm.insert(key, entry))
+                })
                 .map(|_| ());
 
             let _ = try!(res);
@@ -102,43 +107,38 @@ impl Mapper for JsonMapper {
         Ok(())
     }
 
-    fn fs_to_write<W: Write>(&self, hm: &mut HashMap<PathBuf, Cursor<Vec<u8>>>, out: &mut W) -> Result<()> {
-        use util::entry_buffer_to_header_content;
-
-        #[derive(Serialize, Deserialize)]
-        struct Entry {
+    fn fs_to_write<W: Write>(&self, hm: &mut HashMap<PathBuf, Entry>, out: &mut W) -> Result<()> {
+        #[derive(Serialize)]
+        struct BackendEntry {
             header: ::toml::Value,
             content: String,
         }
 
+        impl BackendEntry {
+            fn construct_from(e: Entry) -> BackendEntry {
+                BackendEntry {
+                    header:  e.get_header().clone(),
+                    content: e.get_content().clone(),
+                }
+            }
+        }
+
         #[derive(Serialize)]
         struct OutDocument {
             version: String,
-            store: HashMap<PathBuf, Entry>,
+            store: HashMap<PathBuf, BackendEntry>,
         }
 
-        let mut doc = OutDocument {
-            version: String::from(version!()),
-            store:   HashMap::new(),
-        };
-
+        let mut store = HashMap::new();
         for (key, value) in hm.drain() {
-            let res = String::from_utf8(value.into_inner())
-                .map_err_into(SEK::IoError)
-                .and_then(|buf| entry_buffer_to_header_content(&buf))
-                .map(|(header, content)| {
-                    let entry = Entry {
-                        header: header,
-                        content: content
-                    };
-
-                    doc.store.insert(key, entry);
-                })
-                .map(|_| ());
-
-            let _ = try!(res);
+            store.insert(key, BackendEntry::construct_from(value));
         }
 
+        let doc = OutDocument {
+            version: String::from(version!()),
+            store:   store,
+        };
+
         serde_json::to_string(&doc)
             .map_err_into(SEK::IoError)
             .and_then(|json| out.write(&json.into_bytes()).map_err_into(SEK::IoError))
@@ -158,7 +158,7 @@ mod test {
         let json = r#"
         { "version": "0.3.0",
           "store": {
-            "/example": {
+            "example": {
                 "header": {
                     "imag": {
                         "version": "0.3.0"
@@ -191,7 +191,10 @@ mod test {
 version = "0.3.0"
 ---
 hi there!"#;
-            hm.insert(PathBuf::from("/example"), Cursor::new(String::from(content).into_bytes()));
+
+            let id = PathBuf::from("example");
+            let entry = Entry::from_str(id.clone(), content).unwrap();
+            hm.insert(id, entry);
             hm
         };
 
@@ -202,7 +205,7 @@ hi there!"#;
         {
             "version": "0.3.0",
             "store": {
-                "/example": {
+                "example": {
                     "header": {
                         "imag": {
                             "version": "0.3.0"

+ 3 - 3
libimagstore/src/file_abstraction/stdio/mapper/mod.rs

@@ -18,14 +18,14 @@
 //
 
 use std::collections::HashMap;
-use std::io::Cursor;
 use std::io::{Read, Write};
 use std::path::PathBuf;
 use store::Result;
+use store::Entry;
 
 pub trait Mapper {
-    fn read_to_fs<R: Read>(&self, &mut R, &mut HashMap<PathBuf, Cursor<Vec<u8>>>)   -> Result<()>;
-    fn fs_to_write<W: Write>(&self, &mut HashMap<PathBuf, Cursor<Vec<u8>>>, &mut W) -> Result<()>;
+    fn read_to_fs<R: Read>(&self, &mut R, &mut HashMap<PathBuf, Entry>)   -> Result<()>;
+    fn fs_to_write<W: Write>(&self, &mut HashMap<PathBuf, Entry>, &mut W) -> Result<()>;
 }
 
 pub mod json;

+ 49 - 57
libimagstore/src/file_abstraction/stdio/mod.rs

@@ -20,44 +20,34 @@
 use std::rc::Rc;
 use std::cell::RefCell;
 use std::collections::HashMap;
-use std::fmt::Debug;
-use std::fmt::Error as FmtError;
-use std::fmt::Formatter;
-use std::io::Cursor;
 use std::io::{Read, Write};
 use std::path::PathBuf;
 use std::sync::Arc;
 use std::sync::Mutex;
+use std::ops::Deref;
+use std::fmt::Debug;
+use std::fmt::Error as FmtError;
+use std::fmt::Formatter;
 
 use libimagerror::into::IntoError;
-use libimagerror::trace::*;
 
 use error::StoreErrorKind as SEK;
 use error::StoreError as SE;
 use super::FileAbstraction;
 use super::FileAbstractionInstance;
+use super::Drain;
 use super::InMemoryFileAbstraction;
+use store::Entry;
 
 pub mod mapper;
+pub mod out;
 use self::mapper::Mapper;
+use self::out::StdoutFileAbstraction;
 
 // Because this is not exported in super::inmemory;
-type Backend = Arc<Mutex<RefCell<HashMap<PathBuf, Cursor<Vec<u8>>>>>>;
-
-pub struct StdIoFileAbstraction<W: Write, M: Mapper> {
-    mapper: M,
-    mem: InMemoryFileAbstraction,
-    out: Rc<RefCell<W>>,
-}
+type Backend = Arc<Mutex<RefCell<HashMap<PathBuf, Entry>>>>;
 
-impl<W, M> Debug for StdIoFileAbstraction<W, M>
-    where M: Mapper,
-          W: Write
-{
-    fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> {
-        write!(f, "StdIoFileAbstraction({:?}", self.mem)
-    }
-}
+pub struct StdIoFileAbstraction<W: Write, M: Mapper>(StdoutFileAbstraction<W, M>);
 
 impl<W, M> StdIoFileAbstraction<W, M>
     where M: Mapper,
@@ -65,71 +55,73 @@ impl<W, M> StdIoFileAbstraction<W, M>
 {
 
     pub fn new<R: Read>(in_stream: &mut R, out_stream: Rc<RefCell<W>>, mapper: M) -> Result<StdIoFileAbstraction<W, M>, SE> {
-        let mem = InMemoryFileAbstraction::new();
-
-        {
-            let fill_res = match mem.backend().lock() {
-                Err(_) => Err(SEK::LockError.into_error()),
-                Ok(mut mtx) => mapper.read_to_fs(in_stream, mtx.get_mut())
-            };
-            let _ = try!(fill_res);
-        }
-
-        Ok(StdIoFileAbstraction {
-            mapper: mapper,
-            mem:    mem,
-            out:    out_stream,
-        })
+        StdoutFileAbstraction::new(out_stream, mapper)
+            .and_then(|out| {
+                let fill_res = match out.backend().lock() {
+                    Err(_) => Err(SEK::LockError.into_error()),
+                    Ok(mut mtx) => out.mapper().read_to_fs(in_stream, mtx.get_mut())
+                };
+                let _ = try!(fill_res);
+
+                Ok(StdIoFileAbstraction(out))
+            })
     }
 
     pub fn backend(&self) -> &Backend {
-        &self.mem.backend()
+        self.0.backend()
     }
 
 }
 
-impl<W, M> Drop for StdIoFileAbstraction<W, M>
+impl<W, M> Debug for StdIoFileAbstraction<W, M>
+    where M: Mapper,
+          W: Write
+{
+    fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> {
+        write!(f, "StdIoFileAbstraction({:?}", self.0)
+    }
+}
+
+impl<W, M> Deref for StdIoFileAbstraction<W, M>
     where M: Mapper,
           W: Write
 {
-    fn drop(&mut self) {
-        use std::ops::DerefMut;
-
-        let fill_res = match self.mem.backend().lock() {
-            Err(_) => Err(SEK::LockError.into_error()),
-            Ok(mut mtx) => {
-                self.mapper.fs_to_write(mtx.get_mut(), self.out.borrow_mut().deref_mut())
-            },
-        };
-
-        // We can do nothing but end this here with a trace.
-        // As this drop gets called when imag almost exits, there is no point in exit()ing here
-        // again.
-        let _ = fill_res.map_err_trace();
+    type Target = StdoutFileAbstraction<W, M>;
+
+    fn deref(&self) -> &Self::Target {
+        &self.0
     }
 }
 
+// basically #[derive(FileAbstraction)]
 impl<W: Write, M: Mapper> FileAbstraction for StdIoFileAbstraction<W, M> {
 
     fn remove_file(&self, path: &PathBuf) -> Result<(), SE> {
-        self.mem.remove_file(path)
+        self.0.remove_file(path)
     }
 
     fn copy(&self, from: &PathBuf, to: &PathBuf) -> Result<(), SE> {
-        self.mem.copy(from, to)
+        self.0.copy(from, to)
     }
 
     fn rename(&self, from: &PathBuf, to: &PathBuf) -> Result<(), SE> {
-        self.mem.rename(from, to)
+        self.0.rename(from, to)
     }
 
     fn create_dir_all(&self, pb: &PathBuf) -> Result<(), SE> {
-        self.mem.create_dir_all(pb)
+        self.0.create_dir_all(pb)
     }
 
     fn new_instance(&self, p: PathBuf) -> Box<FileAbstractionInstance> {
-        self.mem.new_instance(p)
+        self.0.new_instance(p)
     }
-}
 
+    fn drain(&self) -> Result<Drain, SE> {
+        self.0.drain()
+    }
+
+    fn fill(&mut self, d: Drain) -> Result<(), SE> {
+        self.0.fill(d)
+    }
+}
 

+ 157 - 0
libimagstore/src/file_abstraction/stdio/out.rs

@@ -0,0 +1,157 @@
+//
+// imag - the personal information management suite for the commandline
+// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; version
+// 2.1 of the License.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+//
+
+//! A StdIoFileAbstraction which does not read from stdin.
+
+use std::rc::Rc;
+use std::cell::RefCell;
+use std::collections::HashMap;
+use std::fmt::Debug;
+use std::fmt::Error as FmtError;
+use std::fmt::Formatter;
+use std::io::Write;
+use std::path::PathBuf;
+use std::sync::Arc;
+use std::sync::Mutex;
+use std::ops::Deref;
+
+use libimagerror::into::IntoError;
+use libimagerror::trace::*;
+
+use error::StoreErrorKind as SEK;
+use error::StoreError as SE;
+use super::FileAbstraction;
+use super::FileAbstractionInstance;
+use super::Drain;
+use super::InMemoryFileAbstraction;
+use store::Entry;
+
+use super::mapper::Mapper;
+
+// Because this is not exported in super::inmemory;
+type Backend = Arc<Mutex<RefCell<HashMap<PathBuf, Entry>>>>;
+
+pub struct StdoutFileAbstraction<W: Write, M: Mapper> {
+    mapper: M,
+    mem: InMemoryFileAbstraction,
+    out: Rc<RefCell<W>>,
+}
+
+impl<W, M> StdoutFileAbstraction<W, M>
+    where M: Mapper,
+          W: Write
+{
+
+    pub fn new(out_stream: Rc<RefCell<W>>, mapper: M) -> Result<StdoutFileAbstraction<W, M>, SE> {
+        Ok(StdoutFileAbstraction {
+            mapper: mapper,
+            mem:    InMemoryFileAbstraction::new(),
+            out:    out_stream,
+        })
+    }
+
+    pub fn backend(&self) -> &Backend {
+        self.mem.backend()
+    }
+
+    fn backend_cloned(&self) -> Result<HashMap<PathBuf, Entry>, SE> {
+        self.mem
+            .backend()
+            .lock()
+            .map_err(|_| SEK::LockError.into_error())
+            .map(|mtx| mtx.deref().borrow().clone())
+    }
+
+    pub fn mapper(&self) -> &M {
+        &self.mapper
+    }
+
+}
+
+impl<W, M> Debug for StdoutFileAbstraction<W, M>
+    where M: Mapper,
+          W: Write
+{
+    fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> {
+        write!(f, "StdoutFileAbstraction({:?}", self.mem)
+    }
+}
+
+impl<W, M> Drop for StdoutFileAbstraction<W, M>
+    where M: Mapper,
+          W: Write
+{
+    fn drop(&mut self) {
+        use std::ops::DerefMut;
+
+        let fill_res = match self.mem.backend().lock() {
+            Err(_) => Err(SEK::LockError.into_error()),
+            Ok(mut mtx) => {
+                self.mapper.fs_to_write(mtx.get_mut(), self.out.borrow_mut().deref_mut())
+            },
+        };
+
+        // We can do nothing but end this here with a trace.
+        // As this drop gets called when imag almost exits, there is no point in exit()ing here
+        // again.
+        let _ = fill_res.map_err_trace();
+    }
+}
+
+impl<W: Write, M: Mapper> FileAbstraction for StdoutFileAbstraction<W, M> {
+
+    fn remove_file(&self, path: &PathBuf) -> Result<(), SE> {
+        self.mem.remove_file(path)
+    }
+
+    fn copy(&self, from: &PathBuf, to: &PathBuf) -> Result<(), SE> {
+        self.mem.copy(from, to)
+    }
+
+    fn rename(&self, from: &PathBuf, to: &PathBuf) -> Result<(), SE> {
+        self.mem.rename(from, to)
+    }
+
+    fn create_dir_all(&self, pb: &PathBuf) -> Result<(), SE> {
+        self.mem.create_dir_all(pb)
+    }
+
+    fn new_instance(&self, p: PathBuf) -> Box<FileAbstractionInstance> {
+        self.mem.new_instance(p)
+    }
+
+    fn drain(&self) -> Result<Drain, SE> {
+        self.backend_cloned().map(Drain::new)
+    }
+
+    fn fill(&mut self, mut d: Drain) -> Result<(), SE> {
+        debug!("Draining into : {:?}", self);
+        let mut mtx = try!(self.backend().lock().map_err(|_| SEK::IoError.into_error()));
+        let mut backend = mtx.get_mut();
+
+        for (path, element) in d.iter() {
+            debug!("Drain into {:?}: {:?}", self, path);
+            backend.insert(path, element);
+        }
+        Ok(())
+    }
+
+}
+
+

+ 0 - 0
libimagstore/src/store.rs


Some files were not shown because too many files changed in this diff