buildable 0.0.2

Buildable trait definition and utilities helpful in build lifecycles
//! Defines the `BuildConfig` type, the `Buildable` trait, and the `Empty`
//! trait.
//!
//! # Examples
//! ```rust
//! use buildable::BuildConfig;
//!
//! let mut bc = BuildConfig::new();
//! bc.project("v8");
//! ```
#![experimental]
pub mod command;
pub mod scm;

/// `BuildConfig` type definition.
#[experimental]
#[deriving(Clone,Default)]
pub struct BuildConfig {
    dir: Option<Path>,
    project: Option<String>,
    branch: Option<String>,
    test: bool,
    lifecycle: Option<Vec<String>>,
}

impl BuildConfig {
    /// Create a new `BuildConfig` type.
    ///
    /// ```rust
    /// use buildable::BuildConfig;
    ///
    /// let bc = BuildConfig::new();
    /// ```
    pub fn new() -> BuildConfig {
        BuildConfig {
            dir: {
                let home = match std::os::getenv("HOME") {
                    Some(h) => h,
                    None    => panic!("No home environment variable set!"),
                };
                let path = Path::new(home);
                Some(path.join("projects"))
            },
            project: Some("rust".to_string()),
            branch: Some("master".to_string()),
            test: false,
            lifecycle: Some(vec!["most".to_string()]),
        }
    }

    /// Change the default base path for a `BuildConfig`.
    ///
    /// ```rust
    /// use buildable::BuildConfig;
    ///
    /// let mut bc = BuildConfig::new();
    /// bc.dir(Path::new("/tmp"));
    /// ```
    ///
    /// # Arguments
    /// * `dir` - The path you wish to set as the base for this `BuildConfig`.
    ///
    /// # Notes
    /// * The default base path is `$HOME/projects`.
    pub fn dir(&mut self, dir: Path) -> &mut BuildConfig {
        self.dir = Some(dir);
        self
    }

    /// Change the default project for a `BuildConfig`.
    ///
    /// ```rust
    /// use buildable::BuildConfig;
    ///
    /// let mut bc = BuildConfig::new();
    /// bc.project("cargo");
    /// ```
    ///
    /// # Arguments
    /// * `project` - The name of the project for this `BuildConfig`.
    ///
    /// # Notes
    /// * The default project is "rust".
    pub fn project(&mut self, project: &str) -> &mut BuildConfig {
        self.project = Some(project.to_string());
        self
    }

    /// Change the default branch for a `BuildConfig`.
    ///
    /// ```
    /// use buildable::BuildConfig;
    ///
    /// let mut bc = BuildConfig::new();
    /// bc.branch("my-feature");
    /// ```
    ///
    /// # Arguments
    /// * `branch` - The name of the branch for this `BuildConfig`.
    ///
    /// # Notes
    /// * The default branch is "master".
    pub fn branch(&mut self, branch: &str) -> &mut BuildConfig {
        self.branch = Some(branch.to_string());
        self
    }

    /// Enable/disable the test flag for a `BuildConfig`.
    ///
    /// ```rust
    /// use buildable::BuildConfig;
    ///
    /// let mut bc = BuildConfig::new();
    /// bc.test(true);
    /// ```
    ///
    /// # Arguments
    /// * `test` - true, enable tests. false, disable tests.
    ///
    /// # Notes
    /// * The default test flag is `false`.
    pub fn test(&mut self, test: bool) -> &mut BuildConfig {
        self.test = test;
        self
    }

    /// Change the default lifecycle for a `BuildConfig`.
    ///
    /// ```rust
    /// use buildable::BuildConfig;
    ///
    /// let mut bc = BuildConfig::new();
    /// bc.lifecycle(vec!["all"]);
    /// ```
    ///
    /// # Arguments
    /// * `lc` - A vector of lifecycle names.
    ///
    /// # Notes
    /// * The default lifecycle is `vec!["most"]`.
    pub fn lifecycle(&mut self, lc: Vec<&str>) -> &mut BuildConfig {
        let mut mylc = Vec::new();
        for work in lc.iter() {
            mylc.push(work.to_string());
        }
        self.lifecycle = Some(mylc);
        self
    }

    /// Get the base directory in this `BuildConfig`.
    pub fn get_dir<'a>(&'a self) -> &'a Path {
        match self.dir {
            Some(ref d) => d,
            None        => panic!("No directory set in BuildConfig!"),
        }
    }

    /// Get the project in this `BuildConfig`.
    pub fn get_project<'a>(&'a self) -> &'a str {
        match self.project {
            Some(ref p) => p.as_slice(),
            None        => panic!("No project set in BuildConfig!"),
        }
    }

    /// Get the branch in this `BuildConfig`.
    pub fn get_branch<'a>(&'a self) -> &'a str {
        match self.branch {
            Some(ref b) => b.as_slice(),
            None        => panic!("No branch set in BuildConfig!"),
        }
    }

    /// Get the test flag in this `BuildConfig`.
    pub fn get_test(&self) -> bool {
        self.test
    }

    /// Get the lifecycle vector in this `BuildConfig`.
    pub fn get_lifecycle(&self) -> &Vec<String> {
        match self.lifecycle {
            Some(ref lc) => lc,
            None         => panic!("No lifecycle set in BuildConfig!"),
        }
    }
}

