<commit_before>use std::io::{self, ErrorKind};
use std::fs::{File, Metadata, read_link};
use std::path::{Path, PathBuf};
use std::os::unix::fs::{PermissionsExt, MetadataExt};
use std::collections::{BTreeMap, BTreeSet, HashSet};

use libc::{uid_t, gid_t};
use quire::ast::{Ast, Tag};
use quire::validate as V;
use regex::{Regex, Error as RegexError};
use scan_dir::{ScanDir, Error as ScanDirError};

use container::root::temporary_change_root;
use file_util::shallow_copy;
use path_util::IterSelfAndParents;
use build_step::{BuildStep, VersionError, StepError, Digest, Config, Guard};
use quick_error::ResultExt;

const DEFAULT_UMASK: u32 = 0o002;

const DIR_MODE: u32 = 0o777;
const FILE_MODE: u32 = 0o666;
const EXE_FILE_MODE: u32 = 0o777;
const EXE_CHECK_MASK: u32 = 0o100;

#[derive(RustcDecodable, Debug)]
pub struct Depends {
    pub path: PathBuf,
    pub ignore_regex: String,
    pub include_regex: Option<String>,
}

fn depends_parser(ast: Ast) -> BTreeMap<String, Ast> {
    match ast {
        Ast::Scalar(pos, _, style, value) => {
            let mut map = BTreeMap::new();
            map.insert("path".to_string(),
                Ast::Scalar(pos.clone(), Tag::NonSpecific, style, value));
            map
        },
        _ => unreachable!(),
    }
}

impl Depends {
    pub fn config() -> V::Structure<'static> {
        V::Structure::new()
            .member("path", V::Scalar::new())
            .member("ignore_regex", V::Scalar::new().default(
                r#"(^|/)\.(git|hg|svn|vagga)($|/)|~$|\.bak$|\.orig$|^#.*#$"#))
            .member("include_regex", V::Scalar::new().optional())
            .parser(depends_parser)
    }
}

impl BuildStep for Depends {
    fn name(&self) -> &'static str { "Depends" }
    fn hash(&self, _cfg: &Config, hash: &mut Digest)
        -> Result<(), VersionError>
    {
        let path = Path::new("/work").join(&self.path);
        let filter = Filter::new(
            &self.ignore_regex, &self.include_regex)?;
        hash_path(hash, &path, &filter, |h, p, st| {
            h.field("filename", p);
            // We hash only executable flag for files
            // as mode depends on the host system umask
            if st.is_file() {
                let mode = st.permissions().mode();
                let is_executable = mode & EXE_CHECK_MASK > 0;
                h.field("is_executable", is_executable);
            }
            hash_file_content(h, p, st)
                .map_err(|e| VersionError::Io(e, PathBuf::from(p)))?;
            Ok(())
        })?;
        Ok(())
    }
    fn build(&self, _guard: &mut Guard, _build: bool)
        -> Result<(), StepError>
    {
        Ok(())
    }
    fn is_dependent_on(&self) -> Option<&str> {
        None
    }
}

#[derive(RustcDecodable, Debug)]
pub struct Copy {
    pub source: PathBuf,
    pub path: PathBuf,
    pub owner_uid: Option<uid_t>,
    pub owner_gid: Option<gid_t>,
    pub umask: u32,
    pub preserve_permissions: bool,
    pub ignore_regex: String,
    pub include_regex: Option<String>,
}

impl Copy {
    pub fn config() -> V::Structure<'static> {
        V::Structure::new()
        .member("source", V::Scalar::new())
        .member("path", V::Directory::new().absolute(true))
        .member("ignore_regex", V::Scalar::new().default(
            r#"(^|/)\.(git|hg|svn|vagga)($|/)|~$|\.bak$|\.orig$|^#.*#$"#))
        .member("include_regex", V::Scalar::new().optional())
        .member("owner_uid", V::Numeric::new().min(0).optional())
        .member("owner_gid", V::Numeric::new().min(0).optional())
        .member("umask", V::Numeric::new().min(0).max(0o777).default(
            DEFAULT_UMASK as i64))
        .member("preserve_permissions", V::Scalar::new().default(false))
    }

    fn calc_mode(&self, stat: &Metadata) -> Option<u32> {
        if stat.file_type().is_symlink() {
            // ignore as we do not set permissions for symlinks
            return None;
        }
        if self.preserve_permissions {
            Some(stat.permissions().mode())
        } else {
            let base_mode = if stat.is_dir() {
                DIR_MODE
            } else {
                if stat.permissions().mode() & EXE_CHECK_MASK > 0 {
                    EXE_FILE_MODE
                } else {
                    FILE_MODE
                }
            };
            Some(base_mode & !self.umask)
        }
    }
}

impl BuildStep for Copy {
    fn name(&self) -> &'static str { "Copy" }
    fn hash(&self, _cfg: &Config, hash: &mut Digest)
        -> Result<(), VersionError>
    {
        let ref src = self.source;
        if src.starts_with("/work") {
            let filter = Filter::new(
                &self.ignore_regex, &self.include_regex)?;
            hash_path(hash, src, &filter, |h, p, st| {
                h.field("filename", p);
                if let Some(mode) = self.calc_mode(st) {
                    h.field("mode", mode);
                }
                h.field("uid", self.owner_uid.unwrap_or(st.uid()));
                h.field("gid", self.owner_gid.unwrap_or(st.gid()));
                hash_file_content(h, p, st)
                    .map_err(|e| VersionError::Io(e, PathBuf::from(p)))?;
                Ok(())
            })?;
            hash.field("path", &self.path);
        } else {
            // We don't version the files outside of the /work because
            // we believe they are result of the commands run above
            //
            // And we need already built container to version the files
            // inside the container which is ugly
            hash.field("source", src);
            hash.field("path", &self.path);
            if !self.preserve_permissions {
                if let Some(uid) = self.owner_uid {
                    hash.field("owner_uid", uid);
                }
                if let Some(gid) = self.owner_gid {
                    hash.field("owner_gid", gid);
                }
                hash.field("umask", self.umask);
            }
            hash.field("preserve_permissions", self.preserve_permissions);
        }
        Ok(())
    }
    fn build(&self, _guard: &mut Guard, build: bool)
        -> Result<(), StepError>
    {
        if !build {
            return Ok(());
        }
        temporary_change_root("/vagga/root", || {
            let ref src = self.source;
            let ref dest = self.path;
            let ref typ = src.symlink_metadata()
                .map_err(|e| StepError::Read(src.into(), e))?;
            if typ.is_dir() {
                shallow_copy(src, typ, dest,
                        self.owner_uid, self.owner_gid,
                        self.calc_mode(typ))
                    .context((src, dest))?;
                let filter = Filter::new(&self.ignore_regex, &self.include_regex)?;
                let mut processed_paths = HashSet::new();
                ScanDir::all().walk(src, |iter| {
                    for (entry, _) in iter {
                        let fpath = entry.path();
                        // We know that directory is inside
                        // the source
                        let path = fpath.strip_prefix(src).unwrap();
                        // We know that it's decodable
                        let strpath = path.to_str().unwrap();
                        if filter.is_match(strpath) {
                            let parents: Vec<_> = path
                                .iter_self_and_parents()
                                .take_while(|p| !processed_paths.contains(*p))
                                .collect();
                            for parent in parents.iter().rev() {
                                let ref fdest = dest.join(parent);
                                let ref fsrc = src.join(parent);
                                let ref fsrc_stat = fsrc.symlink_metadata()
                                    .map_err(|e| StepError::Read(src.into(), e))?;
                                shallow_copy(fsrc, fsrc_stat, fdest,
                                        self.owner_uid, self.owner_gid,
                                        self.calc_mode(fsrc_stat))
                                     .context((fsrc, fdest))?;
                                processed_paths.insert(PathBuf::from(parent));
                            }
                        }
                    }
                    Ok(())
                }).map_err(StepError::ScanDir).and_then(|x| x)?;
            } else {
                shallow_copy(src, typ, dest,
                        self.owner_uid, self.owner_gid,
                        self.calc_mode(typ))
                    .context((src, dest))?;
            }
            Ok(())
        })
    }
    fn is_dependent_on(&self) -> Option<&str> {
        None
    }
}

fn hash_path<F>(hash: &mut Digest, path: &Path, filter: &Filter, hash_file: F)
    -> Result<(), VersionError>
    where F: Fn(&mut Digest, &Path, &Metadata) -> Result<(), VersionError>
{
    match path.symlink_metadata() {
        Ok(ref meta) if meta.file_type().is_dir() => {
            hash_file(hash, path, meta)?;
            let all_rel_paths = get_sorted_rel_paths(path, &filter)?;
            for rel_path in &all_rel_paths {
                let ref abs_path = path.join(rel_path);
                let stat = abs_path.symlink_metadata()
                    .map_err(|e| VersionError::Io(e, PathBuf::from(abs_path)))?;
                hash_file(hash, abs_path, &stat)?;
            }
        }
        Ok(ref meta) => {
            hash_file(hash, path, meta)?;
        }
        Err(ref e) if e.kind() == ErrorKind::NotFound => {
            return Err(VersionError::New);
        }
        Err(e) => {
            return Err(VersionError::Io(e, path.into()));
        }
    }
    Ok(())
}

fn get_sorted_rel_paths(path: &Path, filter: &Filter)
    -> Result<BTreeSet<PathBuf>, Vec<ScanDirError>>
{
    ScanDir::all().walk(path, |iter| {
        let mut all_rel_paths = BTreeSet::new();
        for (entry, _) in iter {
            let fpath = entry.path();
            // We know that directory is inside
            // the path
            let rel_path = fpath.strip_prefix(path).unwrap();
            // We know that rel_path is decodable
            let strpath = rel_path.to_str().unwrap();
            if filter.is_match(strpath) {
                for parent in rel_path.iter_self_and_parents() {
                    if !all_rel_paths.contains(parent) {
                        all_rel_paths.insert(PathBuf::from(parent));
                    }
                }
            }
        }
        all_rel_paths
    })
}

fn hash_file_content(hash: &mut Digest, path: &Path, stat: &Metadata)
    -> Result<(), io::Error>
{
    if stat.file_type().is_file() {
        let mut file = File::open(&path)?;
        hash.file(&path, &mut file)?;
    } else if stat.file_type().is_symlink() {
        let data = read_link(path)?;
        hash.field("symlink", data);
    }
    Ok(())
}

struct Filter {
    ignore_re: Option<Regex>,
    include_re: Option<Regex>,
}

impl Filter {
    fn new(ignore_regex: &String, include_regex: &Option<String>)
        -> Result<Filter, RegexError>
    {
        Ok(Filter {
            ignore_re: Some(Regex::new(ignore_regex)?),
            include_re: match *include_regex {
                Some(ref include_regex) => {
                    Some(Regex::new(include_regex)?)
                },
                None => None,
            },
        })
    }

