Merge pull request #1444 from matthiasbeyer/libimagstore/remove-backend-mapper

libimagstore: remove backend mapper
This commit is contained in:
Matthias Beyer 2018-04-24 23:08:30 +02:00 committed by GitHub
commit 2ac4fa42b3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 4 additions and 952 deletions

View File

@ -1,39 +0,0 @@
//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015-2018 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
//
use std::process::exit;
use libimagrt::runtime::Runtime;
use libimagerror::trace::*;
pub fn dump(rt: &mut Runtime) {
rt.store()
.entries()
.map_err_trace_exit_unwrap(1)
.for_each(|elem| {
debug!("Working on {:?}", elem);
rt.store().get(elem).map_err_trace_exit_unwrap(1);
});
if let Err(_) = rt.store_backend_to_stdout().map_err_trace() {
error!("Loading Store IO backend failed");
exit(1);
}
}

View File

@ -54,7 +54,6 @@ use libimagerror::trace::MapErrTrace;
mod create;
mod delete;
mod dump;
mod error;
mod get;
mod retrieve;
@ -67,7 +66,6 @@ use std::ops::Deref;
use create::create;
use delete::delete;
use dump::dump;
use get::get;
use retrieve::retrieve;
use ui::build_ui;
@ -76,10 +74,10 @@ use verify::verify;
fn main() {
let version = make_imag_version!();
let mut rt = generate_runtime_setup("imag-store",
&version,
"Direct interface to the store. Use with great care!",
build_ui);
let rt = generate_runtime_setup("imag-store",
&version,
"Direct interface to the store. Use with great care!",
build_ui);
let command = rt.cli().subcommand_name().map(String::from);
@ -92,7 +90,6 @@ fn main() {
"retrieve" => retrieve(&rt),
"update" => update(&rt),
"verify" => verify(&rt),
"dump" => dump(&mut rt),
other => {
debug!("Unknown command");
let _ = rt.handle_unknown_subcommand("imag-store", other, rt.cli())

View File

@ -190,9 +190,4 @@ pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> {
.about("Verify the store")
.version("0.1")
)
.subcommand(SubCommand::with_name("dump")
.about("Dump the complete store to stdout. Currently does only support JSON")
.version("0.1")
)
}

View File

@ -178,80 +178,3 @@ The `InMemoryFileAbstractionInstance` implementation is corrosponding to
the `InMemoryFileAbstraction` implementation - for the in-memory
"filesystem".
## The StdIo backend {#sec:thestore:backends:stdio}
Sidenote: The name is "StdIo" because its main purpose is Stdin/Stdio, but it
is abstracted over Read/Write actually, so it is also possible to use this
backend in other ways, too.
### Why? {#sec:thestore:backends:stdio:why}
This is a backend for the imag store which is created
from stdin, by piping contents into the store (via JSON or TOML) and piping the
store contents (as JSON or TOML) to stdout when the backend is destructed.
This is one of some components which make command-chaining in imag possible.
With this, the application does not have to know whether the store actually
lives on the filesystem or just "in memory".
### Mappers {#sec:thestore:backends:stdio:mappers}
The backend contains a "Mapper" which defines how the contents get mapped into
the in-memory store representation: A JSON implementation or a TOML
implementation are possible.
The following section assumes a JSON mapper.
The mapper reads the JSON, parses it and translates it to a `Entry`.
Then, the entry is made available to the store codebase.
To summarize what we do right now, lets have a look at the awesome ascii-art
below:
```
libimag*
|
v
IO Mapper Store Mapper IO
+--+---------+----------------+--------+--+
| | | | | |
JSON -> Entry -> JSON
```
This is what gets translated where for one imag call with a stdio store backend.
### The JSON Mapper {#sec:thestore:backends:stdio:json}
The JSON mapper maps JSON which is read from a source into a HashMap which
represents the in-memory filesystem.
The strucure is as follows:
```json
{
"version": "0.8.0",
"store": {
"example": {
"header": {
"imag": {
"version": "0.8.0",
},
},
"content": "hi there!",
},
},
}
```
### TODO {#sec:thestore:backends:todo}
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.
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.

View File

@ -368,50 +368,6 @@ 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::stdin();
let output = ::std::io::stdout();
let output = Rc::new(RefCell::new(output));
let mapper = JsonMapper::new();
StdIoFileAbstraction::new(&mut input, output, mapper)
.chain_err(|| RuntimeErrorKind::Instantiate)
.and_then(|backend| {
self.store
.reset_backend(Box::new(backend))
.chain_err(|| RuntimeErrorKind::Instantiate)
})
}
pub fn store_backend_to_stdout(&mut self) -> Result<(), RuntimeError> {
use libimagstore::file_abstraction::stdio::mapper::json::JsonMapper;
use libimagstore::file_abstraction::stdio::out::StdoutFileAbstraction;
use std::rc::Rc;
use std::cell::RefCell;
let output = ::std::io::stdout();
let output = Rc::new(RefCell::new(output));
let mapper = JsonMapper::new();
StdoutFileAbstraction::new(output, mapper)
.chain_err(|| RuntimeErrorKind::Instantiate)
.and_then(|backend| {
self.store
.reset_backend(Box::new(backend))
.chain_err(|| RuntimeErrorKind::Instantiate)
})
}
/// Get a editor command object which can be called to open the $EDITOR
pub fn editor(&self) -> Result<Option<Command>, RuntimeError> {
self.cli()

View File

@ -29,7 +29,6 @@ walkdir = "1"
is-match = "0.1"
serde = "1"
serde_json = "1"
serde_derive = "1"
error-chain = "0.11"
toml-query = "0.6"

View File

@ -28,7 +28,6 @@ use storeid::StoreId;
mod fs;
mod inmemory;
mod iter;
pub mod stdio;
pub use self::fs::FSFileAbstraction;
pub use self::fs::FSFileAbstractionInstance;

View File

@ -1,250 +0,0 @@
//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015-2018 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
//
use std::collections::HashMap;
use std::io::{Read, Write};
use std::path::PathBuf;
use serde_json;
use toml;
use error::StoreErrorKind as SEK;
use error::StoreError as SE;
use error::ResultExt;
use super::Mapper;
use store::Result;
use store::Entry;
use storeid::StoreId;
#[derive(Debug, Deserialize, Serialize)]
struct BackendEntry {
header: serde_json::Value,
content: String,
}
impl BackendEntry {
fn to_string(self) -> Result<String> {
toml::to_string(&self.header)
.chain_err(|| SEK::IoError)
.map(|hdr| {
format!("---\n{header}---\n{content}",
header = hdr,
content = self.content)
})
}
}
#[derive(Debug, Deserialize, Serialize)]
struct Document {
version: String,
store: HashMap<PathBuf, BackendEntry>,
}
pub struct JsonMapper;
impl JsonMapper {
pub fn new() -> JsonMapper {
JsonMapper
}
}
impl Mapper for JsonMapper {
fn read_to_fs<R: Read>(&self, r: &mut R, hm: &mut HashMap<PathBuf, Entry>) -> Result<()> {
let mut document = {
debug!("Reading Document");
let mut s = String::new();
r.read_to_string(&mut s).chain_err(|| SEK::IoError)?;
debug!("Document = {:?}", s);
debug!("Parsing Document");
let doc : Document = serde_json::from_str(&s).chain_err(|| SEK::IoError)?;
debug!("Document = {:?}", doc);
doc
};
let _ = ::semver::Version::parse(&document.version)
.chain_err(|| SEK::VersionError)
.and_then(|doc_vers| {
// safe because cargo does not compile if crate version is not valid
let crate_version = ::semver::Version::parse(env!("CARGO_PKG_VERSION")).unwrap();
debug!("Document version vs. own version: {doc_vers} > {crate_vers}",
doc_vers = doc_vers,
crate_vers = crate_version);
if doc_vers > crate_version {
Err(SE::from_kind(SEK::VersionError))
} else {
Ok(())
}
})?;
for (key, val) in document.store.drain() {
debug!("(key, value) ({:?}, {:?})", key, val);
let res = val
.to_string()
.and_then(|vals| {
debug!("value string = {:?}", vals);
StoreId::new_baseless(key.clone())
.and_then(|id| Entry::from_str(id, &vals))
.map(|entry| hm.insert(key, entry))
})
.map(|_| ());
let _ = res?;
}
Ok(())
}
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, BackendEntry>,
}
let mut store = HashMap::new();
for (key, value) in hm.drain() {
store.insert(key, BackendEntry::construct_from(value));
}
let doc = OutDocument {
version: String::from(env!("CARGO_PKG_VERSION")),
store: store,
};
serde_json::to_string(&doc)
.chain_err(|| SEK::IoError)
.and_then(|json| out.write(&json.into_bytes()).chain_err(|| SEK::IoError))
.and_then(|_| out.flush().chain_err(|| SEK::IoError))
.map(|_| ())
}
}
#[cfg(test)]
mod test {
use std::io::Cursor;
use super::*;
#[test]
fn test_empty_json_to_fs() {
let json = format!(r#"{{"version":"{version}","store":{{}}}}"#,
version = env!("CARGO_PKG_VERSION"));
let mut json = Cursor::new(String::from(json).into_bytes());
let mapper = JsonMapper::new();
let mut hm = HashMap::new();
let io_res = mapper.read_to_fs(&mut json, &mut hm);
assert!(io_res.is_ok());
assert!(hm.is_empty());
}
#[test]
fn test_json_to_fs() {
let json = format!(r#"
{{ "version": "{version}",
"store": {{
"example": {{
"header": {{
"imag": {{
"version": "{version}"
}}
}},
"content": "test"
}}
}}
}}
"#, version = env!("CARGO_PKG_VERSION"));
let mut json = Cursor::new(String::from(json).into_bytes());
let mapper = JsonMapper::new();
let mut hm = HashMap::new();
let io_res = mapper.read_to_fs(&mut json, &mut hm);
assert!(io_res.is_ok());
assert_eq!(1, hm.len()); // we should have exactly one entry
}
#[test]
fn test_fs_to_json() {
let mapper = JsonMapper::new();
let mut out : Cursor<Vec<u8>> = Cursor::new(vec![]);
let mut hm = {
let mut hm = HashMap::new();
let content = format!(r#"---
[imag]
version = "{}"
---
hi there!"#, env!("CARGO_PKG_VERSION"));
let id = PathBuf::from("example");
let entry = Entry::from_str(id.clone(), &content).unwrap();
hm.insert(id, entry);
hm
};
let io_res = mapper.fs_to_write(&mut hm, &mut out);
assert!(io_res.is_ok());
let example = format!(r#"
{{
"version": "{version}",
"store": {{
"example": {{
"header": {{
"imag": {{
"version": "{version}"
}}
}},
"content": "hi there!"
}}
}}
}}
"#, version = env!("CARGO_PKG_VERSION"));
let example_json : ::serde_json::Value = ::serde_json::from_str(&example).unwrap();
let output_json = String::from_utf8(out.into_inner()).unwrap();
let output_json : ::serde_json::Value = ::serde_json::from_str(&output_json).unwrap();
assert_eq!(example_json, output_json);
}
}

View File

@ -1,32 +0,0 @@
//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015-2018 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
//
use std::collections::HashMap;
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, Entry>) -> Result<()>;
fn fs_to_write<W: Write>(&self, &mut HashMap<PathBuf, Entry>, &mut W) -> Result<()>;
}
pub mod json;

View File

@ -1,137 +0,0 @@
//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015-2018 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
//
use std::rc::Rc;
use std::cell::RefCell;
use std::collections::HashMap;
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 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 file_abstraction::iter::PathIterator;
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, Entry>>>>;
pub struct StdIoFileAbstraction<W: Write, M: Mapper>(StdoutFileAbstraction<W, M>);
impl<W, M> StdIoFileAbstraction<W, M>
where M: Mapper,
W: Write
{
pub fn new<R: Read>(in_stream: &mut R, out_stream: Rc<RefCell<W>>, mapper: M) -> Result<StdIoFileAbstraction<W, M>, SE> {
StdoutFileAbstraction::new(out_stream, mapper)
.and_then(|out| {
let _ = out.backend()
.lock()
.map_err(|_| SE::from_kind(SEK::LockError))
.map(|mut mtx| out.mapper().read_to_fs(in_stream, mtx.get_mut()))?;
Ok(StdIoFileAbstraction(out))
})
}
pub fn backend(&self) -> &Backend {
self.0.backend()
}
}
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
{
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.0.remove_file(path)
}
fn copy(&self, from: &PathBuf, to: &PathBuf) -> Result<(), SE> {
self.0.copy(from, to)
}
fn rename(&self, from: &PathBuf, to: &PathBuf) -> Result<(), SE> {
self.0.rename(from, to)
}
fn create_dir_all(&self, pb: &PathBuf) -> Result<(), SE> {
self.0.create_dir_all(pb)
}
fn new_instance(&self, p: PathBuf) -> Box<FileAbstractionInstance> {
self.0.new_instance(p)
}
fn exists(&self, p: &PathBuf) -> Result<bool, SE> {
self.0.exists(p)
}
fn is_file(&self, p: &PathBuf) -> Result<bool, SE> {
self.0.is_file(p)
}
fn drain(&self) -> Result<Drain, SE> {
self.0.drain()
}
fn fill(&mut self, d: Drain) -> Result<(), SE> {
self.0.fill(d)
}
fn pathes_recursively(&self, basepath: PathBuf) -> Result<PathIterator, SE> {
self.0.pathes_recursively(basepath)
}
}

