use std::ops::Deref;
use std::path::Path;
use std::path::PathBuf;
use std::fmt::{Display, Debug, Formatter};
use std::fmt::Error as FmtError;
use std::result::Result as RResult;
use std::path::Components;
use error::StoreErrorKind as SEK;
use error::StoreError as SE;
use error::ResultExt;
use store::Result;
#[derive(Debug, Clone, Hash, Eq, PartialOrd, Ord)]
pub struct StoreId {
base: Option<PathBuf>,
id: PathBuf,
}
impl PartialEq for StoreId {
fn eq(&self, other: &StoreId) -> bool {
self.id == other.id
}
}
impl StoreId {
pub fn new(base: Option<PathBuf>, id: PathBuf) -> Result<StoreId> {
StoreId::new_baseless(id).map(|mut sid| { sid.base = base; sid })
}
pub fn from_full_path<D>(store_part: &PathBuf, full_path: D) -> Result<StoreId>
where D: Deref<Target = Path>
{
let p = full_path
.strip_prefix(store_part)
.chain_err(|| SEK::StoreIdBuildFromFullPathError)?;
StoreId::new(Some(store_part.clone()), PathBuf::from(p))
}
pub fn new_baseless(id: PathBuf) -> Result<StoreId> {
debug!("Trying to get a new baseless id from: {:?}", id);
if id.is_absolute() {
Err(SE::from_kind(SEK::StoreIdLocalPartAbsoluteError(id)))
} else {
Ok(StoreId {
base: None,
id: id
})
}
}
pub fn without_base(mut self) -> StoreId {
self.base = None;
self
}
pub fn with_base(mut self, base: PathBuf) -> Self {
self.base = Some(base);
self
}
pub fn into_pathbuf(mut self) -> Result<PathBuf> {
let base = self.base.take();
let mut base = base.ok_or_else(|| SEK::StoreIdHasNoBaseError(self.id.clone()))?;
base.push(self.id);
Ok(base)
}
pub fn exists(&self) -> Result<bool> {
self.clone().into_pathbuf().map(|pb| pb.exists())
}
pub fn to_str(&self) -> Result<String> {
self.base
.as_ref()
.cloned()
.map(|mut base| { base.push(self.id.clone()); base })
.unwrap_or_else(|| self.id.clone())
.to_str()
.map(String::from)
.ok_or(SE::from_kind(SEK::StoreIdHandlingError))
}
pub fn components(&self) -> Components {
self.id.components()
}
pub fn local(&self) -> &PathBuf {
&self.id
}
pub fn is_in_collection(&self, colls: &[&str]) -> bool {
use std::path::Component;
self.id
.components()
.zip(colls)
.map(|(component, pred_coll)| match component {
Component::Normal(ref s) => s.to_str().map(|ref s| s == pred_coll).unwrap_or(false),
_ => false
})
.all(|x| x)
}
pub fn local_push<P: AsRef<Path>>(&mut self, path: P) {
self.id.push(path)
}
}
impl Display for StoreId {
fn fmt(&self, fmt: &mut Formatter) -> RResult<(), FmtError> {
match self.id.to_str() {
Some(s) => write!(fmt, "{}", s),
None => write!(fmt, "{}", self.id.to_string_lossy()),
}
}
}
pub trait IntoStoreId {
fn into_storeid(self) -> Result<StoreId>;
}
impl IntoStoreId for StoreId {
fn into_storeid(self) -> Result<StoreId> {
Ok(self)
}
}
impl IntoStoreId for PathBuf {
fn into_storeid(self) -> Result<StoreId> {
StoreId::new_baseless(self)
}
}
#[macro_export]
macro_rules! module_entry_path_mod {
($name:expr) => (
#[deny(missing_docs,
missing_copy_implementations,
trivial_casts, trivial_numeric_casts,
unsafe_code,
unstable_features,
unused_import_braces, unused_qualifications,
unused_imports)]
pub mod module_path {
use std::convert::AsRef;
use std::path::Path;
use std::path::PathBuf;
use $crate::storeid::StoreId;
use $crate::store::Result;
pub struct ModuleEntryPath(PathBuf);
impl ModuleEntryPath {
pub fn new<P: AsRef<Path>>(pa: P) -> ModuleEntryPath {
let mut path = PathBuf::new();
path.push(format!("{}", $name));
path.push(pa.as_ref().clone());
let name = pa.as_ref().file_name().unwrap()
.to_str().unwrap();
path.set_file_name(name);
ModuleEntryPath(path)
}
}
impl $crate::storeid::IntoStoreId for ModuleEntryPath {
fn into_storeid(self) -> Result<$crate::storeid::StoreId> {
StoreId::new(None, self.0)
}
}
}
)
}
pub struct StoreIdIterator {
iter: Box<Iterator<Item = StoreId>>,
}
impl Debug for StoreIdIterator {
fn fmt(&self, fmt: &mut Formatter) -> RResult<(), FmtError> {
write!(fmt, "StoreIdIterator")
}
}
impl StoreIdIterator {
pub fn new(iter: Box<Iterator<Item = StoreId>>) -> StoreIdIterator {
StoreIdIterator {
iter: iter,
}
}
}
impl Iterator for StoreIdIterator {
type Item = StoreId;
fn next(&mut self) -> Option<StoreId> {
self.iter.next()
}
}
#[cfg(test)]
mod test {
use std::path::PathBuf;
use storeid::StoreId;
use storeid::IntoStoreId;
use error::StoreErrorKind as SEK;
module_entry_path_mod!("test");
#[test]
fn test_correct_path() {
let p = module_path::ModuleEntryPath::new("test");
assert_eq!(p.into_storeid().unwrap().to_str().unwrap(), "test/test");
}
#[test]
fn test_baseless_path() {
let id = StoreId::new_baseless(PathBuf::from("test"));
assert!(id.is_ok());
assert_eq!(id.unwrap(), StoreId {
base: None,
id: PathBuf::from("test")
});
}
#[test]
fn test_base_path() {
let id = StoreId::from_full_path(&PathBuf::from("/tmp/"), PathBuf::from("/tmp/test"));
assert!(id.is_ok());
assert_eq!(id.unwrap(), StoreId {
base: Some(PathBuf::from("/tmp/")),
id: PathBuf::from("test")
});
}
#[test]
fn test_adding_base_to_baseless_path() {
let id = StoreId::new_baseless(PathBuf::from("test"));
assert!(id.is_ok());
let id = id.unwrap();
assert_eq!(id, StoreId { base: None, id: PathBuf::from("test") });
let id = id.with_base(PathBuf::from("/tmp/"));
assert_eq!(id, StoreId {
base: Some(PathBuf::from("/tmp/")),
id: PathBuf::from("test")
});
}
#[test]
fn test_removing_base_from_base_path() {
let id = StoreId::from_full_path(&PathBuf::from("/tmp/"), PathBuf::from("/tmp/test"));
assert!(id.is_ok());
let id = id.unwrap();
assert_eq!(id, StoreId {
base: Some(PathBuf::from("/tmp/")),
id: PathBuf::from("test")
});
let id = id.without_base();
assert_eq!(id, StoreId {
base: None,
id: PathBuf::from("test")
});
}
#[test]
fn test_baseless_into_pathbuf_is_err() {
let id = StoreId::new_baseless(PathBuf::from("test"));
assert!(id.is_ok());
assert!(id.unwrap().into_pathbuf().is_err());
}
#[test]
fn test_baseless_into_pathbuf_is_storeidhasnobaseerror() {
let id = StoreId::new_baseless(PathBuf::from("test"));
assert!(id.is_ok());
let pb = id.unwrap().into_pathbuf();
assert!(pb.is_err());
assert!(is_match!(pb.unwrap_err().kind(), &SEK::StoreIdHasNoBaseError(_)));
}
#[test]
fn test_basefull_into_pathbuf_is_ok() {
let id = StoreId::from_full_path(&PathBuf::from("/tmp/"), PathBuf::from("/tmp/test"));
assert!(id.is_ok());
assert!(id.unwrap().into_pathbuf().is_ok());
}
#[test]
fn test_basefull_into_pathbuf_is_correct() {
let id = StoreId::from_full_path(&PathBuf::from("/tmp/"), PathBuf::from("/tmp/test"));
assert!(id.is_ok());
let pb = id.unwrap().into_pathbuf();
assert!(pb.is_ok());
assert_eq!(pb.unwrap(), PathBuf::from("/tmp/test"));
}
#[test]
fn storeid_in_collection() {
let p = module_path::ModuleEntryPath::new("1/2/3/4/5/6/7/8/9/0").into_storeid().unwrap();
assert!(p.is_in_collection(&["test", "1"]));
assert!(p.is_in_collection(&["test", "1", "2"]));
assert!(p.is_in_collection(&["test", "1", "2", "3"]));
assert!(p.is_in_collection(&["test", "1", "2", "3", "4"]));
assert!(p.is_in_collection(&["test", "1", "2", "3", "4", "5"]));
assert!(p.is_in_collection(&["test", "1", "2", "3", "4", "5", "6"]));
assert!(p.is_in_collection(&["test", "1", "2", "3", "4", "5", "6", "7"]));
assert!(p.is_in_collection(&["test", "1", "2", "3", "4", "5", "6", "7", "8"]));
assert!(p.is_in_collection(&["test", "1", "2", "3", "4", "5", "6", "7", "8", "9"]));
assert!(p.is_in_collection(&["test", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0"]));
assert!(!p.is_in_collection(&["test", "0", "2", "3", "4", "5", "6", "7", "8", "9", "0"]));
assert!(!p.is_in_collection(&["test", "1", "2", "3", "4", "5", "6", "8"]));
assert!(!p.is_in_collection(&["test", "1", "2", "3", "leet", "5", "6", "7"]));
}
}