    fn is_match(&self, s: &str) -> bool
    {
        self.ignore_re.as_ref().map_or(true, |r| !r.is_match(s))
            && self.include_re.as_ref().map_or(true, |r| r.is_match(s))
    }
}
<commit_msg>Simplified hashing code in Copy<commit_after>use std::io::{self, ErrorKind};
use std::fs::{File, Metadata, read_link};
use std::path::{Path, PathBuf};
use std::os::unix::fs::{PermissionsExt, MetadataExt};
use std::collections::{BTreeMap, BTreeSet, HashSet};

use libc::{uid_t, gid_t};
use quire::ast::{Ast, Tag};
use quire::validate as V;
use regex::{Regex, Error as RegexError};
use scan_dir::{ScanDir, Error as ScanDirError};

use container::root::temporary_change_root;
use file_util::shallow_copy;
use path_util::IterSelfAndParents;
use build_step::{BuildStep, VersionError, StepError, Digest, Config, Guard};
use quick_error::ResultExt;

const DEFAULT_UMASK: u32 = 0o002;

const DIR_MODE: u32 = 0o777;
const FILE_MODE: u32 = 0o666;
const EXE_FILE_MODE: u32 = 0o777;
const EXE_CHECK_MASK: u32 = 0o100;

#[derive(RustcDecodable, Debug)]
pub struct Depends {
    pub path: PathBuf,
    pub ignore_regex: String,
    pub include_regex: Option<String>,
}

fn depends_parser(ast: Ast) -> BTreeMap<String, Ast> {
    match ast {
        Ast::Scalar(pos, _, style, value) => {
            let mut map = BTreeMap::new();
            map.insert("path".to_string(),
                Ast::Scalar(pos.clone(), Tag::NonSpecific, style, value));
            map
        },
        _ => unreachable!(),
    }
}

impl Depends {
    pub fn config() -> V::Structure<'static> {
        V::Structure::new()
            .member("path", V::Scalar::new())
            .member("ignore_regex", V::Scalar::new().default(
                r#"(^|/)\.(git|hg|svn|vagga)($|/)|~$|\.bak$|\.orig$|^#.*#$"#))
            .member("include_regex", V::Scalar::new().optional())
            .parser(depends_parser)
    }
}

impl BuildStep for Depends {
    fn name(&self) -> &'static str { "Depends" }
    fn hash(&self, _cfg: &Config, hash: &mut Digest)
        -> Result<(), VersionError>
    {
        let path = Path::new("/work").join(&self.path);
        let filter = Filter::new(
            &self.ignore_regex, &self.include_regex)?;
        hash_path(hash, &path, &filter, |h, p, st| {
            h.field("filename", p);
            // We hash only executable flag for files
            // as mode depends on the host system umask
            if st.is_file() {
                let mode = st.permissions().mode();
                let is_executable = mode & EXE_CHECK_MASK > 0;
                h.field("is_executable", is_executable);
            }
            hash_file_content(h, p, st)
                .map_err(|e| VersionError::Io(e, PathBuf::from(p)))?;
            Ok(())
        })?;
        Ok(())
    }
    fn build(&self, _guard: &mut Guard, _build: bool)
        -> Result<(), StepError>
    {
        Ok(())
    }
    fn is_dependent_on(&self) -> Option<&str> {
        None
    }
}

#[derive(RustcDecodable, Debug)]
pub struct Copy {
    pub source: PathBuf,
    pub path: PathBuf,
    pub owner_uid: Option<uid_t>,
    pub owner_gid: Option<gid_t>,
    pub umask: u32,
    pub preserve_permissions: bool,
    pub ignore_regex: String,
    pub include_regex: Option<String>,
}

impl Copy {
    pub fn config() -> V::Structure<'static> {
        V::Structure::new()
        .member("source", V::Scalar::new())
        .member("path", V::Directory::new().absolute(true))
        .member("ignore_regex", V::Scalar::new().default(
            r#"(^|/)\.(git|hg|svn|vagga)($|/)|~$|\.bak$|\.orig$|^#.*#$"#))
        .member("include_regex", V::Scalar::new().optional())
        .member("owner_uid", V::Numeric::new().min(0).optional())
        .member("owner_gid", V::Numeric::new().min(0).optional())
        .member("umask", V::Numeric::new().min(0).max(0o777).default(
            DEFAULT_UMASK as i64))
        .member("preserve_permissions", V::Scalar::new().default(false))
    }

    fn calc_mode(&self, stat: &Metadata) -> Option<u32> {
        if stat.file_type().is_symlink() {
            // ignore as we do not set permissions for symlinks
            return None;
        }
        if self.preserve_permissions {
            Some(stat.permissions().mode())
        } else {
            let base_mode = if stat.is_dir() {
                DIR_MODE
            } else {
                if stat.permissions().mode() & EXE_CHECK_MASK > 0 {
                    EXE_FILE_MODE
                } else {
                    FILE_MODE
                }
            };
            Some(base_mode & !self.umask)
        }
    }
}

impl BuildStep for Copy {
    fn name(&self) -> &'static str { "Copy" }
    fn hash(&self, _cfg: &Config, hash: &mut Digest)
        -> Result<(), VersionError>
    {
        let ref src = self.source;
        if src.starts_with("/work") {
            let filter = Filter::new(
                &self.ignore_regex, &self.include_regex)?;
            hash_path(hash, src, &filter, |h, p, st| {
                h.field("filename", p);
                h.opt_field("mode", &self.calc_mode(st));
                h.field("uid", self.owner_uid.unwrap_or(st.uid()));
                h.field("gid", self.owner_gid.unwrap_or(st.gid()));
                hash_file_content(h, p, st)
                    .map_err(|e| VersionError::Io(e, PathBuf::from(p)))?;
                Ok(())
            })?;
            hash.field("path", &self.path);
        } else {
            // We don't version the files outside of the /work because
            // we believe they are result of the commands run above
            //
            // And we need already built container to version the files
            // inside the container which is ugly
            hash.field("source", src);
            hash.field("path", &self.path);
            hash.field("preserve_permissions", self.preserve_permissions);
            if !self.preserve_permissions {
                hash.opt_field("owner_uid", &self.owner_uid);
                hash.opt_field("owner_gid", &self.owner_gid);
                hash.field("umask", self.umask);
            }
        }
        Ok(())
    }
    fn build(&self, _guard: &mut Guard, build: bool)
        -> Result<(), StepError>
    {
        if !build {
            return Ok(());
        }
        temporary_change_root("/vagga/root", || {
            let ref src = self.source;
            let ref dest = self.path;
            let ref typ = src.symlink_metadata()
                .map_err(|e| StepError::Read(src.into(), e))?;
            if typ.is_dir() {
                shallow_copy(src, typ, dest,
                        self.owner_uid, self.owner_gid,
                        self.calc_mode(typ))
                    .context((src, dest))?;
                let filter = Filter::new(&self.ignore_regex, &self.include_regex)?;
                let mut processed_paths = HashSet::new();
                ScanDir::all().walk(src, |iter| {
                    for (entry, _) in iter {
                        let fpath = entry.path();
                        // We know that directory is inside
                        // the source
                        let path = fpath.strip_prefix(src).unwrap();
                        // We know that it's decodable
                        let strpath = path.to_str().unwrap();
                        if filter.is_match(strpath) {
                            let parents: Vec<_> = path
                                .iter_self_and_parents()
                                .take_while(|p| !processed_paths.contains(*p))
                                .collect();
                            for parent in parents.iter().rev() {
                                let ref fdest = dest.join(parent);
                                let ref fsrc = src.join(parent);
                                let ref fsrc_stat = fsrc.symlink_metadata()
                                    .map_err(|e| StepError::Read(src.into(), e))?;
                                shallow_copy(fsrc, fsrc_stat, fdest,
                                        self.owner_uid, self.owner_gid,
                                        self.calc_mode(fsrc_stat))
                                     .context((fsrc, fdest))?;
                                processed_paths.insert(PathBuf::from(parent));
                            }
                        }
                    }
                    Ok(())
                }).map_err(StepError::ScanDir).and_then(|x| x)?;
            } else {
                shallow_copy(src, typ, dest,
                        self.owner_uid, self.owner_gid,
                        self.calc_mode(typ))
                    .context((src, dest))?;
            }
            Ok(())
        })
    }
    fn is_dependent_on(&self) -> Option<&str> {
        None
    }
}

fn hash_path<F>(hash: &mut Digest, path: &Path, filter: &Filter, hash_file: F)
    -> Result<(), VersionError>
    where F: Fn(&mut Digest, &Path, &Metadata) -> Result<(), VersionError>
{
    match path.symlink_metadata() {
        Ok(ref meta) if meta.file_type().is_dir() => {
            hash_file(hash, path, meta)?;
            let all_rel_paths = get_sorted_rel_paths(path, &filter)?;
            for rel_path in &all_rel_paths {
                let ref abs_path = path.join(rel_path);
                let stat = abs_path.symlink_metadata()
                    .map_err(|e| VersionError::Io(e, PathBuf::from(abs_path)))?;
                hash_file(hash, abs_path, &stat)?;
            }
        }
        Ok(ref meta) => {
            hash_file(hash, path, meta)?;
        }
        Err(ref e) if e.kind() == ErrorKind::NotFound => {
            return Err(VersionError::New);
        }
        Err(e) => {
            return Err(VersionError::Io(e, path.into()));
        }
    }
    Ok(())
}

fn get_sorted_rel_paths(path: &Path, filter: &Filter)
    -> Result<BTreeSet<PathBuf>, Vec<ScanDirError>>
{
    ScanDir::all().walk(path, |iter| {
        let mut all_rel_paths = BTreeSet::new();
        for (entry, _) in iter {
            let fpath = entry.path();
            // We know that directory is inside
            // the path
            let rel_path = fpath.strip_prefix(path).unwrap();
            // We know that rel_path is decodable
            let strpath = rel_path.to_str().unwrap();
            if filter.is_match(strpath) {
                for parent in rel_path.iter_self_and_parents() {
                    if !all_rel_paths.contains(parent) {
                        all_rel_paths.insert(PathBuf::from(parent));
                    }
                }
            }
        }
        all_rel_paths
    })
}

fn hash_file_content(hash: &mut Digest, path: &Path, stat: &Metadata)
    -> Result<(), io::Error>
{
    if stat.file_type().is_file() {
        let mut file = File::open(&path)?;
        hash.file(&path, &mut file)?;
    } else if stat.file_type().is_symlink() {
        let data = read_link(path)?;
        hash.field("symlink", data);
    }
    Ok(())
}

struct Filter {
    ignore_re: Option<Regex>,
    include_re: Option<Regex>,
}

impl Filter {
    fn new(ignore_regex: &String, include_regex: &Option<String>)
        -> Result<Filter, RegexError>
    {
        Ok(Filter {
            ignore_re: Some(Regex::new(ignore_regex)?),
            include_re: match *include_regex {
                Some(ref include_regex) => {
                    Some(Regex::new(include_regex)?)
                },
                None => None,
            },
        })
    }