View File

@ -1,169 +0,0 @@
//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015-2018 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::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 file_abstraction::iter::PathIterator;
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(|_| SE::from_kind(SEK::LockError))
.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(SE::from_kind(SEK::LockError)),
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 exists(&self, p: &PathBuf) -> Result<bool, SE> {
self.mem.exists(p)
}
fn is_file(&self, p: &PathBuf) -> Result<bool, SE> {
self.mem.is_file(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 = self.backend().lock().map_err(|_| SE::from_kind(SEK::IoError))?;
let backend = mtx.get_mut();
for (path, element) in d.iter() {
debug!("Drain into {:?}: {:?}", self, path);
backend.insert(path, element);
}
Ok(())
}
fn pathes_recursively(&self, basepath: PathBuf) -> Result<PathIterator, SE> {
self.mem.pathes_recursively(basepath)
}
}

View File

@ -44,7 +44,6 @@ extern crate semver;
extern crate walkdir;
#[macro_use] extern crate is_match;
extern crate serde_json;
#[macro_use] extern crate serde_derive;
#[macro_use] extern crate error_chain;
extern crate toml_query;

View File

@ -1208,96 +1208,6 @@ mod store_tests {
}
}
#[test]
fn test_store_create_with_io_backend() {
use std::io::Cursor;
use std::rc::Rc;
use std::cell::RefCell;
use serde_json::Value;
//let sink = vec![];
//let output : Cursor<&mut [u8]> = Cursor::new(&mut sink);
//let output = Rc::new(RefCell::new(output));
let output = Rc::new(RefCell::new(vec![]));
{
let store = {
use file_abstraction::stdio::StdIoFileAbstraction;
use file_abstraction::stdio::mapper::json::JsonMapper;
// Lets have an empty store as input
let mut input = Cursor::new(format!(r#"
{{ "version": "{}",
"store": {{}}
}}
"#, env!("CARGO_PKG_VERSION")));
let mapper = JsonMapper::new();
let backend = StdIoFileAbstraction::new(&mut input, output.clone(), mapper).unwrap();
let backend = Box::new(backend);
Store::new_with_backend(PathBuf::from("/"), &None, backend).unwrap()
};
for n in 1..100 {
let s = format!("test-{}", n);
let entry = store.create(PathBuf::from(s.clone())).unwrap();
assert!(entry.verify().is_ok());
let loc = entry.get_location().clone().into_pathbuf().unwrap();
assert!(loc.starts_with("/"));
assert!(loc.ends_with(s));
}
}
let vec = Rc::try_unwrap(output).unwrap().into_inner();
let errstr = format!("Not UTF8: '{:?}'", vec);
let string = String::from_utf8(vec);
assert!(string.is_ok(), errstr);
let string = string.unwrap();
assert!(!string.is_empty(), format!("Expected not to be empty: '{}'", string));
let json : ::serde_json::Value = ::serde_json::from_str(&string).unwrap();
match json {
Value::Object(ref map) => {
assert!(map.get("version").is_some(), format!("No 'version' in JSON"));
match map.get("version").unwrap() {
&Value::String(ref s) => assert_eq!(env!("CARGO_PKG_VERSION"), s),
_ => panic!("Wrong type in JSON at 'version'"),
}
assert!(map.get("store").is_some(), format!("No 'store' in JSON"));
match map.get("store").unwrap() {
&Value::Object(ref objs) => {
for n in 1..100 {
let s = format!("/test-{}", n);
assert!(objs.get(&s).is_some(), format!("No entry: '{}'", s));
match objs.get(&s).unwrap() {
&Value::Object(ref entry) => {
match entry.get("header").unwrap() {
&Value::Object(_) => assert!(true),
_ => panic!("Wrong type in JSON at 'store.'{}'.header'", s),
}
match entry.get("content").unwrap() {
&Value::String(_) => assert!(true),
_ => panic!("Wrong type in JSON at 'store.'{}'.content'", s),
}
},
_ => panic!("Wrong type in JSON at 'store.'{}''", s),
}
}
},
_ => panic!("Wrong type in JSON at 'store'"),
}
},
_ => panic!("Wrong type in JSON at top level"),
}
}
#[test]
fn test_store_get_create_get_delete_get() {
let store = get_store();
@ -1497,104 +1407,5 @@ mod store_tests {
}
}
#[test]
fn test_swap_backend_during_runtime_with_io() {
use std::io::Cursor;
use std::rc::Rc;
use std::cell::RefCell;
use serde_json::Value;
use file_abstraction::stdio::out::StdoutFileAbstraction;
use file_abstraction::stdio::mapper::json::JsonMapper;
// The output we later read from and check whether there is an entry
let output = Rc::new(RefCell::new(vec![]));
{
let mut store = {
use file_abstraction::stdio::StdIoFileAbstraction;
use file_abstraction::stdio::mapper::json::JsonMapper;
// Lets have an empty store as input
let mut input = Cursor::new(format!(r#"
{{ "version": "{version}",
"store": {{
"example": {{
"header": {{
"imag": {{
"version": "{version}"
}}
}},
"content": "foobar"
}}
}}
}}
"#, version = env!("CARGO_PKG_VERSION")));
let output = Rc::new(RefCell::new(::std::io::sink()));
let mapper = JsonMapper::new();
let backend = StdIoFileAbstraction::new(&mut input, output, mapper).unwrap();
let backend = Box::new(backend);
Store::new_with_backend(PathBuf::from("/"), &None, backend).unwrap()
};
// Replacing the backend
{
let mapper = JsonMapper::new();
let backend = StdoutFileAbstraction::new(output.clone(), mapper);
let _ = assert!(backend.is_ok(), format!("Should be ok: {:?}", backend));
let backend = backend.unwrap();
let backend = Box::new(backend);
assert!(store.reset_backend(backend).is_ok());
}
}
let vec = Rc::try_unwrap(output).unwrap().into_inner();
let errstr = format!("Not UTF8: '{:?}'", vec);
let string = String::from_utf8(vec);
assert!(string.is_ok(), errstr);
let string = string.unwrap();
assert!(!string.is_empty(), format!("Expected not to be empty: '{}'", string));
let json : ::serde_json::Value = ::serde_json::from_str(&string).unwrap();
match json {
Value::Object(ref map) => {
assert!(map.get("version").is_some(), format!("No 'version' in JSON"));
match map.get("version").unwrap() {
&Value::String(ref s) => assert_eq!(env!("CARGO_PKG_VERSION"), s),
_ => panic!("Wrong type in JSON at 'version'"),
}
assert!(map.get("store").is_some(), format!("No 'store' in JSON"));
match map.get("store").unwrap() {
&Value::Object(ref objs) => {
let s = String::from("example");
assert!(objs.get(&s).is_some(), format!("No entry: '{}' in \n{:?}", s, objs));
match objs.get(&s).unwrap() {
&Value::Object(ref entry) => {
match entry.get("header").unwrap() {
&Value::Object(_) => assert!(true),
_ => panic!("Wrong type in JSON at 'store.'{}'.header'", s),
}
match entry.get("content").unwrap() {
&Value::String(_) => assert!(true),
_ => panic!("Wrong type in JSON at 'store.'{}'.content'", s),
}
},
_ => panic!("Wrong type in JSON at 'store.'{}''", s),
}
},
_ => panic!("Wrong type in JSON at 'store'"),
}
},
_ => panic!("Wrong type in JSON at top level"),
}
}
}