use log;
use errors::{Result, ChainErr};
use std::path::{Path, PathBuf};
use std::fs;
use std::io::Read;
use toml;
pub fn move_files(src: &PathBuf, dst: &PathBuf) -> Result<()> {
let err = || format!("failed: move_files({:?}, {:?})", src, dst);
if src.as_path().is_dir() {
if !dst.as_path().is_dir() {
log::llog(log::DebugMoveFiles,
|| format!("New dir created: {}", dst.display()));
create_dir(dst).chain_err(&err)?;
}
for item in read_dir(dst).chain_err(&err)? {
let item = item.chain_err(&err)?;
if !src.with_added(item.file_name()).as_path().exists() {
let path = item.path();
if path.as_path().is_dir() {
log::llog(log::DebugMoveFiles,
|| format!("Old dir removed: {}", path.display()));
remove_dir_all(&path).chain_err(&err)?;
} else {
log::llog(log::DebugMoveFiles,
|| format!("Old file removed: {}", path.display()));
remove_file(&path).chain_err(&err)?;
}
}
}
for item in read_dir(src).chain_err(&err)? {
let item = item.chain_err(&err)?;
let from = item.path().to_path_buf();
let to = dst.with_added(item.file_name());
move_files(&from, &to).chain_err(&err)?;
}
remove_dir_all(src).chain_err(&err)?;
} else {
move_one_file(src, dst).chain_err(&err)?;
}
Ok(())
}
pub fn copy_recursively(src: &PathBuf, dst: &PathBuf) -> Result<()> {
let err = || format!("failed: copy_recursively({:?}, {:?})", src, dst);
if src.as_path().is_dir() {
create_dir(&dst).chain_err(&err)?;
for item in read_dir(src).chain_err(&err)? {
let item = item.chain_err(&err)?;
let from = item.path().to_path_buf();
let to = dst.with_added(item.file_name());
copy_recursively(&from, &to).chain_err(&err)?;
}
} else {
copy_file(src, dst).chain_err(&err)?;
}
Ok(())
}
fn move_one_file(old_path: &PathBuf, new_path: &PathBuf) -> Result<()> {
let err = || format!("failed: move_one_file({:?}, {:?})", old_path, new_path);
let is_changed = if new_path.as_path().is_file() {
let string1 = file_to_string(old_path).chain_err(&err)?;
let string2 = file_to_string(new_path).chain_err(&err)?;
string1 != string2
} else {
true
};
if is_changed {
if new_path.as_path().exists() {
remove_file(&new_path).chain_err(&err)?;
}
rename_file(&old_path, &new_path).chain_err(&err)?;
log::llog(log::DebugMoveFiles,
|| format!("File changed: {}", new_path.display()));
} else {
remove_file(&old_path).chain_err(&err)?;
log::llog(log::DebugMoveFiles,
|| format!("File not changed: {}", new_path.display()));
}
Ok(())
}
pub trait PathBufWithAdded {
fn with_added<P: AsRef<Path>>(&self, path: P) -> PathBuf;
}
impl<T: AsRef<Path>> PathBufWithAdded for T {
fn with_added<P: AsRef<Path>>(&self, path: P) -> PathBuf {
let mut p = self.as_ref().to_path_buf();
p.push(path);
p
}
}
pub struct FileWrapper {
file: fs::File,
path: PathBuf,
}
pub fn open_file<P: AsRef<Path>>(path: P) -> Result<FileWrapper> {
Ok(FileWrapper {
file: fs::File::open(path.as_ref())
.chain_err(|| format!("Failed to open file for reading: {:?}", path.as_ref()))?,
path: path.as_ref().to_path_buf(),
})
}
pub fn file_to_string<P: AsRef<Path>>(path: P) -> Result<String> {
let mut f = open_file(path)?;
f.read_all()
}
pub fn create_file<P: AsRef<Path>>(path: P) -> Result<FileWrapper> {
Ok(FileWrapper {
file: fs::File::create(path.as_ref())
.chain_err(|| format!("Failed to create file: {:?}", path.as_ref()))?,
path: path.as_ref().to_path_buf(),
})
}
pub fn open_file_with_options<P: AsRef<Path>>(path: P,
options: &fs::OpenOptions)
-> Result<FileWrapper> {
Ok(FileWrapper {
file: options
.open(path.as_ref())
.chain_err(|| format!("Failed to open file: {:?}", path.as_ref()))?,
path: path.as_ref().to_path_buf(),
})
}
impl FileWrapper {
pub fn read_all(&mut self) -> Result<String> {
let mut r = String::new();
self
.file
.read_to_string(&mut r)
.chain_err(|| format!("Failed to read from file: {:?}", self.path))?;
Ok(r)
}
pub fn write<S: AsRef<str>>(&mut self, text: S) -> Result<()> {
use std::io::Write;
self
.file
.write_all(text.as_ref().as_bytes())
.chain_err(|| format!("Failed to write to file: {:?}", self.path))
}
pub fn into_file(self) -> fs::File {
self.file
}
}
pub fn load_json<P: AsRef<Path>, T: ::serde::Deserialize>(path: P) -> Result<T> {
let file = open_file(path.as_ref())?;
::serde_json::from_reader(file.into_file())
.chain_err(|| format!("failed to parse file as JSON: {}", path.as_ref().display()))
}
pub fn save_json<P: AsRef<Path>, T: ::serde::Serialize>(path: P, value: &T) -> Result<()> {
let file = create_file(path.as_ref())?;
::serde_json::to_writer(&mut file.into_file(), value).chain_err(|| {
format!("failed to serialize to JSON file: {}",
path.as_ref().display())
})
}
pub fn load_bincode<P: AsRef<Path>, T: ::serde::Deserialize>(path: P) -> Result<T> {
let mut file = open_file(path.as_ref())?.into_file();
::bincode::deserialize_from(&mut file, ::bincode::Infinite)
.chain_err(|| format!("load_bincode failed: {}", path.as_ref().display()))
}
pub fn save_bincode<P: AsRef<Path>, T: ::serde::Serialize>(path: P, value: &T) -> Result<()> {
let mut file = create_file(path.as_ref())?.into_file();
::bincode::serialize_into(&mut file, value, ::bincode::Infinite)
.chain_err(|| format!("save_bincode failed: {}", path.as_ref().display()))
}
pub fn load_toml<P: AsRef<Path>>(path: P) -> Result<toml::Table> {
let data = file_to_string(path.as_ref())?;
let mut parser = toml::Parser::new(&data);
parser
.parse()
.chain_err(|| format!("failed to parse TOML file: {}", path.as_ref().display()))
}
pub fn save_toml<P: AsRef<Path>>(path: P, data: toml::Table) -> Result<()> {
let mut file = create_file(path.as_ref())?;
file
.write(toml::Value::Table(data).to_string())
.chain_err(|| format!("failed to write to TOML file: {}", path.as_ref().display()))
}
pub fn create_dir<P: AsRef<Path>>(path: P) -> Result<()> {
fs::create_dir(path.as_ref()).chain_err(|| format!("Failed to create dir: {:?}", path.as_ref()))
}
pub fn create_dir_all<P: AsRef<Path>>(path: P) -> Result<()> {
fs::create_dir_all(path.as_ref()).chain_err(|| {
format!("Failed to create dirs (with parent components): {:?}",
path.as_ref())
})
}
pub fn remove_dir<P: AsRef<Path>>(path: P) -> Result<()> {
fs::remove_dir(path.as_ref()).chain_err(|| format!("Failed to remove dir: {:?}", path.as_ref()))
}
pub fn remove_dir_all<P: AsRef<Path>>(path: P) -> Result<()> {
fs::remove_dir_all(path.as_ref()).chain_err(|| {
format!("Failed to remove dir (recursively): {:?}",
path.as_ref())
})
}
pub fn remove_file<P: AsRef<Path>>(path: P) -> Result<()> {
fs::remove_file(path.as_ref()).chain_err(|| format!("Failed to remove file: {:?}", path.as_ref()))
}
pub fn rename_file<P: AsRef<Path>, P2: AsRef<Path>>(path1: P, path2: P2) -> Result<()> {
fs::rename(path1.as_ref(), path2.as_ref()).chain_err(|| {
format!("Failed to rename file from {:?} to {:?}",
path1.as_ref(),
path2.as_ref())
})
}
pub fn copy_file<P: AsRef<Path>, P2: AsRef<Path>>(path1: P, path2: P2) -> Result<()> {
fs::copy(path1.as_ref(), path2.as_ref())
.map(|_| ())
.chain_err(|| {
format!("Failed to copy file from {:?} to {:?}",
path1.as_ref(),
path2.as_ref())
})
}
pub struct ReadDirWrapper {
read_dir: fs::ReadDir,
path: PathBuf,
}
pub fn read_dir<P: AsRef<Path>>(path: P) -> Result<ReadDirWrapper> {
Ok(ReadDirWrapper {
read_dir: fs::read_dir(path.as_ref())
.chain_err(|| format!("Failed to read dir: {:?}", path.as_ref()))?,
path: path.as_ref().to_path_buf(),
})
}
impl Iterator for ReadDirWrapper {
type Item = Result<fs::DirEntry>;
fn next(&mut self) -> Option<Result<fs::DirEntry>> {
self
.read_dir
.next()
.map(|value| value.chain_err(|| format!("Failed to read dir (in item): {:?}", self.path)))
}
}
pub fn canonicalize<P: AsRef<Path>>(path: P) -> Result<PathBuf> {
let r = fs::canonicalize(path.as_ref())
.chain_err(|| format!("failed to canonicalize {}", path.as_ref().display()))?;
{
let str = path_to_str(&r)?;
if str.starts_with(r"\\?\") {
return Ok(PathBuf::from(&str[4..]));
}
}
Ok(r)
}
pub fn path_to_str(path: &Path) -> Result<&str> {
path
.to_str()
.chain_err(|| format!("Path is not valid unicode: {}", path.display()))
}
use std::ffi::{OsStr, OsString};
pub fn os_str_to_str(os_str: &OsStr) -> Result<&str> {
os_str
.to_str()
.chain_err(|| format!("String is not valid unicode: {}", os_str.to_string_lossy()))
}
pub fn os_string_into_string(s: OsString) -> Result<String> {
s.into_string()
.map_err(|s| format!("String is not valid unicode: {}", s.to_string_lossy()).into())
}
pub fn repo_crate_local_path(relative_path: &str) -> Result<PathBuf> {
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let parent = path
.parent()
.chain_err(|| "failed to get parent directory")?;
let parent2 = parent
.parent()
.chain_err(|| "failed to get parent directory")?;
let result = parent2.with_added(relative_path);
if !result.exists() {
return Err(format!("detected path does not exist: {}", result.display()).into());
}
Ok(result)
}