    fn is_match(&self, s: &str) -> bool
    {
        self.ignore_re.as_ref().map_or(true, |r| !r.is_match(s))
            && self.include_re.as_ref().map_or(true, |r| r.is_match(s))
    }
}
<|endoftext|>


<commit_before>//! Lua thread test.
//! Spawns a lua thread!

use std::time::Duration;
use std::thread;
use std::collections::BTreeMap;

use hlua::any::AnyLuaValue::*;
use hlua::LuaError;
use rustc_serialize::json::{Json, ToJson, Object};

use super::*;

fn wait_for_thread() {
    for i in 0..20 {
        if !thread_running() {
            thread::sleep_ms(200);
        }
        else { return; }
    }
    panic!("Thread didn't run!");
}

#[test]
fn activate_thread() {
    super::init();
}

#[test]
fn thread_exec_okay() {
    wait_for_thread();

    let hello_receiver = send(LuaQuery::Execute(
        "hello = 'hello world'".to_string())).unwrap();
    let hello_result = hello_receiver.recv().unwrap();
    assert!(hello_result.is_ok());
    assert_eq!(hello_result, LuaResponse::Pong);

    let assert_receiver = send(LuaQuery::Execute(
        "assert(hello == 'hello world')".to_string())).unwrap();
    let assert_result = assert_receiver.recv().unwrap();
    assert!(hello_result.is_ok());
    assert_eq!(assert_result, LuaResponse::Pong);
}

#[test]
fn thread_exec_err() {
    wait_for_thread();

    let assert_receiver = send(LuaQuery::Execute(
        "assert(true == false, 'Logic works')".to_string())).unwrap();
    let assert_result = assert_receiver.recv().unwrap();
    assert!(assert_result.is_err());
    assert!(!assert_result.is_ok());
    if let LuaResponse::Error(err) = assert_result {
        if let LuaError::ExecutionError(lua_err) = err {
            assert!(lua_err.contains("Logic works"));
        }
        else {
            panic!("thread_exec_err result was not an ExecutionError: {:?}",
                   err);
        }
    }
    else {
        panic!("thread_exec_err result was not an error: {:?}", assert_result);
    }
}

#[test]
fn thread_exec_file_ok() {
    wait_for_thread();

    let file_receiver = send(LuaQuery::ExecFile(
        "lib/test/lua-exec-file.lua".to_string())).unwrap();
    let file_result = file_receiver.recv().unwrap();
    if let LuaResponse::Error(err) = file_result {
        if let LuaError::ReadError(ioerr) = err {
            panic!("Lua thread was unable to execute file: {:?}", ioerr);
        }
        else {
            panic!("Unexpected error executing file: {:?}", err);
        }
    }
    assert!(file_result.is_ok());
    assert_eq!(file_result, LuaResponse::Pong);

    // Print the method from the file
    let test_receiver = send(LuaQuery::Execute(
        "foo = confirm_file()".to_string())).unwrap();
    let test_result = test_receiver.recv().unwrap();
    assert!(test_result.is_ok());
    assert!(test_result == LuaResponse::Pong);
}

#[test]
fn thread_exec_file_err() {
    wait_for_thread();

    let run_receiver = send(LuaQuery::ExecFile(
        "lib/test/lua-bad-assert.lua".to_string())).unwrap();
    let run_result = run_receiver.recv().unwrap();
    assert!(run_result.is_err());
    match run_result {
        LuaResponse::Error(err) => {
            match err {
                LuaError::ExecutionError(ex) => {
                    assert!(ex.contains("Dude, 1 is totally equal to 0"));
                },
                _ => panic!("Got wrong hlua error type: {:?}", err)
            }
        },
        _ => panic!("Got wrong LuaResponse type: {:?}", run_result)
    }

    let syntax_receiver = send(LuaQuery::ExecFile(
        "lib/test/lua-syntax-err.txt".to_string())).unwrap();
    let syntax_result = syntax_receiver.recv().unwrap();
    assert!(syntax_result.is_err());
    match syntax_result {
        LuaResponse::Error(err) => {
            match err {
                LuaError::SyntaxError(serr) => {
                    assert!(serr.contains("expected"));
                },
                _ => panic!("Got wrong hlua error type: {:?}", err)
            }
        }
        _ => panic!("Got wrong LuaResponse type: {:?}", syntax_result)
    }
}

#[test]
fn convert_string() {
    let input = "Hello World".to_string();
    let json_input = Json::String(input.clone());
    assert_eq!(json_to_lua(json_input), LuaString(input));
}
#[test]
fn convert_numbers() {
    let lua_number = LuaNumber(42f64);
    assert_eq!(json_to_lua(Json::F64(42f64)), lua_number);
    assert_eq!(json_to_lua(Json::I64(42i64)), lua_number);
    assert_eq!(json_to_lua(Json::U64(42u64)), lua_number);
}

#[test]
fn convert_bool() {
    assert_eq!(json_to_lua(Json::Boolean(true)), LuaBoolean(true));
    assert_eq!(json_to_lua(Json::Boolean(false)), LuaBoolean(false));
}

#[test]
fn convert_array() {
    let json = Json::Array(
        vec![Json::Boolean(true), Json::F64(11f64),
             Json::String("Foo".to_string())]
    );
    let lua = LuaArray(vec![
        (LuaNumber(1f64), LuaBoolean(true)),
        (LuaNumber(2f64), LuaNumber(11f64)),
        (LuaNumber(3f64), LuaString("Foo".to_string()))
    ]);
    assert_eq!(json_to_lua(json), lua);
}

#[test]
fn convert_object() {
    // { name="foo", point: {x=0, y=1}, valid=false }
    // The fields are emitted in alphabetical order thanks to Json's
    // internal BTreeMap representation.

    let mut json_tree: Object = BTreeMap::new();
    let mut point_tree: Object = BTreeMap::new();
    point_tree.insert("x".to_string(), Json::U64(0u64));
    point_tree.insert("y".to_string(), Json::U64(1u64));
    json_tree.insert("point".to_string(), Json::Object(point_tree));
    json_tree.insert("name".to_string(), Json::String("foo".to_string()));
    json_tree.insert("valid".to_string(), Json::Boolean(false));

    let lua = LuaArray(vec![
        (LuaString("name".to_string()), LuaString("foo".to_string())),
        (LuaString("point".to_string()), LuaArray(vec![
            (LuaString("x".to_string()), LuaNumber(0f64)),
            (LuaString("y".to_string()), LuaNumber(1f64))
        ])),
        (LuaString("valid".to_string()), LuaBoolean(false)),
    ]);

    assert_eq!(json_to_lua(Json::Object(json_tree)), lua);
}
<commit_msg>Add Rust function for tests<commit_after>//! Lua thread test.
//! Spawns a lua thread!

use std::time::Duration;
use std::thread;
use std::collections::BTreeMap;

use hlua::any::AnyLuaValue;
use hlua::any::AnyLuaValue::*;
use hlua::{Lua, LuaError};
use rustc_serialize::json::{Json, ToJson, Object};

use super::*;

fn wait_for_thread() {
    for i in 0..20 {
        if !thread_running() {
            thread::sleep_ms(200);
        }
        else { return; }
    }
    panic!("Thread didn't run!");
}

#[test]
fn activate_thread() {
    super::init();
}

#[test]
fn thread_exec_okay() {
    wait_for_thread();

    let hello_receiver = send(LuaQuery::Execute(
        "hello = 'hello world'".to_string())).unwrap();
    let hello_result = hello_receiver.recv().unwrap();
    assert!(hello_result.is_ok());
    assert_eq!(hello_result, LuaResponse::Pong);

    let assert_receiver = send(LuaQuery::Execute(
        "assert(hello == 'hello world')".to_string())).unwrap();
    let assert_result = assert_receiver.recv().unwrap();
    assert!(hello_result.is_ok());
    assert_eq!(assert_result, LuaResponse::Pong);
}

#[test]
fn thread_exec_err() {
    wait_for_thread();

    let assert_receiver = send(LuaQuery::Execute(
        "assert(true == false, 'Logic works')".to_string())).unwrap();
    let assert_result = assert_receiver.recv().unwrap();
    assert!(assert_result.is_err());
    assert!(!assert_result.is_ok());
    if let LuaResponse::Error(err) = assert_result {
        if let LuaError::ExecutionError(lua_err) = err {
            assert!(lua_err.contains("Logic works"));
        }
        else {
            panic!("thread_exec_err result was not an ExecutionError: {:?}",
                   err);
        }
    }
    else {
        panic!("thread_exec_err result was not an error: {:?}", assert_result);
    }
}

#[test]
fn thread_exec_file_ok() {
    wait_for_thread();

    let file_receiver = send(LuaQuery::ExecFile(
        "lib/test/lua-exec-file.lua".to_string())).unwrap();
    let file_result = file_receiver.recv().unwrap();
    if let LuaResponse::Error(err) = file_result {
        if let LuaError::ReadError(ioerr) = err {
            panic!("Lua thread was unable to execute file: {:?}", ioerr);
        }
        else {
            panic!("Unexpected error executing file: {:?}", err);
        }
    }
    assert!(file_result.is_ok());
    assert_eq!(file_result, LuaResponse::Pong);

    // Print the method from the file
    let test_receiver = send(LuaQuery::Execute(
        "confirm_file()".to_string())).unwrap();
    let test_result = test_receiver.recv().unwrap();
    assert!(test_result.is_ok());
    assert!(test_result == LuaResponse::Pong);
}

#[test]
fn thread_exec_file_err() {
    wait_for_thread();

    let run_receiver = send(LuaQuery::ExecFile(
        "lib/test/lua-bad-assert.lua".to_string())).unwrap();
    let run_result = run_receiver.recv().unwrap();
    assert!(run_result.is_err());
    match run_result {
        LuaResponse::Error(err) => {
            match err {
                LuaError::ExecutionError(ex) => {
                    assert!(ex.contains("Dude, 1 is totally equal to 0"));
                },
                _ => panic!("Got wrong hlua error type: {:?}", err)
            }
        },
        _ => panic!("Got wrong LuaResponse type: {:?}", run_result)
    }

    let syntax_receiver = send(LuaQuery::ExecFile(
        "lib/test/lua-syntax-err.txt".to_string())).unwrap();
    let syntax_result = syntax_receiver.recv().unwrap();
    assert!(syntax_result.is_err());
    match syntax_result {
        LuaResponse::Error(err) => {
            match err {
                LuaError::SyntaxError(serr) => {
                    assert!(serr.contains("expected"));
                },
                _ => panic!("Got wrong hlua error type: {:?}", err)
            }
        }
        _ => panic!("Got wrong LuaResponse type: {:?}", syntax_result)
    }
}

fn test_rust_exec() {
    wait_for_thread();
}

fn rust_lua_fn(lua: &mut Lua) -> AnyLuaValue {
    {
        let mut foo = lua.empty_array("foo");
        foo.set("bar", 12);
    }
    match lua.get::<AnyLuaValue, _>("foo") {
        Some(LuaArray(arr)) => AnyLuaValue::LuaBoolean(true),
        _ => AnyLuaValue::LuaBoolean(false)
    }
}

#[test]
fn convert_string() {
    let input = "Hello World".to_string();
    let json_input = Json::String(input.clone());
    assert_eq!(json_to_lua(json_input), LuaString(input));
}
#[test]
fn convert_numbers() {
    let lua_number = LuaNumber(42f64);
    assert_eq!(json_to_lua(Json::F64(42f64)), lua_number);
    assert_eq!(json_to_lua(Json::I64(42i64)), lua_number);
    assert_eq!(json_to_lua(Json::U64(42u64)), lua_number);
}

#[test]
fn convert_bool() {
    assert_eq!(json_to_lua(Json::Boolean(true)), LuaBoolean(true));
    assert_eq!(json_to_lua(Json::Boolean(false)), LuaBoolean(false));
}

#[test]
fn convert_array() {
    let json = Json::Array(
        vec![Json::Boolean(true), Json::F64(11f64),
             Json::String("Foo".to_string())]
    );
    let lua = LuaArray(vec![
        (LuaNumber(1f64), LuaBoolean(true)),
        (LuaNumber(2f64), LuaNumber(11f64)),
        (LuaNumber(3f64), LuaString("Foo".to_string()))
    ]);
    assert_eq!(json_to_lua(json), lua);
}

#[test]
fn convert_object() {
    // { name="foo", point: {x=0, y=1}, valid=false }
    // The fields are emitted in alphabetical order thanks to Json's
    // internal BTreeMap representation.

    let mut json_tree: Object = BTreeMap::new();
    let mut point_tree: Object = BTreeMap::new();
    point_tree.insert("x".to_string(), Json::U64(0u64));
    point_tree.insert("y".to_string(), Json::U64(1u64));
    json_tree.insert("point".to_string(), Json::Object(point_tree));
    json_tree.insert("name".to_string(), Json::String("foo".to_string()));
    json_tree.insert("valid".to_string(), Json::Boolean(false));

    let lua = LuaArray(vec![
        (LuaString("name".to_string()), LuaString("foo".to_string())),
        (LuaString("point".to_string()), LuaArray(vec![
            (LuaString("x".to_string()), LuaNumber(0f64)),
            (LuaString("y".to_string()), LuaNumber(1f64))
        ])),
        (LuaString("valid".to_string()), LuaBoolean(false)),
    ]);

    assert_eq!(json_to_lua(Json::Object(json_tree)), lua);
}
<|endoftext|>


<commit_before>use semver;
use url::Url;
use std::hash::Hash;
use std::fmt;
use std::fmt::{Show,Formatter};
use collections::hash;
use serialize::{
    Encodable,
    Encoder,
    Decodable,
    Decoder
};

use util::{CargoResult, CargoError, short_hash};
use core::source::SourceId;

trait ToVersion {
    fn to_version(self) -> Result<semver::Version, String>;
}

impl ToVersion for semver::Version {
    fn to_version(self) -> Result<semver::Version, String> {
        Ok(self)
    }
}

impl<'a> ToVersion for &'a str {
    fn to_version(self) -> Result<semver::Version, String> {
        match semver::parse(self) {
            Some(v) => Ok(v),
            None => Err(format!("cannot parse '{}' as a semver", self)),
        }
    }
}

trait ToUrl {
    fn to_url(self) -> Result<Url, String>;
}

impl<'a> ToUrl for &'a str {
    fn to_url(self) -> Result<Url, String> {
        Url::parse(self)
    }
}

impl ToUrl for Url {
    fn to_url(self) -> Result<Url, String> {
        Ok(self)
    }
}

impl<'a> ToUrl for &'a Url {
    fn to_url(self) -> Result<Url, String> {
        Ok(self.clone())
    }
}

#[deriving(Clone, PartialEq)]
pub struct PackageId {
    name: String,
    version: semver::Version,
    source_id: SourceId,
}

impl<S: hash::Writer> Hash<S> for PackageId {
    fn hash(&self, state: &mut S) {
        self.name.hash(state);
        self.version.to_string().hash(state);
        self.source_id.hash(state);
    }
}

impl Eq for PackageId {}

#[deriving(Clone, Show, PartialEq)]
pub enum PackageIdError {
    InvalidVersion(String),
    InvalidNamespace(String)
}

impl CargoError for PackageIdError {
    fn description(&self) -> String {
        match *self {
            InvalidVersion(ref v) => format!("invalid version: {}", *v),
            InvalidNamespace(ref ns) => format!("invalid namespace: {}", *ns),
        }
    }
    fn is_human(&self) -> bool { true }
}

#[deriving(PartialEq, Hash, Clone, Encodable)]
pub struct Metadata {
    pub metadata: String,
    pub extra_filename: String
}

impl PackageId {
    pub fn new<T: ToVersion>(name: &str, version: T,
                             sid: &SourceId) -> CargoResult<PackageId> {
        let v = try!(version.to_version().map_err(InvalidVersion));
        Ok(PackageId {
            name: name.to_string(),
            version: v,
            source_id: sid.clone()
        })
    }

    pub fn get_name<'a>(&'a self) -> &'a str {
        self.name.as_slice()
    }