/// `Buildable` trait definition.
///
/// The `Buildable` trait represents the lifecycle methods for most buildable
/// software.
#[experimental]
pub trait Buildable {
    fn new(&mut self, &Vec<String>) -> &mut Self;
    fn get_bc(&self) -> &BuildConfig;
    fn scm(&self) -> Result<u8,u8>;
    fn clean(&self) -> Result<u8,u8>;
    fn configure(&self) -> Result<u8,u8>;
    fn make(&self) -> Result<u8,u8>;
    fn test(&self) -> Result<u8,u8>;
    fn install(&self) -> Result<u8,u8>;
    fn cleanup(&self) -> Result<u8,u8>;
    fn help(&self) -> Result<u8,u8>;
    fn version(&self) -> Result<u8,u8>;
}

/// `Empty` trait definition.
///
/// The `Empty` trait wraps `is_empty` calls for various types.  Use with
/// `to_opt` to convert different types to `Option`.
///
/// ```rust
/// use buildable::to_opt;
///
/// let opt = to_opt("val");                 // Some("val")
/// let opt1 = to_opt("".to_string());     // None
/// let opt2 = to_opt(Vec::<&str>::new());   // None
/// let opt3 = to_opt(Vec::<String>::new()); // None
/// ```
#[experimental]
pub trait Empty {
    /// Return true if self is empty, false otherwise.
    fn empty(&self) -> bool;
}

/// `Empty` implementation for string slice.
#[experimental]
impl <'a>Empty for &'a str {
    fn empty(&self) -> bool {
        self.is_empty()
    }
}

/// `Empty` implementation for String.
#[experimental]
impl Empty for String {
    fn empty(&self) -> bool {
        self.as_slice().is_empty()
    }
}

/// `Empty` implementation for Vec<String>.
#[experimental]
impl Empty for Vec<String> {
    fn empty(&self) -> bool {
        self.is_empty()
    }
}

/// `Empty` implementation for Vec<&str>.
#[experimental]
impl <'a>Empty for Vec<&'a str> {
    fn empty(&self) -> bool {
        self.is_empty()
    }
}

/// Convert an `Empty` element of type `T` to an `Option` of type `T`.
///
///
/// ```rust
/// use buildable::to_opt;
///
/// let opt = to_opt("val"); // Some("val")
/// ```
///
/// # Arguments
/// * `arg` - The element to convert from an `Empty` to an `Option`.
#[experimental]
pub fn to_opt<T: Empty>(arg: T) -> Option<T> {
    if arg.empty() {
        None
    } else {
        Some(arg)
    }
}

#[cfg(test)]
mod test {
    use super::{BuildConfig,to_opt};

    fn proj_dir() -> String {
        let mut base = env!("HOME").to_string();
        base.push_str("/projects");
        base
    }

    #[test]
    fn test_new() {
        let bc = BuildConfig::new();
        assert_eq!(bc.get_dir().as_str().unwrap(), proj_dir().as_slice());
        assert_eq!(bc.get_project(), "rust");
        assert_eq!(bc.get_branch(), "master");
        assert!(!bc.get_test());
        assert_eq!(*bc.get_lifecycle(), vec!["most".to_string()]);
    }

    #[test]
    fn test_dir() {
        let mut bc = BuildConfig::new();
        bc.dir(Path::new("/tmp"));
        assert_eq!(bc.get_dir().as_str().unwrap(), "/tmp");
        assert_eq!(bc.get_project(), "rust");
        assert_eq!(bc.get_branch(), "master");
        assert!(!bc.get_test());
        assert_eq!(*bc.get_lifecycle(), vec!["most".to_string()]);
    }

    #[test]
    fn test_project() {
        let mut bc = BuildConfig::new();
        bc.project("cargo");
        assert_eq!(bc.get_dir().as_str().unwrap(), proj_dir().as_slice());
        assert_eq!(bc.get_project(), "cargo");
        assert_eq!(bc.get_branch(), "master");
        assert!(!bc.get_test());
        assert_eq!(*bc.get_lifecycle(), vec!["most".to_string()]);
    }

    #[test]
    fn test_branch() {
        let mut bc = BuildConfig::new();
        bc.branch("my-feature");
        assert_eq!(bc.get_dir().as_str().unwrap(), proj_dir().as_slice());
        assert_eq!(bc.get_project(), "rust");
        assert_eq!(bc.get_branch(), "my-feature");
        assert!(!bc.get_test());
        assert_eq!(*bc.get_lifecycle(), vec!["most".to_string()]);
    }

    #[test]
    fn test_test_flag() {
        let mut bc = BuildConfig::new();
        bc.test(true);
        assert_eq!(bc.get_dir().as_str().unwrap(), proj_dir().as_slice());
        assert_eq!(bc.get_project(), "rust");
        assert_eq!(bc.get_branch(), "master");
        assert!(bc.get_test());
        assert_eq!(*bc.get_lifecycle(), vec!["most".to_string()]);
    }

    #[test]
    fn test_lifecycle() {
        let mut bc = BuildConfig::new();
        bc.lifecycle(vec!["all"]);
        assert_eq!(bc.get_dir().as_str().unwrap(), proj_dir().as_slice());
        assert_eq!(bc.get_project(), "rust");
        assert_eq!(bc.get_branch(), "master");
        assert!(!bc.get_test());
        assert_eq!(*bc.get_lifecycle(), vec!["all".to_string()]);
    }

    #[test]
    fn test_to_opt() {
        assert_eq!(to_opt(""), None);
        assert_eq!(to_opt("val"), Some("val"));
        assert_eq!(to_opt("".to_string()), None);
        assert_eq!(to_opt("val".to_string()), Some("val".to_string()));
        assert_eq!(to_opt(Vec::<&str>::new()), None);
        assert_eq!(to_opt(vec!["a"]), Some(vec!["a"]));
        assert_eq!(to_opt(Vec::<String>::new()), None);
        assert_eq!(to_opt(vec!["a".to_string()]),
                   Some(vec!["a".to_string()]));
    }
}