use std::fs;
use std::fmt;
use std::io;
use std_prelude::*;
use super::{Error, Result};
use super::{PathAbs, PathArc, PathType};
#[derive(Clone, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub struct PathDir(pub(crate) PathAbs);
impl PathDir {
pub fn new<P: AsRef<Path>>(path: P) -> Result<PathDir> {
let abs = PathAbs::new(path)?;
PathDir::from_abs(abs)
}
pub fn current_dir() -> Result<PathDir> {
let dir = ::std::env::current_dir()
.map_err(|err| Error::new(err, "getting current_dir", PathArc::new("$CWD")))?;
PathDir::new(dir)
}
pub fn from_abs(abs: PathAbs) -> Result<PathDir> {
if abs.is_dir() {
Ok(PathDir::from_abs_unchecked(abs))
} else {
Err(Error::new(
io::Error::new(io::ErrorKind::InvalidInput, "path is not a dir"),
"resolving",
abs.into(),
))
}
}
#[inline(always)]
pub fn from_abs_unchecked(abs: PathAbs) -> PathDir {
PathDir(abs)
}
pub fn create<P: AsRef<Path>>(path: P) -> Result<PathDir> {
if let Err(err) = fs::create_dir(&path) {
match err.kind() {
io::ErrorKind::AlreadyExists => {}
_ => return Err(Error::new(err, "creating", PathArc::new(path))),
}
}
PathDir::new(path)
}
pub fn create_all<P: AsRef<Path>>(path: P) -> Result<PathDir> {
fs::create_dir_all(&path)
.map_err(|err| Error::new(err, "creating-all", PathArc::new(&path)))?;
PathDir::new(path)
}
pub fn join_abs<P: AsRef<Path>>(&self, path: P) -> Result<PathType> {
let joined = self.join(path.as_ref());
PathType::new(joined)
}
pub fn list(&self) -> Result<ListDir> {
let fsread =
fs::read_dir(self).map_err(|err| Error::new(err, "reading dir", self.clone().into()))?;
Ok(ListDir {
dir: self.clone(),
fsread: fsread,
})
}
pub fn remove(self) -> Result<()> {
fs::remove_dir(&self).map_err(|err| Error::new(err, "removing", self.into()))
}
pub fn remove_all(self) -> Result<()> {
fs::remove_dir_all(&self).map_err(|err| Error::new(err, "removing-all", self.into()))
}
pub fn symlink<P: AsRef<Path>>(&self, dst: P) -> Result<PathDir> {
symlink_dir(&self, &dst).map_err(|err| {
Error::new(
err,
&format!("linking from {} to", dst.as_ref().display()),
self.clone().into(),
)
})?;
PathDir::new(dst)
}
pub fn as_path(&self) -> &Path {
self.as_ref()
}
pub fn canonicalize(&self) -> Result<PathDir> {
Ok(PathDir(self.0.canonicalize()?))
}
pub fn mock<P: AsRef<Path>>(path: P) -> PathDir {
PathDir(PathAbs::mock(path))
}
}
pub struct ListDir {
dir: PathDir,
fsread: fs::ReadDir,
}
impl ::std::iter::Iterator for ListDir {
type Item = Result<PathType>;
fn next(&mut self) -> Option<Result<PathType>> {
let entry = match self.fsread.next() {
Some(r) => match r {
Ok(e) => e,
Err(err) => {
return Some(Err(Error::new(
err,
"iterating over",
self.dir.clone().into(),
)))
}
},
None => return None,
};
Some(PathType::new(entry.path()))
}
}
impl fmt::Debug for PathDir {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
}
}
impl AsRef<PathAbs> for PathDir {
fn as_ref(&self) -> &PathAbs {
&self.0
}
}
impl AsRef<PathArc> for PathDir {
fn as_ref(&self) -> &PathArc {
self.0.as_ref()
}
}
impl AsRef<Path> for PathDir {
fn as_ref(&self) -> &Path {
self.0.as_ref()
}
}
impl AsRef<PathBuf> for PathDir {
fn as_ref(&self) -> &PathBuf {
self.0.as_ref()
}
}
impl Borrow<PathAbs> for PathDir {
fn borrow(&self) -> &PathAbs {
self.as_ref()
}
}
impl Borrow<PathArc> for PathDir {
fn borrow(&self) -> &PathArc {
self.as_ref()
}
}
impl Borrow<Path> for PathDir {
fn borrow(&self) -> &Path {
self.as_ref()
}
}
impl Borrow<PathBuf> for PathDir {
fn borrow(&self) -> &PathBuf {
self.as_ref()
}
}
impl<'a> Borrow<PathAbs> for &'a PathDir {
fn borrow(&self) -> &PathAbs {
self.as_ref()
}
}
impl<'a> Borrow<PathArc> for &'a PathDir {
fn borrow(&self) -> &PathArc {
self.as_ref()
}
}
impl<'a> Borrow<Path> for &'a PathDir {
fn borrow(&self) -> &Path {
self.as_ref()
}
}
impl<'a> Borrow<PathBuf> for &'a PathDir {
fn borrow(&self) -> &PathBuf {
self.as_ref()
}
}
impl Deref for PathDir {
type Target = PathAbs;
fn deref(&self) -> &PathAbs {
&self.0
}
}
impl Into<PathAbs> for PathDir {
fn into(self) -> PathAbs {
self.0
}
}
impl Into<PathArc> for PathDir {
fn into(self) -> PathArc {
(self.0).0
}
}
impl Into<PathBuf> for PathDir {
fn into(self) -> PathBuf {
let arc: PathArc = self.into();
arc.into()
}
}
#[cfg(test)]
mod tests {
use tempdir::TempDir;
use std::collections::HashSet;
use super::super::{PathAbs, PathDir, PathFile, PathType};
#[test]
fn sanity_list() {
let tmp_dir = TempDir::new("example").expect("create temp dir");
let tmp_abs = PathDir::new(tmp_dir.path()).unwrap();
let foo_dir = PathDir::create(tmp_abs.join("foo")).unwrap();
let bar_file = PathFile::create(tmp_abs.join("bar.txt")).unwrap();
let mut result = HashSet::new();
for p in tmp_abs.list().unwrap() {
result.insert(p.unwrap());
}
let mut expected = HashSet::new();
expected.insert(PathType::Dir(foo_dir.clone()));
expected.insert(PathType::File(bar_file.clone()));
assert_eq!(expected, result);
let _: PathAbs = foo_dir.into();
let _: PathAbs = bar_file.into();
}
}
#[cfg(unix)]
fn symlink_dir<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> {
::std::os::unix::fs::symlink(src, dst)
}
#[cfg(windows)]
fn symlink_dir<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> {
::std::os::windows::fs::symlink_dir(src, dst)
}