    pub fn get_version<'a>(&'a self) -> &'a semver::Version {
        &self.version
    }

    pub fn get_source_id<'a>(&'a self) -> &'a SourceId {
        &self.source_id
    }

    pub fn generate_metadata(&self) -> Metadata {
        let metadata = format!("{}:-:{}:-:{}", self.name, self.version, self.source_id);
        let extra_filename = short_hash(
            &(self.name.as_slice(), self.version.to_string(), &self.source_id));

        Metadata { metadata: metadata, extra_filename: format!("-{}", extra_filename) }
    }
}

static central_repo: &'static str = "http://rust-lang.org/central-repo";

impl Show for PackageId {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        try!(write!(f, "{} v{}", self.name, self.version));

        if self.source_id.to_string().as_slice() != central_repo {
            try!(write!(f, " ({})", self.source_id));
        }

        Ok(())
    }
}

impl<D: Decoder<Box<CargoError + Send>>>
    Decodable<D,Box<CargoError + Send>> for PackageId {
    fn decode(d: &mut D) -> CargoResult<PackageId> {
        let (name, version, source_id): (String, String, SourceId) = try!(Decodable::decode(d));

        PackageId::new(name.as_slice(), version.as_slice(), &source_id)
    }
}

impl<E, S: Encoder<E>> Encodable<S,E> for PackageId {
    fn encode(&self, e: &mut S) -> Result<(), E> {
        (self.name.clone(), self.version.to_string(), self.source_id.clone()).encode(e)
    }
}

#[cfg(test)]
mod tests {
    use super::{PackageId, central_repo};
    use core::source::{Location, RegistryKind, SourceId};

    #[test]
    fn invalid_version_handled_nicely() {
        let loc = Location::parse(central_repo).unwrap();
        let repo = SourceId::new(RegistryKind, loc);

        assert!(PackageId::new("foo", "1.0", &repo).is_err());
        assert!(PackageId::new("foo", "1", &repo).is_err());
        assert!(PackageId::new("foo", "bar", &repo).is_err());
        assert!(PackageId::new("foo", "", &repo).is_err());
    }
}
<commit_msg>undo inline for better readability<commit_after>use semver;
use url::Url;
use std::hash::Hash;
use std::fmt;
use std::fmt::{Show,Formatter};
use collections::hash;
use serialize::{
    Encodable,
    Encoder,
    Decodable,
    Decoder
};

use util::{CargoResult, CargoError, short_hash};
use core::source::SourceId;

trait ToVersion {
    fn to_version(self) -> Result<semver::Version, String>;
}

impl ToVersion for semver::Version {
    fn to_version(self) -> Result<semver::Version, String> {
        Ok(self)
    }
}

impl<'a> ToVersion for &'a str {
    fn to_version(self) -> Result<semver::Version, String> {
        match semver::parse(self) {
            Some(v) => Ok(v),
            None => Err(format!("cannot parse '{}' as a semver", self)),
        }
    }
}

trait ToUrl {
    fn to_url(self) -> Result<Url, String>;
}

impl<'a> ToUrl for &'a str {
    fn to_url(self) -> Result<Url, String> {
        Url::parse(self)
    }
}

impl ToUrl for Url {
    fn to_url(self) -> Result<Url, String> {
        Ok(self)
    }
}

impl<'a> ToUrl for &'a Url {
    fn to_url(self) -> Result<Url, String> {
        Ok(self.clone())
    }
}

#[deriving(Clone, PartialEq)]
pub struct PackageId {
    name: String,
    version: semver::Version,
    source_id: SourceId,
}

impl<S: hash::Writer> Hash<S> for PackageId {
    fn hash(&self, state: &mut S) {
        self.name.hash(state);
        self.version.to_string().hash(state);
        self.source_id.hash(state);
    }
}

impl Eq for PackageId {}

#[deriving(Clone, Show, PartialEq)]
pub enum PackageIdError {
    InvalidVersion(String),
    InvalidNamespace(String)
}

impl CargoError for PackageIdError {
    fn description(&self) -> String {
        match *self {
            InvalidVersion(ref v) => format!("invalid version: {}", *v),
            InvalidNamespace(ref ns) => format!("invalid namespace: {}", *ns),
        }
    }
    fn is_human(&self) -> bool { true }
}

#[deriving(PartialEq, Hash, Clone, Encodable)]
pub struct Metadata {
    pub metadata: String,
    pub extra_filename: String
}

impl PackageId {
    pub fn new<T: ToVersion>(name: &str, version: T,
                             sid: &SourceId) -> CargoResult<PackageId> {
        let v = try!(version.to_version().map_err(InvalidVersion));
        Ok(PackageId {
            name: name.to_string(),
            version: v,
            source_id: sid.clone()
        })
    }

    pub fn get_name<'a>(&'a self) -> &'a str {
        self.name.as_slice()
    }

    pub fn get_version<'a>(&'a self) -> &'a semver::Version {
        &self.version
    }

    pub fn get_source_id<'a>(&'a self) -> &'a SourceId {
        &self.source_id
    }

    pub fn generate_metadata(&self) -> Metadata {
        let metadata = format!("{}:-:{}:-:{}", self.name, self.version, self.source_id);
        let extra_filename = short_hash(
            &(self.name.as_slice(), self.version.to_string(), &self.source_id));

        Metadata { metadata: metadata, extra_filename: format!("-{}", extra_filename) }
    }
}

static central_repo: &'static str = "http://rust-lang.org/central-repo";

impl Show for PackageId {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        try!(write!(f, "{} v{}", self.name, self.version));

        if self.source_id.to_string().as_slice() != central_repo {
            try!(write!(f, " ({})", self.source_id));
        }

        Ok(())
    }
}

impl<D: Decoder<Box<CargoError + Send>>>
    Decodable<D,Box<CargoError + Send>>
    for PackageId
{
    fn decode(d: &mut D) -> CargoResult<PackageId> {
        let (name, version, source_id): (String, String, SourceId) = try!(Decodable::decode(d));

        PackageId::new(name.as_slice(), version.as_slice(), &source_id)
    }
}

impl<E, S: Encoder<E>> Encodable<S,E> for PackageId {
    fn encode(&self, e: &mut S) -> Result<(), E> {
        (self.name.clone(), self.version.to_string(), self.source_id.clone()).encode(e)
    }
}

#[cfg(test)]
mod tests {
    use super::{PackageId, central_repo};
    use core::source::{Location, RegistryKind, SourceId};

    #[test]
    fn invalid_version_handled_nicely() {
        let loc = Location::parse(central_repo).unwrap();
        let repo = SourceId::new(RegistryKind, loc);

        assert!(PackageId::new("foo", "1.0", &repo).is_err());
        assert!(PackageId::new("foo", "1", &repo).is_err());
        assert!(PackageId::new("foo", "bar", &repo).is_err());
        assert!(PackageId::new("foo", "", &repo).is_err());
    }
}
<|endoftext|>


<commit_before>use std::env;

use cargo::core::Workspace;
use cargo::ops::{self, MessageFormat, Packages};
use cargo::util::{CliResult, CliError, Config, CargoErrorKind};
use cargo::util::important_paths::find_root_manifest_for_wd;

#[derive(Deserialize)]
pub struct Options {
    arg_args: Vec<String>,
    flag_features: Vec<String>,
    flag_all_features: bool,
    flag_jobs: Option<u32>,
    flag_manifest_path: Option<String>,
    flag_no_default_features: bool,
    flag_no_run: bool,
    flag_package: Vec<String>,
    flag_target: Option<String>,
    flag_lib: bool,
    flag_doc: bool,
    flag_bin: Vec<String>,
    flag_bins: bool,
    flag_example: Vec<String>,
    flag_examples: bool,
    flag_test: Vec<String>,
    flag_tests: bool,
    flag_bench: Vec<String>,
    flag_benches: bool,
    flag_all_targets: bool,
    flag_verbose: u32,
    flag_quiet: Option<bool>,
    flag_color: Option<String>,
    flag_message_format: MessageFormat,
    flag_release: bool,
    flag_no_fail_fast: bool,
    flag_frozen: bool,
    flag_locked: bool,
    flag_all: bool,
    flag_exclude: Vec<String>,
    #[serde(rename = "flag_Z")]
    flag_z: Vec<String>,
}

pub const USAGE: &'static str = "
Execute all unit and integration tests of a local package

Usage:
    cargo test [options] [--] [<args>...]

Options:
    -h, --help                   Print this message
    --lib                        Test only this package's library
    --doc                        Test only this library's documentation
    --bin NAME ...               Test only the specified binary
    --bins                       Test all binaries
    --example NAME ...           Check that the specified examples compile
    --examples                   Check that all examples compile
    --test NAME ...              Test only the specified test target
    --tests                      Test all tests
    --bench NAME ...             Test only the specified bench target
    --benches                    Test all benches
    --all-targets                Test all targets (default)
    --no-run                     Compile, but don't run tests
    -p SPEC, --package SPEC ...  Package to run tests for
    --all                        Test all packages in the workspace
    --exclude SPEC ...           Exclude packages from the test
    -j N, --jobs N               Number of parallel builds, see below for details
    --release                    Build artifacts in release mode, with optimizations
    --features FEATURES          Space-separated list of features to also build
    --all-features               Build all available features
    --no-default-features        Do not build the `default` feature
    --target TRIPLE              Build for the target triple
    --manifest-path PATH         Path to the manifest to build tests for
    -v, --verbose ...            Use verbose output (-vv very verbose/build.rs output)
    -q, --quiet                  No output printed to stdout
    --color WHEN                 Coloring: auto, always, never
    --message-format FMT         Error format: human, json [default: human]
    --no-fail-fast               Run all tests regardless of failure
    --frozen                     Require Cargo.lock and cache are up to date
    --locked                     Require Cargo.lock is up to date
    -Z FLAG ...                  Unstable (nightly-only) flags to Cargo

All of the trailing arguments are passed to the test binaries generated for
filtering tests and generally providing options configuring how they run. For
example, this will run all tests with the name `foo` in their name:

    cargo test foo

If the --package argument is given, then SPEC is a package id specification
which indicates which package should be tested. If it is not given, then the
current package is tested. For more information on SPEC and its format, see the
`cargo help pkgid` command.

All packages in the workspace are tested if the `--all` flag is supplied. The
`--all` flag is automatically assumed for a virtual manifest.
Note that `--exclude` has to be specified in conjunction with the `--all` flag.

The --jobs argument affects the building of the test executable but does
not affect how many jobs are used when running the tests. The default value
for the --jobs argument is the number of CPUs. If you want to control the
number of simultaneous running test cases, pass the `--test-threads` option
to the test binaries:

  cargo test -- --test-threads=1

Compilation can be configured via the `test` profile in the manifest.

By default the rust test harness hides output from test execution to
keep results readable. Test output can be recovered (e.g. for debugging)
by passing `--nocapture` to the test binaries:

  cargo test -- --nocapture

To get the list of all options available for the test binaries use this:

  cargo test -- --help
";

pub fn execute(options: Options, config: &mut Config) -> CliResult {
    debug!("executing; cmd=cargo-test; args={:?}",
           env::args().collect::<Vec<_>>());

    config.configure(options.flag_verbose,
                     options.flag_quiet,
                     &options.flag_color,
                     options.flag_frozen,
                     options.flag_locked,
                     &options.flag_z)?;

    let root = find_root_manifest_for_wd(options.flag_manifest_path, config.cwd())?;
    let ws = Workspace::new(&root, config)?;

    let empty = Vec::new();
    let (mode, filter);
    if options.flag_doc {
        mode = ops::CompileMode::Doctest;
        filter = ops::CompileFilter::new(true, &empty, false, &empty, false,
                                               &empty, false, &empty, false,
                                         false);
    } else {
        mode = ops::CompileMode::Test;
        filter = ops::CompileFilter::new(options.flag_lib,
                                         &options.flag_bin, options.flag_bins,
                                         &options.flag_test, options.flag_tests,
                                         &options.flag_example, options.flag_examples,
                                         &options.flag_bench, options.flag_benches,
                                         options.flag_all_targets);
    }

    let spec = Packages::from_flags(ws.is_virtual(),
                                    options.flag_all,
                                    &options.flag_exclude,
                                    &options.flag_package)?;

    let ops = ops::TestOptions {
        no_run: options.flag_no_run,
        no_fail_fast: options.flag_no_fail_fast,
        only_doc: options.flag_doc,
        compile_opts: ops::CompileOptions {
            config: config,
            jobs: options.flag_jobs,
            target: options.flag_target.as_ref().map(|s| &s[..]),
            features: &options.flag_features,
            all_features: options.flag_all_features,
            no_default_features: options.flag_no_default_features,
            spec: spec,
            release: options.flag_release,
            mode: mode,
            filter: filter,
            message_format: options.flag_message_format,
            target_rustdoc_args: None,
            target_rustc_args: None,
        },
    };

    let err = ops::run_tests(&ws, &ops, &options.arg_args)?;
    match err {
        None => Ok(()),
        Some(err) => {
            Err(match err.exit.as_ref().and_then(|e| e.code()) {
                Some(i) => CliError::new(err.hint().into(), i),
                None => CliError::new(CargoErrorKind::CargoTestErrorKind(err).into(), 101),
            })
        }
    }
}
<commit_msg>Auto merge of #4675 - mgeisler:align-cmdline-examples, r=alexcrichton<commit_after>use std::env;

