use std::borrow::ToOwned;
use std::error::Error as ErrorTrait;
use std::fmt;
use std::io;
use std::iter::IntoIterator;
use std::path::PathBuf;
pub use self::utils::{Bytes, Names};
pub use self::fs::FileSystemRepository;
pub mod fs;
pub type Result<T> = ::std::result::Result<T, Error>;
#[derive(Debug)]
pub enum Error {
InvalidKey(Vec<String>, Option<io::Error>),
InvalidUrl(&'static str),
NotADirectory(PathBuf),
CannotBorrow,
Io(io::Error),
}
impl Error {
#[inline]
pub fn invalid_key<T: AsRef<str>>(key: &[T], cause: Option<io::Error>) ->
Error
{
let copied_key = key.iter()
.map(|e| e.as_ref().to_owned())
.collect();
Error::InvalidKey(copied_key, cause)
}
#[inline]
pub fn invalid_url(detail: &'static str) -> Error {
Error::InvalidUrl(detail)
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
try!(write!(f, "{}", self.description()));
match *self {
Error::InvalidKey(ref key, _) => {
try!(write!(f, ": ["));
let mut first = true;
for i in key.iter() {
if first { first = false; } else { try!(write!(f, ", ")); }
try!(write!(f, "{:?}", i));
}
try!(write!(f, "]"));
}
Error::InvalidUrl(ref msg) => {
try!(write!(f, ": {}", msg));
}
_ => { }
}
if let Some(cause) = self.cause() {
try!(write!(f, " caused by `{}`", cause));
}
Ok(())
}
}
impl ::std::error::Error for Error {
fn description(&self) -> &str {
match *self {
Error::InvalidKey(_, _) => "invalid key",
Error::InvalidUrl(_) => "invalid URL",
Error::NotADirectory(_) => "not a directory",
Error::CannotBorrow => "can't borrow",
Error::Io(_) => "IO error"
}
}
fn cause(&self) -> Option<&::std::error::Error> {
match *self {
Error::InvalidKey(_, Some(ref err)) => Some(err as &::std::error::Error),
Error::Io(ref err) => Some(err as &::std::error::Error),
_ => None
}
}
}
impl From<io::Error> for Error {
fn from(err: io::Error) -> Error {
Error::Io(err)
}
}
pub trait Repository {
fn get_reader<'a, T>(&'a self, key: &[T]) -> Result<Box<io::BufRead + 'a>>
where T: AsRef<str>;
fn get_writer<'a, T>(&'a mut self, key: &[T]) -> Result<Box<io::Write + 'a>>
where T: AsRef<str>;
fn read<T>(&self, key: &[T]) -> Result<Vec<u8>>
where T: AsRef<str>
{
let mut buf = vec![];
try!(try!(self.get_reader(key)).read_to_end(&mut buf));
Ok(buf)
}
fn write<T, U, I>(&mut self, key: &[T], buf: I) -> Result<()>
where T: AsRef<str>, U: Bytes, I: IntoIterator<Item=U>
{
let mut w = try!(self.get_writer(key));
for b in buf {
try!(w.write_all(b.as_bytes()));
}
Ok(())
}
fn exists<T: AsRef<str>>(&self, key: &[T]) -> bool;
fn list<'a, T: AsRef<str>>(&'a self, key: &[T]) -> Result<Names<'a>>;
}
pub trait ToRepository<R: Repository> {
fn to_repo(&self) -> Result<R>;
fn from_repo(repo: &R) -> Self;
}
mod utils {
pub type Names<'a> = Box<Iterator<Item=super::Result<String>> + 'a>;
pub trait Bytes {
fn as_bytes<'a>(&'a self) -> &'a [u8];
}
impl Bytes for [u8] {
fn as_bytes(&self) -> &[u8] { self }
}
impl Bytes for Vec<u8> {
fn as_bytes(&self) -> &[u8] { &self }
}
impl Bytes for str {
fn as_bytes(&self) -> &[u8] { str::as_bytes(self) }
}
impl Bytes for String {
fn as_bytes(&self) -> &[u8] { str::as_bytes(&self) }
}
impl<'a, T: ?Sized + Bytes> Bytes for &'a T {
fn as_bytes(&self) -> &[u8] { (*self).as_bytes() }
}
}
#[cfg(test)]
#[macro_use]
pub mod test {
use super::{Names, Repository};
use std::borrow::ToOwned;
use std::collections::BTreeSet;
use std::io;
use std::marker::PhantomData;
struct RepositoryImplemented;
impl Repository for RepositoryImplemented {
fn get_reader<T: AsRef<str>>(&self, _key: &[T]) ->
super::Result<Box<io::BufRead>>
{
Ok(Box::new(io::empty()) as Box<io::BufRead>)
}
fn get_writer<T: AsRef<str>>(&mut self, _key: &[T]) ->
super::Result<Box<io::Write>>
{
Ok(Box::new(io::sink()) as Box<io::Write>)
}
fn exists<T: AsRef<str>>(&self, _key: &[T]) -> bool {
true
}
fn list<T: AsRef<str>>(&self, _key: &[T]) -> super::Result<Names> {
struct Empty<'a> { _a: PhantomData<&'a ()> }
impl<'a> Iterator for Empty<'a> {
type Item = super::Result<String>;
fn next(&mut self) -> Option<super::Result<String>> { None }
fn size_hint(&self) -> (usize, Option<usize>) { (0, Some(0)) }
}
Ok(Box::new(Empty { _a: PhantomData }) as Names)
}
}
#[test]
fn test_dummy_implementation() {
let mut repository = RepositoryImplemented;
{
let mut reader = repository.get_reader(&["key"]).unwrap();
let mut buf = vec![];
assert_eq!(reader.read_to_end(&mut buf).unwrap(), 0);
assert!(buf.len() == 0);
}
{
let mut writer = repository.get_writer(&["key"]).unwrap();
writer.write_all("Hello".as_bytes()).unwrap();
}
assert!(repository.exists(&["key"]));
let mut path_list = repository.list(&["key"]).unwrap();
assert!(path_list.next().is_none());
}
pub fn test_repository<R: Repository>(mut repository: R) {
let empty: &[&str] = &[];
expect_invalid_key!(repository.get_reader, &[]);
expect_invalid_key!(repository.get_writer, &[]);
assert!(unwrap!(repository.list(empty)).next().is_none());
assert!(!repository.exists(&["key"]));
expect_invalid_key!(repository.read, &["key"]);
unwrap!(repository.write(&["key"], &["cont", "ents"]));
assert_eq!(
repository.list(empty).unwrap().map(|e| e.unwrap())
.collect::<Vec<_>>(),
["key"]);
assert!(repository.exists(&["key"]));
assert_eq!(unwrap!(repository.read(&["key"])), b"contents");
assert!(!repository.exists(&["dir", "key"]));
expect_invalid_key!(repository.read, &["dir", "key"]);
unwrap!(repository.write(&["dir", "key"], &["cont", "ents"]));
assert_eq!(
repository.list(empty).unwrap()
.map(|e| e.unwrap())
.collect::<BTreeSet<_>>(),
["dir", "key"].iter().map(|&e| e.to_owned())
.collect::<BTreeSet<_>>());
assert!(repository.exists(&["dir", "key"]));
assert!(!repository.exists(&["dir", "key2"]));
assert_eq!(unwrap!(repository.read(&["dir", "key"])), b"contents");
expect_invalid_key!(repository.get_writer, &["key", "key"]);
expect_invalid_key!(repository.list, &["key"]);
}
}