iron_test/
project_builder.rsuse std::fs;
use std::io::{self, Write};
use std::env;
use std::path::{Path, PathBuf};
use std::fmt::Debug;
use uuid::Uuid;
use std::sync::{Mutex, Arc};
static IRON_INTEGRATION_TEST_DIR : &'static str = "iron-integration-tests";
#[derive(Debug, PartialEq, Clone)]
struct FileBuilder {
path: PathBuf,
body: Vec<u8>
}
impl FileBuilder {
pub fn new<P: AsRef<Path>, B: Into<Vec<u8>>>(path: P, body: B) -> FileBuilder {
FileBuilder { path: path.as_ref().to_path_buf(), body: body.into() }
}
fn mk(&self) -> Result<(), String> {
try!(mkdir_recursive(&self.dirname()));
let mut file = try!(
fs::File::create(&self.path)
.with_err_msg(format!("Could not create file; path={}",
self.path.display())));
file.write_all(&self.body)
.with_err_msg(format!("Could not write to file; path={}",
self.path.display()))
}
fn dirname(&self) -> &Path {
&self.path.parent().expect("parent directory")
}
}
#[derive(Debug, Clone)]
pub struct ProjectBuilder {
name: String,
root: PathBuf,
files: Vec<FileBuilder>,
lock: Arc<Mutex<()>>,
}
impl ProjectBuilder {
pub fn new(name: &str) -> ProjectBuilder {
let id = Uuid::new_v4();
let path = root(id);
path.rm_rf().unwrap();
ProjectBuilder {
name: name.to_string(),
root: path.join(name),
files: vec!(),
lock: Arc::new(Mutex::new(())),
}
}
pub fn root(&self) -> &Path {
&self.root
}
pub fn file<P, B>(mut self, path: P, body: B) -> ProjectBuilder
where P: AsRef<Path>, B: Into<Vec<u8>> {
self.files.push(FileBuilder::new(self.root.join(&path), body));
self
}
pub fn build(&self) -> &ProjectBuilder {
self.build_with_result().map(|_| self).unwrap()
}
pub fn build_with_result(&self) -> Result<(), String> {
let _lock = self.lock.lock().unwrap();
for file in self.files.iter() {
try!(file.mk());
}
Ok(())
}
}
impl PartialEq for ProjectBuilder {
fn eq(&self, other: &ProjectBuilder) -> bool {
self.name.eq(&other.name) &&
self.root.eq(&other.root) &&
self.files.eq(&other.files)
}
}
impl Drop for ProjectBuilder {
fn drop(&mut self) {
let _lock = self.lock.lock().unwrap();
match self.root().parent().map(BuilderPathExt::rm_rf) {
Some(Ok(_)) => debug!("Successfully cleaned up the test directory; path = {:?}", self.root().parent().unwrap()),
Some(Err(e)) => debug!("Failed to cleanup the test directory; path = {:?}; {}", self.root().parent().unwrap(), e),
None => debug!("Failed to cleanup the test directory; no parent")
}
}
}
fn mkdir_recursive(path: &Path) -> Result<(), String> {
fs::create_dir_all(path)
.with_err_msg(format!("could not create directory; path={}",
path.display()))
}
trait ErrMsg<T> {
fn with_err_msg(self, val: String) -> Result<T, String>;
}
impl<T, E: Debug> ErrMsg<T> for Result<T, E> {
fn with_err_msg(self, val: String) -> Result<T, String> {
match self {
Ok(val) => Ok(val),
Err(err) => Err(format!("{}; original={:?}", val, err))
}
}
}
fn root(id: Uuid) -> PathBuf {
integration_tests_dir().join(&format!("test-{}", id))
}
fn integration_tests_dir() -> PathBuf {
env::current_exe()
.map(|mut p| { p.pop(); p.join(IRON_INTEGRATION_TEST_DIR) })
.unwrap()
}
pub trait BuilderPathExt {
fn rm_rf(&self) -> io::Result<()>;
}
impl BuilderPathExt for Path {
fn rm_rf(&self) -> io::Result<()> {
if let Ok(_) = fs::metadata(self) {
fs::remove_dir_all(self)
} else {
Ok(())
}
}
}