use cargo::core::Workspace;
use cargo::ops::{self, MessageFormat, Packages};
use cargo::util::{CliResult, CliError, Config, CargoErrorKind};
use cargo::util::important_paths::find_root_manifest_for_wd;

#[derive(Deserialize)]
pub struct Options {
    arg_args: Vec<String>,
    flag_features: Vec<String>,
    flag_all_features: bool,
    flag_jobs: Option<u32>,
    flag_manifest_path: Option<String>,
    flag_no_default_features: bool,
    flag_no_run: bool,
    flag_package: Vec<String>,
    flag_target: Option<String>,
    flag_lib: bool,
    flag_doc: bool,
    flag_bin: Vec<String>,
    flag_bins: bool,
    flag_example: Vec<String>,
    flag_examples: bool,
    flag_test: Vec<String>,
    flag_tests: bool,
    flag_bench: Vec<String>,
    flag_benches: bool,
    flag_all_targets: bool,
    flag_verbose: u32,
    flag_quiet: Option<bool>,
    flag_color: Option<String>,
    flag_message_format: MessageFormat,
    flag_release: bool,
    flag_no_fail_fast: bool,
    flag_frozen: bool,
    flag_locked: bool,
    flag_all: bool,
    flag_exclude: Vec<String>,
    #[serde(rename = "flag_Z")]
    flag_z: Vec<String>,
}

pub const USAGE: &'static str = "
Execute all unit and integration tests of a local package

Usage:
    cargo test [options] [--] [<args>...]

Options:
    -h, --help                   Print this message
    --lib                        Test only this package's library
    --doc                        Test only this library's documentation
    --bin NAME ...               Test only the specified binary
    --bins                       Test all binaries
    --example NAME ...           Check that the specified examples compile
    --examples                   Check that all examples compile
    --test NAME ...              Test only the specified test target
    --tests                      Test all tests
    --bench NAME ...             Test only the specified bench target
    --benches                    Test all benches
    --all-targets                Test all targets (default)
    --no-run                     Compile, but don't run tests
    -p SPEC, --package SPEC ...  Package to run tests for
    --all                        Test all packages in the workspace
    --exclude SPEC ...           Exclude packages from the test
    -j N, --jobs N               Number of parallel builds, see below for details
    --release                    Build artifacts in release mode, with optimizations
    --features FEATURES          Space-separated list of features to also build
    --all-features               Build all available features
    --no-default-features        Do not build the `default` feature
    --target TRIPLE              Build for the target triple
    --manifest-path PATH         Path to the manifest to build tests for
    -v, --verbose ...            Use verbose output (-vv very verbose/build.rs output)
    -q, --quiet                  No output printed to stdout
    --color WHEN                 Coloring: auto, always, never
    --message-format FMT         Error format: human, json [default: human]
    --no-fail-fast               Run all tests regardless of failure
    --frozen                     Require Cargo.lock and cache are up to date
    --locked                     Require Cargo.lock is up to date
    -Z FLAG ...                  Unstable (nightly-only) flags to Cargo

All of the trailing arguments are passed to the test binaries generated for
filtering tests and generally providing options configuring how they run. For
example, this will run all tests with the name `foo` in their name:

    cargo test foo

If the --package argument is given, then SPEC is a package id specification
which indicates which package should be tested. If it is not given, then the
current package is tested. For more information on SPEC and its format, see the
`cargo help pkgid` command.

All packages in the workspace are tested if the `--all` flag is supplied. The
`--all` flag is automatically assumed for a virtual manifest.
Note that `--exclude` has to be specified in conjunction with the `--all` flag.

The --jobs argument affects the building of the test executable but does
not affect how many jobs are used when running the tests. The default value
for the --jobs argument is the number of CPUs. If you want to control the
number of simultaneous running test cases, pass the `--test-threads` option
to the test binaries:

    cargo test -- --test-threads=1

Compilation can be configured via the `test` profile in the manifest.

By default the rust test harness hides output from test execution to
keep results readable. Test output can be recovered (e.g. for debugging)
by passing `--nocapture` to the test binaries:

    cargo test -- --nocapture

To get the list of all options available for the test binaries use this:

    cargo test -- --help
";

pub fn execute(options: Options, config: &mut Config) -> CliResult {
    debug!("executing; cmd=cargo-test; args={:?}",
           env::args().collect::<Vec<_>>());

    config.configure(options.flag_verbose,
                     options.flag_quiet,
                     &options.flag_color,
                     options.flag_frozen,
                     options.flag_locked,
                     &options.flag_z)?;

    let root = find_root_manifest_for_wd(options.flag_manifest_path, config.cwd())?;
    let ws = Workspace::new(&root, config)?;

    let empty = Vec::new();
    let (mode, filter);
    if options.flag_doc {
        mode = ops::CompileMode::Doctest;
        filter = ops::CompileFilter::new(true, &empty, false, &empty, false,
                                               &empty, false, &empty, false,
                                         false);
    } else {
        mode = ops::CompileMode::Test;
        filter = ops::CompileFilter::new(options.flag_lib,
                                         &options.flag_bin, options.flag_bins,
                                         &options.flag_test, options.flag_tests,
                                         &options.flag_example, options.flag_examples,
                                         &options.flag_bench, options.flag_benches,
                                         options.flag_all_targets);
    }

    let spec = Packages::from_flags(ws.is_virtual(),
                                    options.flag_all,
                                    &options.flag_exclude,
                                    &options.flag_package)?;

    let ops = ops::TestOptions {
        no_run: options.flag_no_run,
        no_fail_fast: options.flag_no_fail_fast,
        only_doc: options.flag_doc,
        compile_opts: ops::CompileOptions {
            config: config,
            jobs: options.flag_jobs,
            target: options.flag_target.as_ref().map(|s| &s[..]),
            features: &options.flag_features,
            all_features: options.flag_all_features,
            no_default_features: options.flag_no_default_features,
            spec: spec,
            release: options.flag_release,
            mode: mode,
            filter: filter,
            message_format: options.flag_message_format,
            target_rustdoc_args: None,
            target_rustc_args: None,
        },
    };

    let err = ops::run_tests(&ws, &ops, &options.arg_args)?;
    match err {
        None => Ok(()),
        Some(err) => {
            Err(match err.exit.as_ref().and_then(|e| e.code()) {
                Some(i) => CliError::new(err.hint().into(), i),
                None => CliError::new(CargoErrorKind::CargoTestErrorKind(err).into(), 101),
            })
        }
    }
}
<|endoftext|>


<commit_before>#[macro_use]
pub mod macros;
pub mod error;
pub mod arguments;
pub mod configuration;
pub mod engines;

extern crate std;
extern crate config;

use std::io::prelude::*;
use std::fs::File;
use std::path::Path;
use std::collections::HashMap;
use config::types::Config;
use bins::error::*;
use bins::arguments::Arguments;
use bins::engines::Engine;
use bins::engines::gist::Gist;
use bins::engines::hastebin::Hastebin;
use bins::engines::pastie::Pastie;
use bins::engines::pastebin::Pastebin;

#[derive(Clone)]
pub struct PasteFile {
  pub name: String,
  pub data: String
}

impl PasteFile {
  fn new(name: String, data: String) -> Self {
    PasteFile { name: name, data: data }
  }
}

pub struct Bins {
  pub config: Config,
  pub arguments: Arguments
}

impl Bins {
  pub fn new(config: Config, arguments: Arguments) -> Self {
    Bins {
      config: config,
      arguments: arguments
    }
  }

  pub fn get_engine(&self) -> Result<Box<Engine>> {
    let service = match self.arguments.service {
      Some(ref s) => s,
      None => return Err("no service was specified and no default service was set.".into())
    };
    match service.to_lowercase().as_ref() {
      "gist" => Ok(Box::new(Gist::new())),
      "hastebin" => Ok(Box::new(Hastebin::new())),
      "pastie" => Ok(Box::new(Pastie::new())),
      "pastebin" => Ok(Box::new(Pastebin::new())),
      _ => Err(format!("unknown service \"{}\"", service).into())
    }
  }

  fn read_file<P: AsRef<Path>>(&self, p: P) -> Result<String> {
    let path = p.as_ref();
    let name = match path.to_str() {
      Some(s) => s,
      None => return Err(String::from("file name was not valid unicode").into())
    };
    if !path.exists() {
      return Err(format!("{} does not exist", name).into());
    }
    if !path.is_file() {
      return Err(format!("{} is not a file", name).into());
    }
    let mut file = match File::open(path) {
      Ok(f) => f,
      Err(e) => {
        return Err(format!("could not open {}: {}", name, e).into());
      }
    };
    let mut s = String::new();
    if let Err(e) = file.read_to_string(&mut s) {
      return Err(format!("could not read {}: {}", name, e).into());
    }
    Ok(s)
  }

  fn read_file_to_pastefile<P: AsRef<Path>>(&self, p: P) -> Result<PasteFile> {
    let path = p.as_ref();
    match self.read_file(path) {
      Ok(s) => {
        let n = match path.file_name() {
          Some(x) => x,
          None => return Err("not a valid file name".into())
        };
        Ok(PasteFile::new(n.to_string_lossy().into_owned(), s))
      },
      Err(s) => return Err(s)
    }
  }

  pub fn get_to_paste(&self) -> Result<Vec<PasteFile>> {
    let arguments = &self.arguments;
    let message = &arguments.message;
    let paste_files: Vec<PasteFile> = if !message.is_empty() {
      vec![PasteFile::new(String::from("message"), message.to_owned())]
    } else if !arguments.files.is_empty() {
      let files = arguments.files.clone();
      let results = files.iter()
        .map(|s| Path::new(s))
        .map(|p| self.read_file_to_pastefile(p))
        .map(|r| r.map_err(|e| e.iter().map(|x| x.to_string()).collect::<Vec<_>>().join("\n")))
        .collect::<Vec<_>>();
      for res in results.iter().cloned() {
        if res.is_err() {
          return Err(res.err().unwrap().into());
        }
      }
      let mut pastes = results.iter().cloned().map(|r| r.unwrap()).filter(|p| !p.data.trim().is_empty()).collect::<Vec<_>>();
      if pastes.len() < 1 {
        return Err("no files (or all empty files) to paste".into());
      }
      self.handle_duplicate_file_names(&mut pastes);
      pastes
    } else {
      let mut buffer = String::new();
      if let Err(e) = std::io::stdin().read_to_string(&mut buffer) {
        return Err(format!("error reading stdin: {}", e).into());
      }
      vec![PasteFile::new(String::from("stdin"), buffer)]
    };
    Ok(paste_files)
  }

  fn handle_duplicate_file_names(&self, pastes: &mut Vec<PasteFile>) {
    let mut names_map: HashMap<String, i32> = HashMap::new();
    for mut paste in pastes {
      let name = paste.name.clone();
      if names_map.contains_key(&name) {
          let parts = name.rsplit(".");
        let (beginning, end) = if parts.clone().count() > 1 {
          let mut beginning_parts = parts.clone().skip(1).collect::<Vec<_>>();
          beginning_parts.reverse();
          let beginning = beginning_parts.join(".");
          let end = parts.take(1).next().map(|s| String::from(".") + s).unwrap_or(String::from(""));
          (beginning, end)
        } else {
          (name.clone(), String::from(""))
        };
        let number = names_map.entry(name.clone()).or_insert(1);
        paste.name = format!("{}_{}{}", beginning, number, end);
        *number += 1;
      }
      names_map.entry(name.clone()).or_insert(1);
    }
  }
}
<commit_msg>style(bins): fix line indented too far<commit_after>#[macro_use]
pub mod macros;
pub mod error;
pub mod arguments;
pub mod configuration;
pub mod engines;

extern crate std;
extern crate config;

