use std::env;
use std::fs;
use std::path::Path;
use std::path::PathBuf;
use std::slice;
use default_tags::DefaultTags;
use dir;
use ext::file::FileExt;
use ovr::Override;
use pod::Pod;
use repos::Repos;
use util::{ConductorPathExt, Error, ToStrOrErr};
#[derive(Debug)]
pub struct Project {
root_dir: PathBuf,
src_dir: PathBuf,
output_dir: PathBuf,
pods: Vec<Pod>,
overrides: Vec<Override>,
repos: Repos,
default_tags: Option<DefaultTags>,
}
impl Project {
pub fn from_current_dir() -> Result<Project, Error> {
let current = try!(env::current_dir());
let root_dir = try!(dir::find_project(¤t));
let overrides = try!(Project::find_overrides(&root_dir));
let pods = try!(Project::find_pods(&root_dir, &overrides));
let repos = try!(Repos::new(&pods));
Ok(Project {
root_dir: root_dir.clone(),
src_dir: root_dir.join("src"),
output_dir: root_dir.join(".conductor"),
pods: pods,
overrides: overrides,
repos: repos,
default_tags: None,
})
}
#[cfg(test)]
pub fn from_example(name: &str) -> Result<Project, Error> {
use rand::random;
let example_dir = Path::new("examples").join(name);
let root_dir = try!(dir::find_project(&example_dir));
let rand_name = format!("{}-{}", name, random::<u16>());
let test_output = Path::new("target/test_output").join(&rand_name);
let overrides = try!(Project::find_overrides(&root_dir));
let pods = try!(Project::find_pods(&root_dir, &overrides));
let repos = try!(Repos::new(&pods));
Ok(Project {
root_dir: root_dir.clone(),
src_dir: test_output.join("src"),
output_dir: test_output,
pods: pods,
overrides: overrides,
repos: repos,
default_tags: None,
})
}
#[cfg(test)]
pub fn remove_test_output(&self) -> Result<(), Error> {
if self.output_dir.exists() {
try!(fs::remove_dir_all(&self.output_dir));
}
Ok(())
}
fn find_overrides(root_dir: &Path) -> Result<Vec<Override>, Error> {
let overrides_dir = root_dir.join("pods/overrides");
let mut overrides = vec!();
for glob_result in try!(overrides_dir.glob("*")) {
let path = try!(glob_result);
if path.is_dir() {
let name =
try!(path.file_name().unwrap().to_str_or_err()).to_owned();
overrides.push(Override::new(name));
}
}
Ok(overrides)
}
fn find_pods(root_dir: &Path, overrides: &[Override]) ->
Result<Vec<Pod>, Error>
{
let pods_dir = root_dir.join("pods");
let mut pods = vec!();
for glob_result in try!(pods_dir.glob("*.yml")) {
let path = try!(glob_result);
let name =
try!(path.file_stem().unwrap().to_str_or_err()).to_owned();
pods.push(try!(Pod::new(pods_dir.clone(), name, overrides)));
}
Ok(pods)
}
pub fn root_dir(&self) -> &Path {
&self.root_dir
}
pub fn src_dir(&self) -> &Path {
&self.src_dir
}
pub fn output_dir(&self) -> &Path {
&self.output_dir
}
pub fn output_pods_dir(&self) -> PathBuf {
self.output_dir.join("pods")
}
pub fn pods(&self) -> Pods {
Pods { iter: self.pods.iter() }
}
pub fn pod(&self, name: &str) -> Option<&Pod> {
self.pods().find(|pod| pod.name() == name)
}
pub fn overrides(&self) -> Overrides {
Overrides { iter: self.overrides.iter() }
}
pub fn ovr(&self, name: &str) -> Option<&Override> {
self.overrides().find(|ovr| ovr.name() == name)
}
pub fn repos(&self) -> &Repos {
&self.repos
}
pub fn default_tags(&self) -> Option<&DefaultTags> {
self.default_tags.as_ref()
}
pub fn set_default_tags(&mut self, tags: DefaultTags) -> &mut Project {
self.default_tags = Some(tags);
self
}
pub fn output(&self) -> Result<(), Error> {
let in_pods = self.root_dir.join("pods");
let out_pods = self.output_dir.join("pods");
if out_pods.exists() {
try!(fs::remove_dir_all(&out_pods));
}
for glob_result in try!(in_pods.glob("**/*.env")) {
let rel = try!(try!(glob_result).strip_prefix(&in_pods)).to_owned();
let in_path = in_pods.join(&rel);
let out_path = try!(out_pods.join(&rel).with_guaranteed_parent());
debug!("Copy {} to {}", in_path.display(), out_path.display());
try!(fs::copy(in_path, out_path));
}
for pod in &self.pods {
let rel = pod.rel_path();
let out_path = try!(out_pods.join(&rel).with_guaranteed_parent());
debug!("Generating {}", out_path.display());
let mut file = pod.file().to_owned();
try!(file.update_for_output(self));
try!(file.write_to_path(out_path));
for ovr in &self.overrides {
let rel = try!(pod.override_rel_path(ovr));
let out_path = try!(out_pods.join(&rel).with_guaranteed_parent());
debug!("Generating {}", out_path.display());
let mut file = try!(pod.override_file(ovr)).to_owned();
try!(file.update_for_output(self));
try!(file.write_to_path(out_path));
}
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct Pods<'a> {
iter: slice::Iter<'a, Pod>,
}
impl<'a> Iterator for Pods<'a> {
type Item = &'a Pod;
fn next(&mut self) -> Option<&'a Pod> {
self.iter.next()
}
}
#[derive(Debug, Clone)]
pub struct Overrides<'a> {
iter: slice::Iter<'a, Override>,
}
impl<'a> Iterator for Overrides<'a> {
type Item = &'a Override;
fn next(&mut self) -> Option<&'a Override> {
self.iter.next()
}
}
#[test]
fn new_from_example_uses_example_and_target() {
let proj = Project::from_example("hello").unwrap();
assert_eq!(proj.root_dir, Path::new("examples/hello"));
let output_dir = proj.output_dir.to_str_or_err().unwrap();
assert!(output_dir.starts_with("target/test_output/hello-"));
let src_dir = proj.src_dir.to_str_or_err().unwrap();
assert!(src_dir.starts_with("target/test_output/hello-"));
}
#[test]
fn pods_are_loaded() {
let proj = Project::from_example("hello").unwrap();
let names: Vec<_> = proj.pods.iter().map(|pod| pod.name()).collect();
assert_eq!(names, ["frontend"]);
}
#[test]
fn overrides_are_loaded() {
let proj = Project::from_example("hello").unwrap();
let names: Vec<_> = proj.overrides.iter().map(|o| o.name()).collect();
assert_eq!(names, ["development", "production", "test"]);
}
#[test]
fn output_copies_env_files() {
let proj = Project::from_example("hello").unwrap();
proj.output().unwrap();
assert!(proj.output_dir.join("pods/common.env").exists());
assert!(proj.output_dir.join("pods/overrides/test/common.env").exists());
proj.remove_test_output().unwrap();
}
#[test]
fn output_processes_pods_and_overrides() {
let proj = Project::from_example("hello").unwrap();
proj.output().unwrap();
assert!(proj.output_dir.join("pods/frontend.yml").exists());
assert!(proj.output_dir.join("pods/overrides/production/frontend.yml").exists());
assert!(proj.output_dir.join("pods/overrides/test/frontend.yml").exists());
proj.remove_test_output().unwrap();
}