use std::io::prelude::*;
use std::fs::File;
use std::path::Path;
use std::collections::HashMap;
use config::types::Config;
use bins::error::*;
use bins::arguments::Arguments;
use bins::engines::Engine;
use bins::engines::gist::Gist;
use bins::engines::hastebin::Hastebin;
use bins::engines::pastie::Pastie;
use bins::engines::pastebin::Pastebin;

#[derive(Clone)]
pub struct PasteFile {
  pub name: String,
  pub data: String
}

impl PasteFile {
  fn new(name: String, data: String) -> Self {
    PasteFile { name: name, data: data }
  }
}

pub struct Bins {
  pub config: Config,
  pub arguments: Arguments
}

impl Bins {
  pub fn new(config: Config, arguments: Arguments) -> Self {
    Bins {
      config: config,
      arguments: arguments
    }
  }

  pub fn get_engine(&self) -> Result<Box<Engine>> {
    let service = match self.arguments.service {
      Some(ref s) => s,
      None => return Err("no service was specified and no default service was set.".into())
    };
    match service.to_lowercase().as_ref() {
      "gist" => Ok(Box::new(Gist::new())),
      "hastebin" => Ok(Box::new(Hastebin::new())),
      "pastie" => Ok(Box::new(Pastie::new())),
      "pastebin" => Ok(Box::new(Pastebin::new())),
      _ => Err(format!("unknown service \"{}\"", service).into())
    }
  }

  fn read_file<P: AsRef<Path>>(&self, p: P) -> Result<String> {
    let path = p.as_ref();
    let name = match path.to_str() {
      Some(s) => s,
      None => return Err(String::from("file name was not valid unicode").into())
    };
    if !path.exists() {
      return Err(format!("{} does not exist", name).into());
    }
    if !path.is_file() {
      return Err(format!("{} is not a file", name).into());
    }
    let mut file = match File::open(path) {
      Ok(f) => f,
      Err(e) => {
        return Err(format!("could not open {}: {}", name, e).into());
      }
    };
    let mut s = String::new();
    if let Err(e) = file.read_to_string(&mut s) {
      return Err(format!("could not read {}: {}", name, e).into());
    }
    Ok(s)
  }

  fn read_file_to_pastefile<P: AsRef<Path>>(&self, p: P) -> Result<PasteFile> {
    let path = p.as_ref();
    match self.read_file(path) {
      Ok(s) => {
        let n = match path.file_name() {
          Some(x) => x,
          None => return Err("not a valid file name".into())
        };
        Ok(PasteFile::new(n.to_string_lossy().into_owned(), s))
      },
      Err(s) => return Err(s)
    }
  }

  pub fn get_to_paste(&self) -> Result<Vec<PasteFile>> {
    let arguments = &self.arguments;
    let message = &arguments.message;
    let paste_files: Vec<PasteFile> = if !message.is_empty() {
      vec![PasteFile::new(String::from("message"), message.to_owned())]
    } else if !arguments.files.is_empty() {
      let files = arguments.files.clone();
      let results = files.iter()
        .map(|s| Path::new(s))
        .map(|p| self.read_file_to_pastefile(p))
        .map(|r| r.map_err(|e| e.iter().map(|x| x.to_string()).collect::<Vec<_>>().join("\n")))
        .collect::<Vec<_>>();
      for res in results.iter().cloned() {
        if res.is_err() {
          return Err(res.err().unwrap().into());
        }
      }
      let mut pastes = results.iter().cloned().map(|r| r.unwrap()).filter(|p| !p.data.trim().is_empty()).collect::<Vec<_>>();
      if pastes.len() < 1 {
        return Err("no files (or all empty files) to paste".into());
      }
      self.handle_duplicate_file_names(&mut pastes);
      pastes
    } else {
      let mut buffer = String::new();
      if let Err(e) = std::io::stdin().read_to_string(&mut buffer) {
        return Err(format!("error reading stdin: {}", e).into());
      }
      vec![PasteFile::new(String::from("stdin"), buffer)]
    };
    Ok(paste_files)
  }

  fn handle_duplicate_file_names(&self, pastes: &mut Vec<PasteFile>) {
    let mut names_map: HashMap<String, i32> = HashMap::new();
    for mut paste in pastes {
      let name = paste.name.clone();
      if names_map.contains_key(&name) {
        let parts = name.rsplit(".");
        let (beginning, end) = if parts.clone().count() > 1 {
          let mut beginning_parts = parts.clone().skip(1).collect::<Vec<_>>();
          beginning_parts.reverse();
          let beginning = beginning_parts.join(".");
          let end = parts.take(1).next().map(|s| String::from(".") + s).unwrap_or(String::from(""));
          (beginning, end)
        } else {
          (name.clone(), String::from(""))
        };
        let number = names_map.entry(name.clone()).or_insert(1);
        paste.name = format!("{}_{}{}", beginning, number, end);
        *number += 1;
      }
      names_map.entry(name.clone()).or_insert(1);
    }
  }
}
<|endoftext|>


<commit_before>#[macro_use]
pub mod macros;
pub mod error;
pub mod arguments;
pub mod configuration;
pub mod engines;

extern crate std;
extern crate config;

use std::io::prelude::*;
use std::fs::File;
use std::path::Path;
use std::collections::HashMap;
use config::types::Config;
use bins::error::*;
use bins::arguments::Arguments;
use bins::engines::Engine;
use bins::engines::gist::Gist;
use bins::engines::hastebin::Hastebin;
use bins::engines::pastie::Pastie;
use bins::engines::pastebin::Pastebin;
use bins::engines::sprunge::Sprunge;
use hyper::Url;

#[derive(Clone)]
pub struct PasteFile {
  pub name: String,
  pub data: String
}

impl PasteFile {
  fn new(name: String, data: String) -> Self {
    PasteFile { name: name, data: data }
  }
}

pub struct Bins {
  pub config: Config,
  pub arguments: Arguments
}

impl Bins {
  pub fn new(config: Config, arguments: Arguments) -> Self {
    Bins {
      config: config,
      arguments: arguments
    }
  }

  pub fn get_engine(&self) -> Result<Box<Engine>> {
    let service = match self.arguments.service {
      Some(ref s) => s,
      None => return Err("no service was specified and no default service was set.".into())
    };
    match service.to_lowercase().as_ref() {
      "gist" => Ok(Box::new(Gist::new())),
      "hastebin" => Ok(Box::new(Hastebin::new())),
      "pastie" => Ok(Box::new(Pastie::new())),
      "pastebin" => Ok(Box::new(Pastebin::new())),
      "sprunge" => Ok(Box::new(Sprunge::new())),
      _ => Err(format!("unknown service \"{}\"", service).into())
    }
  }

  fn read_file<P: AsRef<Path>>(&self, p: P) -> Result<String> {
    let path = p.as_ref();
    let name = match path.to_str() {
      Some(s) => s,
      None => return Err(String::from("file name was not valid unicode").into())
    };
    if !path.exists() {
      return Err(format!("{} does not exist", name).into());
    }
    if !path.is_file() {
      return Err(format!("{} is not a file", name).into());
    }
    let mut file = match File::open(path) {
      Ok(f) => f,
      Err(e) => {
        return Err(format!("could not open {}: {}", name, e).into());
      }
    };
    let mut s = String::new();
    if let Err(e) = file.read_to_string(&mut s) {
      return Err(format!("could not read {}: {}", name, e).into());
    }
    Ok(s)
  }

  fn read_file_to_pastefile<P: AsRef<Path>>(&self, p: P) -> Result<PasteFile> {
    let path = p.as_ref();
    match self.read_file(path) {
      Ok(s) => {
        let n = match path.file_name() {
          Some(x) => x,
          None => return Err("not a valid file name".into())
        };
        Ok(PasteFile::new(n.to_string_lossy().into_owned(), s))
      },
      Err(s) => Err(s)
    }
  }

  pub fn get_to_paste(&self) -> Result<Vec<PasteFile>> {
    let arguments = &self.arguments;
    let message = &arguments.message;
    let paste_files: Vec<PasteFile> = if message.is_some() {
      vec![PasteFile::new(String::from("message"), message.clone().unwrap())]
    } else if !arguments.files.is_empty() {
      let files = arguments.files.clone();
      let results = files.iter()
        .map(|s| Path::new(s))
        .map(|p| self.read_file_to_pastefile(p))
        .map(|r| r.map_err(|e| e.iter().map(|x| x.to_string()).collect::<Vec<_>>().join("\n")))
        .collect::<Vec<_>>();
      for res in results.iter().cloned() {
        if res.is_err() {
          return Err(res.err().unwrap().into());
        }
      }
      let mut pastes = results.iter().cloned().map(|r| r.unwrap()).filter(|p| !p.data.trim().is_empty()).collect::<Vec<_>>();
      self.handle_duplicate_file_names(&mut pastes);
      pastes
    } else {
      let mut buffer = String::new();
      if let Err(e) = std::io::stdin().read_to_string(&mut buffer) {
        return Err(format!("error reading stdin: {}", e).into());
      }
      vec![PasteFile::new(String::from("stdin"), buffer)]
    };
    if paste_files.iter().filter(|p| !p.data.trim().is_empty()).count() < 1 {
      return Err("no files (or all empty files) to paste".into());
    }
    Ok(paste_files)
  }

  fn handle_duplicate_file_names(&self, pastes: &mut Vec<PasteFile>) {
    let mut names_map: HashMap<String, i32> = HashMap::new();
    for mut paste in pastes {
      let name = paste.name.clone();
      if names_map.contains_key(&name) {
        let parts = name.rsplit('.');
        let (beginning, end) = if parts.clone().count() > 1 {
          let mut beginning_parts = parts.clone().skip(1).collect::<Vec<_>>();
          beginning_parts.reverse();
          let beginning = beginning_parts.join(".");
          let end = parts.take(1).next().map_or(String::new(), |s| String::from(".") + s);
          (beginning, end)
        } else {
          (name.clone(), String::from(""))
        };
        let number = names_map.entry(name.clone()).or_insert(1);
        paste.name = format!("{}_{}{}", beginning, number, end);
        *number += 1;
      }
      names_map.entry(name.clone()).or_insert(1);
    }
  }

  fn get_engine_for_url(&self, url: &Url) -> Result<Box<Engine>> {
    let domain = match url.domain() {
      Some(d) => d,
      None => return Err("input url had no domain".into())
    };
    let engine: Box<Engine> = match domain {
      "gist.github.com" => Box::new(Gist::new()),
      "hastebin.com" => Box::new(Hastebin::new()),
      "pastebin.com" => Box::new(Pastebin::new()),
      "pastie.org" => Box::new(Pastie::new()),
      "sprunge.us" => Box::new(Sprunge::new()),
      _ => return Err(format!("could not find a bin for domain {}", domain).into())
    };
    Ok(engine)
  }

  fn get_raw(&self, url_string: &String) -> Result<String> {
    // can't use try!() because url::parser is private, and ParseError is at url::parser::ParseError
    let mut url = match Url::parse(url_string.as_ref()) {
      Ok(u) => u,
      Err(e) => return Err(e.to_string().into())
    };
    let engine = try!(self.get_engine_for_url(&url));
    engine.get_raw(self, &mut url)
  }

  pub fn get_output(&self) -> Result<String> {
    if let Some(ref input) = self.arguments.input {
      return self.get_raw(input);
    }
    let to_paste = try!(self.get_to_paste());
    let engine = try!(self.get_engine());
    engine.upload(self, &to_paste)
  }
}
<commit_msg>refactor: use &str instead of &String<commit_after>#[macro_use]
pub mod macros;
pub mod error;
pub mod arguments;
pub mod configuration;
pub mod engines;

extern crate std;
extern crate config;

use std::io::prelude::*;
use std::fs::File;
use std::path::Path;
use std::collections::HashMap;
use config::types::Config;
use bins::error::*;
use bins::arguments::Arguments;
use bins::engines::Engine;
use bins::engines::gist::Gist;
use bins::engines::hastebin::Hastebin;
use bins::engines::pastie::Pastie;
use bins::engines::pastebin::Pastebin;
use bins::engines::sprunge::Sprunge;
use hyper::Url;

#[derive(Clone)]
pub struct PasteFile {
  pub name: String,
  pub data: String
}

impl PasteFile {
  fn new(name: String, data: String) -> Self {
    PasteFile { name: name, data: data }
  }
}

pub struct Bins {
  pub config: Config,
  pub arguments: Arguments
}

impl Bins {
  pub fn new(config: Config, arguments: Arguments) -> Self {
    Bins {
      config: config,
      arguments: arguments
    }
  }

  pub fn get_engine(&self) -> Result<Box<Engine>> {
    let service = match self.arguments.service {
      Some(ref s) => s,
      None => return Err("no service was specified and no default service was set.".into())
    };
    match service.to_lowercase().as_ref() {
      "gist" => Ok(Box::new(Gist::new())),
      "hastebin" => Ok(Box::new(Hastebin::new())),
      "pastie" => Ok(Box::new(Pastie::new())),
      "pastebin" => Ok(Box::new(Pastebin::new())),
      "sprunge" => Ok(Box::new(Sprunge::new())),
      _ => Err(format!("unknown service \"{}\"", service).into())
    }
  }

  fn read_file<P: AsRef<Path>>(&self, p: P) -> Result<String> {
    let path = p.as_ref();
    let name = match path.to_str() {
      Some(s) => s,
      None => return Err(String::from("file name was not valid unicode").into())
    };
    if !path.exists() {
      return Err(format!("{} does not exist", name).into());
    }
    if !path.is_file() {
      return Err(format!("{} is not a file", name).into());
    }
    let mut file = match File::open(path) {
      Ok(f) => f,
      Err(e) => {
        return Err(format!("could not open {}: {}", name, e).into());
      }
    };
    let mut s = String::new();
    if let Err(e) = file.read_to_string(&mut s) {
      return Err(format!("could not read {}: {}", name, e).into());
    }
    Ok(s)
  }

  fn read_file_to_pastefile<P: AsRef<Path>>(&self, p: P) -> Result<PasteFile> {
    let path = p.as_ref();
    match self.read_file(path) {
      Ok(s) => {
        let n = match path.file_name() {
          Some(x) => x,
          None => return Err("not a valid file name".into())
        };
        Ok(PasteFile::new(n.to_string_lossy().into_owned(), s))
      },
      Err(s) => Err(s)
    }
  }

  pub fn get_to_paste(&self) -> Result<Vec<PasteFile>> {
    let arguments = &self.arguments;
    let message = &arguments.message;
    let paste_files: Vec<PasteFile> = if message.is_some() {
      vec![PasteFile::new(String::from("message"), message.clone().unwrap())]
    } else if !arguments.files.is_empty() {
      let files = arguments.files.clone();
      let results = files.iter()
        .map(|s| Path::new(s))
        .map(|p| self.read_file_to_pastefile(p))
        .map(|r| r.map_err(|e| e.iter().map(|x| x.to_string()).collect::<Vec<_>>().join("\n")))
        .collect::<Vec<_>>();
      for res in results.iter().cloned() {
        if res.is_err() {
          return Err(res.err().unwrap().into());
        }
      }
      let mut pastes = results.iter().cloned().map(|r| r.unwrap()).filter(|p| !p.data.trim().is_empty()).collect::<Vec<_>>();
      self.handle_duplicate_file_names(&mut pastes);
      pastes
    } else {
      let mut buffer = String::new();
      if let Err(e) = std::io::stdin().read_to_string(&mut buffer) {
        return Err(format!("error reading stdin: {}", e).into());
      }
      vec![PasteFile::new(String::from("stdin"), buffer)]
    };
    if paste_files.iter().filter(|p| !p.data.trim().is_empty()).count() < 1 {
      return Err("no files (or all empty files) to paste".into());
    }
    Ok(paste_files)
  }

  fn handle_duplicate_file_names(&self, pastes: &mut Vec<PasteFile>) {
    let mut names_map: HashMap<String, i32> = HashMap::new();
    for mut paste in pastes {
      let name = paste.name.clone();
      if names_map.contains_key(&name) {
        let parts = name.rsplit('.');
        let (beginning, end) = if parts.clone().count() > 1 {
          let mut beginning_parts = parts.clone().skip(1).collect::<Vec<_>>();
          beginning_parts.reverse();
          let beginning = beginning_parts.join(".");
          let end = parts.take(1).next().map_or(String::new(), |s| String::from(".") + s);
          (beginning, end)
        } else {
          (name.clone(), String::from(""))
        };
        let number = names_map.entry(name.clone()).or_insert(1);
        paste.name = format!("{}_{}{}", beginning, number, end);
        *number += 1;
      }
      names_map.entry(name.clone()).or_insert(1);
    }
  }

  fn get_engine_for_url(&self, url: &Url) -> Result<Box<Engine>> {
    let domain = match url.domain() {
      Some(d) => d,
      None => return Err("input url had no domain".into())
    };
    let engine: Box<Engine> = match domain {
      "gist.github.com" => Box::new(Gist::new()),
      "hastebin.com" => Box::new(Hastebin::new()),
      "pastebin.com" => Box::new(Pastebin::new()),
      "pastie.org" => Box::new(Pastie::new()),
      "sprunge.us" => Box::new(Sprunge::new()),
      _ => return Err(format!("could not find a bin for domain {}", domain).into())
    };
    Ok(engine)
  }

  fn get_raw(&self, url_string: &str) -> Result<String> {
    // can't use try!() because url::parser is private, and ParseError is at url::parser::ParseError
    let mut url = match Url::parse(url_string.as_ref()) {
      Ok(u) => u,
      Err(e) => return Err(e.to_string().into())
    };
    let engine = try!(self.get_engine_for_url(&url));
    engine.get_raw(self, &mut url)
  }

  pub fn get_output(&self) -> Result<String> {
    if let Some(ref input) = self.arguments.input {
      return self.get_raw(input);
    }
    let to_paste = try!(self.get_to_paste());
    let engine = try!(self.get_engine());
    engine.upload(self, &to_paste)
  }
}
<|endoftext|>


<commit_before>// Copyright 2016 Walter Kuppens.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::fs::File;
use std::io::Error;
use std::io::Read;
use std::path::Path;
use std::result::Result;

// Used to identify a rom as being in the iNES format. This byte sequence should
// be at the start of every rom.
const INES_IDENTIFIER: [u8; 4] = [0x4E, 0x45, 0x53, 0x1A];

#[derive(Debug)]
pub struct INESHeader {
    identifier: [u8; 4], // File format identifier.
    prg_rom_size: u8,    // Size of PRG ROM in 16 KB units.
    chr_rom_size: u8,    // Size of CHR ROM in 8 KB units.
    flags_6: u8,
    flags_7: u8,
    prg_ram_size: u8,    // Size of PRG RAM in 8 KB units (0 infers 8 KB for compatibility).
    flags_9: u8,
    flags_10: u8         // Unofficial, unused by most emulators.
}

/// Reads a binary file at a given path and stores it in a vector of bytes.
pub fn read_bin<P: AsRef<Path>>(path: P) -> Result<Vec<u8>, Error> {
    let mut buffer: Vec<u8> = Vec::new();
    match File::open(path) {
        Ok(mut file) => {
            file.read_to_end(&mut buffer).unwrap();
        },
        Err(e) => return Err(e)
    }
    Ok(buffer)
}

/// Parses the header of a rom (assumed to be in the iNES format).
///
/// The first 16 bytes of the rom contain the header. The iNES format is
/// identified by the literal byte string "NES<0x1A>". If the rom is not in the
/// iNES format, then it cannot be executed by the emulator.
pub fn parse_rom_header(rom: &[u8]) -> Result<INESHeader, &str> {
    // Validate that the rom is formatted in the iNES format.
    let identifier = &rom[0x0..0x4];
    if identifier != INES_IDENTIFIER {
        return Err("rom does not contain iNES identifier and is invalid")
    }

    // Copy the identifier from the rom for placement in the header.
    let mut new_identifier: [u8; 4] = [0; 4];
    new_identifier.copy_from_slice(identifier);

    // Return an iNES header containing fields filled in from the rom.
    Ok(INESHeader {
        identifier: new_identifier,
        prg_rom_size: rom[0x4].clone(),
        chr_rom_size: rom[0x5],
        flags_6: rom[0x6],
        flags_7: rom[0x7],
        prg_ram_size: rom[0x8],
        flags_9: rom[0x9],
        flags_10: rom[0xA]
    })
}
<commit_msg>Removed pointless clone is iNES parser<commit_after>// Copyright 2016 Walter Kuppens.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::fs::File;
use std::io::Error;
use std::io::Read;
use std::path::Path;
use std::result::Result;

// Used to identify a rom as being in the iNES format. This byte sequence should
// be at the start of every rom.
const INES_IDENTIFIER: [u8; 4] = [0x4E, 0x45, 0x53, 0x1A];

#[derive(Debug)]
pub struct INESHeader {
    identifier: [u8; 4], // File format identifier.
    prg_rom_size: u8,    // Size of PRG ROM in 16 KB units.
    chr_rom_size: u8,    // Size of CHR ROM in 8 KB units.
    flags_6: u8,
    flags_7: u8,
    prg_ram_size: u8,    // Size of PRG RAM in 8 KB units (0 infers 8 KB for compatibility).
    flags_9: u8,
    flags_10: u8         // Unofficial, unused by most emulators.
}

/// Reads a binary file at a given path and stores it in a vector of bytes.
pub fn read_bin<P: AsRef<Path>>(path: P) -> Result<Vec<u8>, Error> {
    let mut buffer: Vec<u8> = Vec::new();
    match File::open(path) {
        Ok(mut file) => {
            file.read_to_end(&mut buffer).unwrap();
        },
        Err(e) => return Err(e)
    }
    Ok(buffer)
}

/// Parses the header of a rom (assumed to be in the iNES format).
///
/// The first 16 bytes of the rom contain the header. The iNES format is
/// identified by the literal byte string "NES<0x1A>". If the rom is not in the
/// iNES format, then it cannot be executed by the emulator.
pub fn parse_rom_header(rom: &[u8]) -> Result<INESHeader, &str> {
    // Validate that the rom is formatted in the iNES format.
    let identifier = &rom[0x0..0x4];
    if identifier != INES_IDENTIFIER {
        return Err("rom does not contain iNES identifier and is invalid")
    }

    // Copy the identifier from the rom for placement in the header.
    let mut new_identifier: [u8; 4] = [0; 4];
    new_identifier.copy_from_slice(identifier);

    // Return an iNES header containing fields filled in from the rom.
    Ok(INESHeader {
        identifier: new_identifier,
        prg_rom_size: rom[0x4],
        chr_rom_size: rom[0x5],
        flags_6: rom[0x6],
        flags_7: rom[0x7],
        prg_ram_size: rom[0x8],
        flags_9: rom[0x9],
        flags_10: rom[0xA]
    })
}
<|endoftext|>


<commit_before>extern crate rustc_serialize;
extern crate docopt;

use docopt::Docopt;

// Write the Docopt usage string.
const USAGE: &'static str = "
Rust's package manager

Usage:
    cargo <command> [<args>...]
    cargo [options]

Options:
    -h, --help       Display this message
    -V, --version    Print version info and exit
    --list           List installed commands
    -v, --verbose    Use verbose output

Some common cargo commands are:
    build       Compile the current project
    clean       Remove the target directory
    doc         Build this project's and its dependencies' documentation
    new         Create a new cargo project
    run         Build and execute src/main.rs
    test        Run the tests
    bench       Run the benchmarks
    update      Update dependencies listed in Cargo.lock

See 'cargo help <command>' for more information on a specific command.
";

#[derive(Debug, RustcDecodable)]
struct Args {
    arg_command: Option<Command>,
    arg_args: Vec<String>,
    flag_list: bool,
    flag_verbose: bool,
}

#[derive(Debug, RustcDecodable)]
enum Command {
    Build, Clean, Doc, New, Run, Test, Bench, Update,
}

fn main() {
    let args: Args = Docopt::new(USAGE)
                            .and_then(|d| d.decode())
                            .unwrap_or_else(|e| e.exit());
    println!("{:?}", args);
}
<commit_msg>Use options_first(true) in cargo exemple<commit_after>extern crate rustc_serialize;
extern crate docopt;

use docopt::Docopt;

// Write the Docopt usage string.
const USAGE: &'static str = "
Rust's package manager

Usage:
    cargo <command> [<args>...]
    cargo [options]

Options:
    -h, --help       Display this message
    -V, --version    Print version info and exit
    --list           List installed commands
    -v, --verbose    Use verbose output

Some common cargo commands are:
    build       Compile the current project
    clean       Remove the target directory
    doc         Build this project's and its dependencies' documentation
    new         Create a new cargo project
    run         Build and execute src/main.rs
    test        Run the tests
    bench       Run the benchmarks
    update      Update dependencies listed in Cargo.lock

See 'cargo help <command>' for more information on a specific command.
";

#[derive(Debug, RustcDecodable)]
struct Args {
    arg_command: Option<Command>,
    arg_args: Vec<String>,
    flag_list: bool,
    flag_verbose: bool,
}

#[derive(Debug, RustcDecodable)]
enum Command {
    Build, Clean, Doc, New, Run, Test, Bench, Update,
}

fn main() {
    let args: Args = Docopt::new(USAGE)
                            .and_then(|d| d.options_first(true).decode())
                            .unwrap_or_else(|e| e.exit());
    println!("{:?}", args);
}
<|endoftext|>


<commit_before>//! [Church booleans](https://en.wikipedia.org/wiki/Church_encoding#Church_Booleans)

use term::*;

/// A Church-encoded boolean `true`.
///
/// TRUE := λab.a = λ λ 2
pub fn tru() -> Term { abs(abs(Var(2))) }

/// A Church-encoded boolean `false`.
///
/// FALSE := λab.b = λ λ 1
pub fn fls() -> Term { abs(abs(Var(1))) }

/// Applied to two Church-encoded booleans it returns their Church-encoded conjunction.
///
/// AND := λpq.p q p = λ λ 2 1 2
///
/// # Examples
/// ```
/// # #[macro_use] extern crate lambda_calculus;
/// # fn main() {
/// use lambda_calculus::booleans::{and, tru, fls};
/// use lambda_calculus::reduction::beta;
/// use lambda_calculus::reduction::Order::*;
///
/// assert_eq!(beta(app!(and(), tru(), tru()), NOR, 0, false), tru());
/// assert_eq!(beta(app!(and(), tru(), fls()), NOR, 0, false), fls());
/// assert_eq!(beta(app!(and(), fls(), tru()), NOR, 0, false), fls());
/// assert_eq!(beta(app!(and(), fls(), fls()), NOR, 0, false), fls());
/// # }
/// ```
pub fn and() -> Term {
    abs(abs(
        app!(Var(2), Var(1), Var(2))
    ))
}

/// Applied to two Church-encoded booleans it returns their Church-encoded disjunction.
///
/// OR := λpq.p p q = λ λ 2 2 1
///
/// # Examples
/// ```
/// # #[macro_use] extern crate lambda_calculus;
/// # fn main() {
/// use lambda_calculus::booleans::{or, tru, fls};
/// use lambda_calculus::reduction::beta;
/// use lambda_calculus::reduction::Order::*;
///
/// assert_eq!(beta(app!(or(), tru(), tru()), NOR, 0, false), tru());
/// assert_eq!(beta(app!(or(), tru(), fls()), NOR, 0, false), tru());
/// assert_eq!(beta(app!(or(), fls(), tru()), NOR, 0, false), tru());
/// assert_eq!(beta(app!(or(), fls(), fls()), NOR, 0, false), fls());
/// # }
/// ```
pub fn or() -> Term {
    abs(abs(
        app!(Var(2), Var(2), Var(1))
    ))
}

/// Applied to a Church-encoded boolean it returns its Church-encoded negation.
///
/// NOT := λp.p FALSE TRUE = λ 1 FALSE TRUE
///
/// # Examples
/// ```
/// # #[macro_use] extern crate lambda_calculus;
/// # fn main() {
/// use lambda_calculus::booleans::{not, tru, fls};
/// use lambda_calculus::reduction::beta;
/// use lambda_calculus::reduction::Order::*;
///
/// assert_eq!(beta(app!(not(), tru()), NOR, 0, false), fls());
/// assert_eq!(beta(app!(not(), fls()), NOR, 0, false), tru());
/// # }
/// ```
pub fn not() -> Term {
    abs(
        app!(Var(1), fls(), tru())
    )
}

/// Applied to a Church-encoded boolean it returns its Church-encoded exclusive disjunction.
///
/// XOR := λab.a (NOT b) b = λ λ 2 (NOT 1) 1
///
/// # Examples
/// ```
/// # #[macro_use] extern crate lambda_calculus;
/// # fn main() {
/// use lambda_calculus::booleans::{xor, tru, fls};
/// use lambda_calculus::reduction::beta;
/// use lambda_calculus::reduction::Order::*;
///
/// assert_eq!(beta(app!(xor(), tru(), tru()), NOR, 0, false), fls());
/// assert_eq!(beta(app!(xor(), tru(), fls()), NOR, 0, false), tru());
/// assert_eq!(beta(app!(xor(), fls(), tru()), NOR, 0, false), tru());
/// assert_eq!(beta(app!(xor(), fls(), fls()), NOR, 0, false), fls());
/// # }
/// ```
pub fn xor() -> Term {
    abs(abs(
        app!(Var(2), app(not(), Var(1)), Var(1))
    ))
}

/// Applied to a Church-encoded boolean it returns its Church-encoded joint denial.
///
/// NOR := λab.a a b (λab.b) (λab.a) = λ λ 2 2 1 (λ λ 1) (λ λ 2)
///
/// # Examples
/// ```
/// # #[macro_use] extern crate lambda_calculus;
/// # fn main() {
/// use lambda_calculus::booleans::{nor, tru, fls};
/// use lambda_calculus::reduction::beta;
/// use lambda_calculus::reduction::Order::*;
///
/// assert_eq!(beta(app!(nor(), tru(), tru()), NOR, 0, false), fls());
/// assert_eq!(beta(app!(nor(), tru(), fls()), NOR, 0, false), fls());
/// assert_eq!(beta(app!(nor(), fls(), tru()), NOR, 0, false), fls());
/// assert_eq!(beta(nor(), NOR, 0, false), nor());
/// # }
/// ```
pub fn nor() -> Term {
    abs(abs(
        app!(
            Var(2),
            Var(2),
            Var(1),
            abs(abs(Var(1))),
            abs(abs(Var(2)))
        )
    ))
}

/// Applied to a Church encoded predicate and two terms it returns the first one if the predicate
/// is true or the second one if the predicate is false.
///
/// IF_ELSE := λpab.p a b = λ λ λ 3 2 1
///
/// # Examples
/// ```
/// # #[macro_use] extern crate lambda_calculus;
/// # fn main() {
/// use lambda_calculus::booleans::{if_else, tru, fls};
/// use lambda_calculus::arithmetic::{zero, one};
/// use lambda_calculus::reduction::beta;
/// use lambda_calculus::reduction::Order::*;
///
/// assert_eq!(beta(app!(if_else(), tru(), one(), zero()), NOR, 0, false),  one());
/// assert_eq!(beta(app!(if_else(), fls(), one(), zero()), NOR, 0, false), zero());
/// # }
/// ```
pub fn if_else() -> Term {
    abs(abs(abs(
        app!(Var(3), Var(2), Var(1))
    )))
}

impl From<bool> for Term {
    fn from(b: bool) -> Self {
        if b { tru() } else { fls() }
    }
}
<commit_msg>fix a NOR test<commit_after>//! [Church booleans](https://en.wikipedia.org/wiki/Church_encoding#Church_Booleans)

use term::*;

/// A Church-encoded boolean `true`.
///
/// TRUE := λab.a = λ λ 2
pub fn tru() -> Term { abs(abs(Var(2))) }

/// A Church-encoded boolean `false`.
///
/// FALSE := λab.b = λ λ 1
pub fn fls() -> Term { abs(abs(Var(1))) }

/// Applied to two Church-encoded booleans it returns their Church-encoded conjunction.
///
/// AND := λpq.p q p = λ λ 2 1 2
///
/// # Examples
/// ```
/// # #[macro_use] extern crate lambda_calculus;
/// # fn main() {
/// use lambda_calculus::booleans::{and, tru, fls};
/// use lambda_calculus::reduction::beta;
/// use lambda_calculus::reduction::Order::*;
///
/// assert_eq!(beta(app!(and(), tru(), tru()), NOR, 0, false), tru());
/// assert_eq!(beta(app!(and(), tru(), fls()), NOR, 0, false), fls());
/// assert_eq!(beta(app!(and(), fls(), tru()), NOR, 0, false), fls());
/// assert_eq!(beta(app!(and(), fls(), fls()), NOR, 0, false), fls());
/// # }
/// ```
pub fn and() -> Term {
    abs(abs(
        app!(Var(2), Var(1), Var(2))
    ))
}

/// Applied to two Church-encoded booleans it returns their Church-encoded disjunction.
///
/// OR := λpq.p p q = λ λ 2 2 1
///
/// # Examples
/// ```
/// # #[macro_use] extern crate lambda_calculus;
/// # fn main() {
/// use lambda_calculus::booleans::{or, tru, fls};
/// use lambda_calculus::reduction::beta;
/// use lambda_calculus::reduction::Order::*;
///
/// assert_eq!(beta(app!(or(), tru(), tru()), NOR, 0, false), tru());
/// assert_eq!(beta(app!(or(), tru(), fls()), NOR, 0, false), tru());
/// assert_eq!(beta(app!(or(), fls(), tru()), NOR, 0, false), tru());
/// assert_eq!(beta(app!(or(), fls(), fls()), NOR, 0, false), fls());
/// # }
/// ```
pub fn or() -> Term {
    abs(abs(
        app!(Var(2), Var(2), Var(1))
    ))
}

/// Applied to a Church-encoded boolean it returns its Church-encoded negation.
///
/// NOT := λp.p FALSE TRUE = λ 1 FALSE TRUE
///
/// # Examples
/// ```
/// # #[macro_use] extern crate lambda_calculus;
/// # fn main() {
/// use lambda_calculus::booleans::{not, tru, fls};
/// use lambda_calculus::reduction::beta;
/// use lambda_calculus::reduction::Order::*;
///
/// assert_eq!(beta(app!(not(), tru()), NOR, 0, false), fls());
/// assert_eq!(beta(app!(not(), fls()), NOR, 0, false), tru());
/// # }
/// ```
pub fn not() -> Term {
    abs(
        app!(Var(1), fls(), tru())
    )
}

/// Applied to a Church-encoded boolean it returns its Church-encoded exclusive disjunction.
///
/// XOR := λab.a (NOT b) b = λ λ 2 (NOT 1) 1
///
/// # Examples
/// ```
/// # #[macro_use] extern crate lambda_calculus;
/// # fn main() {
/// use lambda_calculus::booleans::{xor, tru, fls};
/// use lambda_calculus::reduction::beta;
/// use lambda_calculus::reduction::Order::*;
///
/// assert_eq!(beta(app!(xor(), tru(), tru()), NOR, 0, false), fls());
/// assert_eq!(beta(app!(xor(), tru(), fls()), NOR, 0, false), tru());
/// assert_eq!(beta(app!(xor(), fls(), tru()), NOR, 0, false), tru());
/// assert_eq!(beta(app!(xor(), fls(), fls()), NOR, 0, false), fls());
/// # }
/// ```
pub fn xor() -> Term {
    abs(abs(
        app!(Var(2), app(not(), Var(1)), Var(1))
    ))
}

/// Applied to a Church-encoded boolean it returns its Church-encoded joint denial.
///
/// NOR := λab.a a b (λab.b) (λab.a) = λ λ 2 2 1 (λ λ 1) (λ λ 2)
///
/// # Examples
/// ```
/// # #[macro_use] extern crate lambda_calculus;
/// # fn main() {
/// use lambda_calculus::booleans::{nor, tru, fls};
/// use lambda_calculus::reduction::beta;
/// use lambda_calculus::reduction::Order::*;
///
/// assert_eq!(beta(app!(nor(), tru(), tru()), NOR, 0, false), fls());
/// assert_eq!(beta(app!(nor(), tru(), fls()), NOR, 0, false), fls());
/// assert_eq!(beta(app!(nor(), fls(), tru()), NOR, 0, false), fls());
/// assert_eq!(beta(app!(nor(), fls(), fls()), NOR, 0, false), tru());
/// # }
/// ```
pub fn nor() -> Term {
    abs(abs(
        app!(
            Var(2),
            Var(2),
            Var(1),
            abs(abs(Var(1))),
            abs(abs(Var(2)))
        )
    ))
}

/// Applied to a Church encoded predicate and two terms it returns the first one if the predicate
/// is true or the second one if the predicate is false.
///
/// IF_ELSE := λpab.p a b = λ λ λ 3 2 1
///
/// # Examples
/// ```
/// # #[macro_use] extern crate lambda_calculus;
/// # fn main() {
/// use lambda_calculus::booleans::{if_else, tru, fls};
/// use lambda_calculus::arithmetic::{zero, one};
/// use lambda_calculus::reduction::beta;
/// use lambda_calculus::reduction::Order::*;
///
/// assert_eq!(beta(app!(if_else(), tru(), one(), zero()), NOR, 0, false),  one());
/// assert_eq!(beta(app!(if_else(), fls(), one(), zero()), NOR, 0, false), zero());
/// # }
/// ```
pub fn if_else() -> Term {
    abs(abs(abs(
        app!(Var(3), Var(2), Var(1))
    )))
}

impl From<bool> for Term {
    fn from(b: bool) -> Self {
        if b { tru() } else { fls() }
    }
}
<|endoftext|>