<commit_before>use errors::*;
use multirust_dist::{utils, dist};
use multirust_dist::prefix::InstallPrefix;
use multirust_dist::dist::ToolchainDesc;
use multirust_dist::manifestation::{Manifestation, Changes};
use multirust_dist::manifest::Component;
use config::Cfg;
use env_var;
use install::{self, InstallMethod};

use std::process::Command;
use std::path::{Path, PathBuf};
use std::ffi::OsStr;
use std::env;

use hyper;
use multirust_dist;

#[derive(Debug)]
pub struct Toolchain<'a> {
    cfg: &'a Cfg,
    name: String,
    path: PathBuf,
    prefix: InstallPrefix,
}

impl<'a> Toolchain<'a> {
    pub fn from(cfg: &'a Cfg, name: &str) -> Self {
        let path = cfg.toolchains_dir.join(name);
        Toolchain {
            cfg: cfg,
            name: name.to_owned(),
            path: path.clone(),
            prefix: InstallPrefix::from(path),
        }
    }
    pub fn cfg(&self) -> &'a Cfg {
        self.cfg
    }
    pub fn name(&self) -> &str {
        &self.name
    }
    pub fn path(&self) -> &Path {
        &self.path
    }
    pub fn exists(&self) -> bool {
        utils::is_directory(&self.path)
    }
    pub fn verify(&self) -> Result<()> {
        Ok(try!(utils::assert_is_directory(&self.path)))
    }
    pub fn remove(&self) -> Result<()> {
        if self.exists() {
            self.cfg.notify_handler.call(Notification::UninstallingToolchain(&self.name));
        } else {
            self.cfg.notify_handler.call(Notification::ToolchainNotInstalled(&self.name));
            return Ok(());
        }
        if let Some(update_hash) = try!(self.update_hash()) {
            try!(utils::remove_file("update hash", &update_hash));
        }
        let handler = self.cfg.notify_handler.as_ref();
        let result = install::uninstall(&self.path, ntfy!(&handler));
        if !self.exists() {
            self.cfg.notify_handler.call(Notification::UninstalledToolchain(&self.name));
        }
        Ok(try!(result))
    }
    pub fn remove_if_exists(&self) -> Result<()> {
        if self.exists() {
            self.remove()
        } else {
            Ok(())
        }
    }
    pub fn install(&self, install_method: InstallMethod) -> Result<()> {
        if self.exists() {
            self.cfg.notify_handler.call(Notification::UpdatingToolchain(&self.name));
        } else {
            self.cfg.notify_handler.call(Notification::InstallingToolchain(&self.name));
        }
        self.cfg
            .notify_handler
            .call(Notification::ToolchainDirectory(&self.path, &self.name));
        let handler = self.cfg.notify_handler.as_ref();
        Ok(try!(install_method.run(&self.path, ntfy!(&handler))))
    }
    pub fn install_if_not_installed(&self, install_method: InstallMethod) -> Result<()> {
        self.cfg.notify_handler.call(Notification::LookingForToolchain(&self.name));
        if !self.exists() {
            self.install(install_method)
        } else {
            self.cfg.notify_handler.call(Notification::UsingExistingToolchain(&self.name));
            Ok(())
        }
    }
    pub fn update_hash(&self) -> Result<Option<PathBuf>> {
        if self.is_custom() {
            Ok(None)
        } else {
            Ok(Some(try!(self.cfg.get_hash_file(&self.name, true))))
        }
    }

    fn download_cfg(&self) -> dist::DownloadCfg {
        dist::DownloadCfg {
            dist_root: &self.cfg.dist_root_url,
            temp_cfg: &self.cfg.temp_cfg,
            notify_handler: ntfy!(&self.cfg.notify_handler),
        }
    }

    pub fn install_from_dist(&self) -> Result<()> {
        let update_hash = try!(self.update_hash());
        self.install(InstallMethod::Dist(&self.name,
                                         update_hash.as_ref().map(|p| &**p),
                                         self.download_cfg()))
    }
    pub fn install_from_dist_if_not_installed(&self) -> Result<()> {
        let update_hash = try!(self.update_hash());
        self.install_if_not_installed(InstallMethod::Dist(&self.name,
                                                          update_hash.as_ref().map(|p| &**p),
                                                          self.download_cfg()))
    }
    pub fn is_custom(&self) -> bool {
        ToolchainDesc::from_str(&self.name).is_err()
    }
    pub fn is_tracking(&self) -> bool {
        ToolchainDesc::from_str(&self.name).ok().map(|d| d.is_tracking()) == Some(true)
    }

    pub fn ensure_custom(&self) -> Result<()> {
        if !self.is_custom() {
            Err(Error::Install(multirust_dist::Error::InvalidToolchainName(self.name.to_string())))
        } else {
            Ok(())
        }
    }

    pub fn install_from_installers(&self, installers: &[&OsStr]) -> Result<()> {
        try!(self.ensure_custom());

        try!(self.remove_if_exists());

        // FIXME: This should do all downloads first, then do
        // installs, and do it all in a single transaction.
        for installer in installers {
            let installer_str = installer.to_str().unwrap_or("bogus");
            match installer_str.rfind(".") {
                Some(i) => {
                    let extension = &installer_str[i+1..];
                    if extension != "gz" {
                        return Err(Error::BadInstallerType(extension.to_string()));
                    }
                }
                None => return Err(Error::BadInstallerType(String::from("(none)")))
            }

            // FIXME: Pretty hacky
            let is_url = installer_str.starts_with("file://")
                || installer_str.starts_with("http://")
                || installer_str.starts_with("https://");
            let url = hyper::Url::parse(installer_str).ok();
            let url = if is_url { url } else { None };
            if let Some(url) = url {

                // Download to a local file
                let local_installer = try!(self.cfg.temp_cfg.new_file_with_ext("", ".tar.gz"));
                try!(utils::download_file(url,
                                          &local_installer,
                                          None,
                                          ntfy!(&self.cfg.notify_handler)));
                try!(self.install(InstallMethod::Installer(&local_installer, &self.cfg.temp_cfg)));
            } else {
                // If installer is a filename

                // No need to download
                let local_installer = Path::new(installer);

                // Install from file
                try!(self.install(InstallMethod::Installer(&local_installer, &self.cfg.temp_cfg)));
            }
        }

        Ok(())
    }

    pub fn install_from_dir(&self, src: &Path, link: bool) -> Result<()> {
        try!(self.ensure_custom());

        if link {
            self.install(InstallMethod::Link(&try!(utils::to_absolute(src))))
        } else {
            self.install(InstallMethod::Copy(src))
        }
    }

    pub fn create_command<T: AsRef<OsStr>>(&self, binary: T) -> Result<Command> {
        if !self.exists() {
            return Err(Error::ToolchainNotInstalled(self.name.to_owned()));
        }

        let mut cmd = Command::new(binary);
        self.set_env(&mut cmd);
        Ok(cmd)
    }

    fn set_env(&self, cmd: &mut Command) {
        let ref cargo_home = self.cfg.multirust_dir.join("cargo");
        let ref bin_path = self.path.join("bin");

        self.set_ldpath(cmd);

        env_var::set_path("PATH", bin_path, cmd);
        env_var::set_default("CARGO_HOME", cargo_home.as_ref(), cmd);
        env_var::inc("RUST_RECURSION_COUNT", cmd);

        // FIXME: This should not be a path, but a toolchain name.
        // Not sure what's going on here.
        cmd.env("MULTIRUST_TOOLCHAIN", &self.path);
        cmd.env("MULTIRUST_HOME", &self.cfg.multirust_dir);
    }

    pub fn set_ldpath(&self, cmd: &mut Command) {
        let new_path = self.path.join("lib");

        env_var::set_path("LD_LIBRARY_PATH", &new_path, cmd);
        env_var::set_path("DYLD_LIBRARY_PATH", &new_path, cmd);
    }

    pub fn doc_path(&self, relative: &str) -> Result<PathBuf> {
        try!(self.verify());

        let parts = vec!["share", "doc", "rust", "html"];
        let mut doc_dir = self.path.clone();
        for part in parts {
            doc_dir.push(part);
        }
        doc_dir.push(relative);

        Ok(doc_dir)
    }
    pub fn open_docs(&self, relative: &str) -> Result<()> {
        try!(self.verify());

        Ok(try!(utils::open_browser(&try!(self.doc_path(relative)))))
    }

    pub fn make_default(&self) -> Result<()> {
        self.cfg.set_default(&self.name)
    }
    pub fn make_override(&self, path: &Path) -> Result<()> {
        Ok(try!(self.cfg.override_db.set(path,
                                         &self.name,
                                         &self.cfg.temp_cfg,
                                         self.cfg.notify_handler.as_ref())))
    }

    pub fn list_components(&self) -> Result<Vec<Component>> {
        if !self.exists() {
            return Err(Error::ToolchainNotInstalled(self.name.to_owned()));
        }

        // FIXME: This toolchain handling is a mess. Just do it once
        // when the toolchain is created.
        let ref toolchain = self.name;
        let ref toolchain = try!(ToolchainDesc::from_str(toolchain));
        let trip = toolchain.target_triple();
        let manifestation = try!(Manifestation::open(self.prefix.clone(), &trip));

        if let Some(manifest) = try!(manifestation.load_manifest()) {
            // Return all optional components of the "rust" package for the
            // toolchain's target triple.
            let mut res = Vec::new();

            let rust_pkg = manifest.packages.get("rust")
                .expect("manifest should cantain a rust package");
            let targ_pkg = rust_pkg.targets.get(&trip)
                .expect("installed manifest should have a known target");

            for extension in &targ_pkg.extensions {
                res.push(extension.clone())
            }

            Ok(res)
        } else {
            Err(Error::ComponentsUnsupported(self.name.to_string()))
        }
    }

    pub fn add_component(&self, component: Component) -> Result<()> {
        if !self.exists() {
            return Err(Error::ToolchainNotInstalled(self.name.to_owned()));
        }

        let ref toolchain = self.name;
        let ref toolchain = try!(ToolchainDesc::from_str(toolchain));
        let trip = toolchain.target_triple();
        let manifestation = try!(Manifestation::open(self.prefix.clone(), &trip));

        if let Some(manifest) = try!(manifestation.load_manifest()) {

            // Validate the component name
            let rust_pkg = manifest.packages.get("rust")
                .expect("manifest should cantain a rust package");
            let targ_pkg = rust_pkg.targets.get(&trip)
                .expect("installed manifest should have a known target");

            if targ_pkg.components.contains(&component) {
                return Err(Error::AddingRequiredComponent(self.name.to_string(), component));
            }

            if !targ_pkg.extensions.contains(&component) {
                return Err(Error::UnknownComponent(self.name.to_string(), component));
            }

            let changes = Changes {
                add_extensions: vec![component],
                remove_extensions: vec![]
            };

            try!(manifestation.update(&self.name,
                                      &manifest,
                                      changes,
                                      self.download_cfg().temp_cfg,
                                      self.download_cfg().notify_handler.clone()));

            Ok(())
        } else {
            Err(Error::ComponentsUnsupported(self.name.to_string()))
        }
    }

    pub fn remove_component(&self, component: Component) -> Result<()> {
        if !self.exists() {
            return Err(Error::ToolchainNotInstalled(self.name.to_owned()));
        }

        let ref toolchain = self.name;
        let ref toolchain = try!(ToolchainDesc::from_str(toolchain));
        let trip = toolchain.target_triple();
        let manifestation = try!(Manifestation::open(self.prefix.clone(), &trip));

        if let Some(manifest) = try!(manifestation.load_manifest()) {

            // Validate the component name
            let rust_pkg = manifest.packages.get("rust")
                .expect("manifest should cantain a rust package");
            let targ_pkg = rust_pkg.targets.get(&trip)
                .expect("installed manifest should have a known target");

            if targ_pkg.components.contains(&component) {
                return Err(Error::RemovingRequiredComponent(self.name.to_string(), component));
            }

            let dist_config = try!(manifestation.read_config()).unwrap();
            if !dist_config.components.contains(&component) {
                return Err(Error::UnknownComponent(self.name.to_string(), component));
            }

            let changes = Changes {
                add_extensions: vec![],
                remove_extensions: vec![component]
            };

            try!(manifestation.update(&self.name,
                                      &manifest,
                                      changes,
                                      self.download_cfg().temp_cfg,
                                      self.download_cfg().notify_handler.clone()));

            Ok(())
        } else {
            Err(Error::ComponentsUnsupported(self.name.to_string()))
        }
    }

    pub fn binary_file(&self, name: &str) -> PathBuf {
        let mut path = self.path.clone();
        path.push("bin");
        path.push(name.to_owned() + env::consts::EXE_SUFFIX);
        path
    }
}
<commit_msg>Remove the InstallPrefix from Toolchain<commit_after>use errors::*;
use multirust_dist::{utils, dist};
use multirust_dist::prefix::InstallPrefix;
use multirust_dist::dist::ToolchainDesc;
use multirust_dist::manifestation::{Manifestation, Changes};
use multirust_dist::manifest::Component;
use config::Cfg;
use env_var;
use install::{self, InstallMethod};

use std::process::Command;
use std::path::{Path, PathBuf};
use std::ffi::OsStr;
use std::env;

use hyper;
use multirust_dist;

#[derive(Debug)]
pub struct Toolchain<'a> {
    cfg: &'a Cfg,
    name: String,
    path: PathBuf,
}

impl<'a> Toolchain<'a> {
    pub fn from(cfg: &'a Cfg, name: &str) -> Self {
        let path = cfg.toolchains_dir.join(name);
        Toolchain {
            cfg: cfg,
            name: name.to_owned(),
            path: path.clone(),
        }
    }
    pub fn cfg(&self) -> &'a Cfg {
        self.cfg
    }
    pub fn name(&self) -> &str {
        &self.name
    }
    pub fn path(&self) -> &Path {
        &self.path
    }
    pub fn exists(&self) -> bool {
        utils::is_directory(&self.path)
    }
    pub fn verify(&self) -> Result<()> {
        Ok(try!(utils::assert_is_directory(&self.path)))
    }
    pub fn remove(&self) -> Result<()> {
        if self.exists() {
            self.cfg.notify_handler.call(Notification::UninstallingToolchain(&self.name));
        } else {
            self.cfg.notify_handler.call(Notification::ToolchainNotInstalled(&self.name));
            return Ok(());
        }
        if let Some(update_hash) = try!(self.update_hash()) {
            try!(utils::remove_file("update hash", &update_hash));
        }
        let handler = self.cfg.notify_handler.as_ref();
        let result = install::uninstall(&self.path, ntfy!(&handler));
        if !self.exists() {
            self.cfg.notify_handler.call(Notification::UninstalledToolchain(&self.name));
        }
        Ok(try!(result))
    }
    pub fn remove_if_exists(&self) -> Result<()> {
        if self.exists() {
            self.remove()
        } else {
            Ok(())
        }
    }
    pub fn install(&self, install_method: InstallMethod) -> Result<()> {
        if self.exists() {
            self.cfg.notify_handler.call(Notification::UpdatingToolchain(&self.name));
        } else {
            self.cfg.notify_handler.call(Notification::InstallingToolchain(&self.name));
        }
        self.cfg
            .notify_handler
            .call(Notification::ToolchainDirectory(&self.path, &self.name));
        let handler = self.cfg.notify_handler.as_ref();
        Ok(try!(install_method.run(&self.path, ntfy!(&handler))))
    }
    pub fn install_if_not_installed(&self, install_method: InstallMethod) -> Result<()> {
        self.cfg.notify_handler.call(Notification::LookingForToolchain(&self.name));
        if !self.exists() {
            self.install(install_method)
        } else {
            self.cfg.notify_handler.call(Notification::UsingExistingToolchain(&self.name));
            Ok(())
        }
    }
    pub fn update_hash(&self) -> Result<Option<PathBuf>> {
        if self.is_custom() {
            Ok(None)
        } else {
            Ok(Some(try!(self.cfg.get_hash_file(&self.name, true))))
        }
    }

    fn download_cfg(&self) -> dist::DownloadCfg {
        dist::DownloadCfg {
            dist_root: &self.cfg.dist_root_url,
            temp_cfg: &self.cfg.temp_cfg,
            notify_handler: ntfy!(&self.cfg.notify_handler),
        }
    }

    pub fn install_from_dist(&self) -> Result<()> {
        let update_hash = try!(self.update_hash());
        self.install(InstallMethod::Dist(&self.name,
                                         update_hash.as_ref().map(|p| &**p),
                                         self.download_cfg()))
    }
    pub fn install_from_dist_if_not_installed(&self) -> Result<()> {
        let update_hash = try!(self.update_hash());
        self.install_if_not_installed(InstallMethod::Dist(&self.name,
                                                          update_hash.as_ref().map(|p| &**p),
                                                          self.download_cfg()))
    }
    pub fn is_custom(&self) -> bool {
        ToolchainDesc::from_str(&self.name).is_err()
    }
    pub fn is_tracking(&self) -> bool {
        ToolchainDesc::from_str(&self.name).ok().map(|d| d.is_tracking()) == Some(true)
    }

    pub fn ensure_custom(&self) -> Result<()> {
        if !self.is_custom() {
            Err(Error::Install(multirust_dist::Error::InvalidToolchainName(self.name.to_string())))
        } else {
            Ok(())
        }
    }

    pub fn install_from_installers(&self, installers: &[&OsStr]) -> Result<()> {
        try!(self.ensure_custom());

        try!(self.remove_if_exists());

        // FIXME: This should do all downloads first, then do
        // installs, and do it all in a single transaction.
        for installer in installers {
            let installer_str = installer.to_str().unwrap_or("bogus");
            match installer_str.rfind(".") {
                Some(i) => {
                    let extension = &installer_str[i+1..];
                    if extension != "gz" {
                        return Err(Error::BadInstallerType(extension.to_string()));
                    }
                }
                None => return Err(Error::BadInstallerType(String::from("(none)")))
            }

            // FIXME: Pretty hacky
            let is_url = installer_str.starts_with("file://")
                || installer_str.starts_with("http://")
                || installer_str.starts_with("https://");
            let url = hyper::Url::parse(installer_str).ok();
            let url = if is_url { url } else { None };
            if let Some(url) = url {

                // Download to a local file
                let local_installer = try!(self.cfg.temp_cfg.new_file_with_ext("", ".tar.gz"));
                try!(utils::download_file(url,
                                          &local_installer,
                                          None,
                                          ntfy!(&self.cfg.notify_handler)));
                try!(self.install(InstallMethod::Installer(&local_installer, &self.cfg.temp_cfg)));
            } else {
                // If installer is a filename

                // No need to download
                let local_installer = Path::new(installer);

                // Install from file
                try!(self.install(InstallMethod::Installer(&local_installer, &self.cfg.temp_cfg)));
            }
        }

        Ok(())
    }

    pub fn install_from_dir(&self, src: &Path, link: bool) -> Result<()> {
        try!(self.ensure_custom());

        if link {
            self.install(InstallMethod::Link(&try!(utils::to_absolute(src))))
        } else {
            self.install(InstallMethod::Copy(src))
        }
    }

    pub fn create_command<T: AsRef<OsStr>>(&self, binary: T) -> Result<Command> {
        if !self.exists() {
            return Err(Error::ToolchainNotInstalled(self.name.to_owned()));
        }

        let mut cmd = Command::new(binary);
        self.set_env(&mut cmd);
        Ok(cmd)
    }

    fn set_env(&self, cmd: &mut Command) {
        let ref cargo_home = self.cfg.multirust_dir.join("cargo");
        let ref bin_path = self.path.join("bin");

        self.set_ldpath(cmd);

        env_var::set_path("PATH", bin_path, cmd);
        env_var::set_default("CARGO_HOME", cargo_home.as_ref(), cmd);
        env_var::inc("RUST_RECURSION_COUNT", cmd);

        // FIXME: This should not be a path, but a toolchain name.
        // Not sure what's going on here.
        cmd.env("MULTIRUST_TOOLCHAIN", &self.path);
        cmd.env("MULTIRUST_HOME", &self.cfg.multirust_dir);
    }

    pub fn set_ldpath(&self, cmd: &mut Command) {
        let new_path = self.path.join("lib");

        env_var::set_path("LD_LIBRARY_PATH", &new_path, cmd);
        env_var::set_path("DYLD_LIBRARY_PATH", &new_path, cmd);
    }

    pub fn doc_path(&self, relative: &str) -> Result<PathBuf> {
        try!(self.verify());

        let parts = vec!["share", "doc", "rust", "html"];
        let mut doc_dir = self.path.clone();
        for part in parts {
            doc_dir.push(part);
        }
        doc_dir.push(relative);

        Ok(doc_dir)
    }
    pub fn open_docs(&self, relative: &str) -> Result<()> {
        try!(self.verify());

        Ok(try!(utils::open_browser(&try!(self.doc_path(relative)))))
    }

    pub fn make_default(&self) -> Result<()> {
        self.cfg.set_default(&self.name)
    }
    pub fn make_override(&self, path: &Path) -> Result<()> {
        Ok(try!(self.cfg.override_db.set(path,
                                         &self.name,
                                         &self.cfg.temp_cfg,
                                         self.cfg.notify_handler.as_ref())))
    }

    pub fn list_components(&self) -> Result<Vec<Component>> {
        if !self.exists() {
            return Err(Error::ToolchainNotInstalled(self.name.to_owned()));
        }

        // FIXME: This toolchain handling is a mess. Just do it once
        // when the toolchain is created.
        let ref toolchain = self.name;
        let ref toolchain = try!(ToolchainDesc::from_str(toolchain));
        let trip = toolchain.target_triple();
        let prefix = InstallPrefix::from(self.path.to_owned());
        let manifestation = try!(Manifestation::open(prefix, &trip));

        if let Some(manifest) = try!(manifestation.load_manifest()) {
            // Return all optional components of the "rust" package for the
            // toolchain's target triple.
            let mut res = Vec::new();

            let rust_pkg = manifest.packages.get("rust")
                .expect("manifest should cantain a rust package");
            let targ_pkg = rust_pkg.targets.get(&trip)
                .expect("installed manifest should have a known target");

            for extension in &targ_pkg.extensions {
                res.push(extension.clone())
            }

            Ok(res)
        } else {
            Err(Error::ComponentsUnsupported(self.name.to_string()))
        }
    }

    pub fn add_component(&self, component: Component) -> Result<()> {
        if !self.exists() {
            return Err(Error::ToolchainNotInstalled(self.name.to_owned()));
        }

        let ref toolchain = self.name;
        let ref toolchain = try!(ToolchainDesc::from_str(toolchain));
        let trip = toolchain.target_triple();
        let prefix = InstallPrefix::from(self.path.to_owned());
        let manifestation = try!(Manifestation::open(prefix, &trip));

        if let Some(manifest) = try!(manifestation.load_manifest()) {

            // Validate the component name
            let rust_pkg = manifest.packages.get("rust")
                .expect("manifest should cantain a rust package");
            let targ_pkg = rust_pkg.targets.get(&trip)
                .expect("installed manifest should have a known target");

            if targ_pkg.components.contains(&component) {
                return Err(Error::AddingRequiredComponent(self.name.to_string(), component));
            }

            if !targ_pkg.extensions.contains(&component) {
                return Err(Error::UnknownComponent(self.name.to_string(), component));
            }

            let changes = Changes {
                add_extensions: vec![component],
                remove_extensions: vec![]
            };

            try!(manifestation.update(&self.name,
                                      &manifest,
                                      changes,
                                      self.download_cfg().temp_cfg,
                                      self.download_cfg().notify_handler.clone()));

            Ok(())
        } else {
            Err(Error::ComponentsUnsupported(self.name.to_string()))
        }
    }

    pub fn remove_component(&self, component: Component) -> Result<()> {
        if !self.exists() {
            return Err(Error::ToolchainNotInstalled(self.name.to_owned()));
        }

        let ref toolchain = self.name;
        let ref toolchain = try!(ToolchainDesc::from_str(toolchain));
        let trip = toolchain.target_triple();
        let prefix = InstallPrefix::from(self.path.to_owned());
        let manifestation = try!(Manifestation::open(prefix, &trip));

        if let Some(manifest) = try!(manifestation.load_manifest()) {

            // Validate the component name
            let rust_pkg = manifest.packages.get("rust")
                .expect("manifest should cantain a rust package");
            let targ_pkg = rust_pkg.targets.get(&trip)
                .expect("installed manifest should have a known target");

            if targ_pkg.components.contains(&component) {
                return Err(Error::RemovingRequiredComponent(self.name.to_string(), component));
            }

            let dist_config = try!(manifestation.read_config()).unwrap();
            if !dist_config.components.contains(&component) {
                return Err(Error::UnknownComponent(self.name.to_string(), component));
            }

            let changes = Changes {
                add_extensions: vec![],
                remove_extensions: vec![component]
            };

            try!(manifestation.update(&self.name,
                                      &manifest,
                                      changes,
                                      self.download_cfg().temp_cfg,
                                      self.download_cfg().notify_handler.clone()));

            Ok(())
        } else {
            Err(Error::ComponentsUnsupported(self.name.to_string()))
        }
    }

    pub fn binary_file(&self, name: &str) -> PathBuf {
        let mut path = self.path.clone();
        path.push("bin");
        path.push(name.to_owned() + env::consts::EXE_SUFFIX);
        path
    }
}
<|endoftext|>


<commit_before>use crate::error;
use crate::gitignore::Gitignore;
use crate::ignore::Ignore;
use globset::{Glob, GlobSet, GlobSetBuilder};
use std::path::Path;

pub struct NotificationFilter {
    filters: GlobSet,
    filter_count: usize,
    ignores: GlobSet,
    gitignore_files: Gitignore,
    ignore_files: Ignore,
}

impl NotificationFilter {
    pub fn new(
        filters: &[String],
        ignores: &[String],
        gitignore_files: Gitignore,
        ignore_files: Ignore,
    ) -> error::Result<Self> {
        let mut filter_set_builder = GlobSetBuilder::new();
        for f in filters {
            filter_set_builder.add(Glob::new(f)?);
            debug!("Adding filter: \"{}\"", f);
        }

        let mut ignore_set_builder = GlobSetBuilder::new();
        for i in ignores {
            let mut ignore_path = Path::new(i).to_path_buf();
            if ignore_path.is_relative() && !i.starts_with('*') {
                ignore_path = Path::new("**").join(&ignore_path);
            }
            if !i.ends_with('*') {
                ignore_path = ignore_path.join("**");
            }
            let pattern = ignore_path
                .to_str()
                .expect("corrupted memory (string -> path -> string)");
            ignore_set_builder.add(Glob::new(pattern)?);
            debug!("Adding ignore: \"{}\"", pattern);
        }

        Ok(Self {
            filters: filter_set_builder.build()?,
            filter_count: filters.len(),
            ignores: ignore_set_builder.build()?,
            gitignore_files,
            ignore_files,
        })
    }

    pub fn is_excluded(&self, path: &Path) -> bool {
        if self.ignores.is_match(path) {
            debug!("Ignoring {:?}: matched ignore filter", path);
            return true;
        }

        if self.filters.is_match(path) {
            return false;
        }

        if self.ignore_files.is_excluded(path) {
            debug!("Ignoring {:?}: matched ignore file", path);
            return true;
        }

        if self.gitignore_files.is_excluded(path) {
            debug!("Ignoring {:?}: matched gitignore file", path);
            return true;
        }

        if self.filter_count > 0 {
            debug!("Ignoring {:?}: did not match any given filters", path);
        }

        self.filter_count > 0
    }
}

#[cfg(test)]
mod tests {
    use super::NotificationFilter;
    use crate::gitignore;
    use crate::ignore;
    use std::path::Path;

    #[test]
    fn test_allows_everything_by_default() {
        let filter = NotificationFilter::new(&[], &[], gitignore::load(&[]), ignore::load(&[]))
            .expect("test filter errors");

        assert!(!filter.is_excluded(Path::new("foo")));
    }

    #[test]
    fn test_filename() {
        let filter = NotificationFilter::new(
            &[],
            &["test.json".into()],
            gitignore::load(&[]),
            ignore::load(&[]),
        )
        .expect("test filter errors");

        assert!(filter.is_excluded(Path::new("/path/to/test.json")));
        assert!(filter.is_excluded(Path::new("test.json")));
    }

    #[test]
    fn test_multiple_filters() {
        let filters = &["*.rs".into(), "*.toml".into()];
        let filter = NotificationFilter::new(filters, &[], gitignore::load(&[]), ignore::load(&[]))
            .expect("test filter errors");

        assert!(!filter.is_excluded(Path::new("hello.rs")));
        assert!(!filter.is_excluded(Path::new("Cargo.toml")));
        assert!(filter.is_excluded(Path::new("README.md")));
    }

    #[test]
    fn test_multiple_ignores() {
        let ignores = &["*.rs".into(), "*.toml".into()];
        let filter = NotificationFilter::new(&[], ignores, gitignore::load(&[]), ignore::load(&[]))
            .expect("test filter errors");

        assert!(filter.is_excluded(Path::new("hello.rs")));
        assert!(filter.is_excluded(Path::new("Cargo.toml")));
        assert!(!filter.is_excluded(Path::new("README.md")));
    }

    #[test]
    fn test_ignores_take_precedence() {
        let ignores = &["*.rs".into(), "*.toml".into()];
        let filter =
            NotificationFilter::new(ignores, ignores, gitignore::load(&[]), ignore::load(&[]))
                .expect("test filter errors");

        assert!(filter.is_excluded(Path::new("hello.rs")));
        assert!(filter.is_excluded(Path::new("Cargo.toml")));
        assert!(filter.is_excluded(Path::new("README.md")));
    }

    #[test]
    fn test_recursive_directory_ignore() {
        let ignores = &["target".into()];
        let filter = NotificationFilter::new(&[], ignores, gitignore::load(&[]), ignore::load(&[]))
            .expect("test filter errors");

        assert!(filter.is_excluded(Path::new("target")));
        // Make sure that sub-directories/-files are recursively ignored.
        assert!(filter.is_excluded(Path::new("target/rls")));
        assert!(filter.is_excluded(Path::new("target/rls/debug")));
        assert!(!filter.is_excluded(Path::new("hello.rs")));
        assert!(!filter.is_excluded(Path::new("Cargo.toml")));
    }
}
<commit_msg>update the test to make sure that files matching part of the ignore are not excluded<commit_after>use crate::error;
use crate::gitignore::Gitignore;
use crate::ignore::Ignore;
use globset::{Glob, GlobSet, GlobSetBuilder};
use std::path::Path;

pub struct NotificationFilter {
    filters: GlobSet,
    filter_count: usize,
    ignores: GlobSet,
    gitignore_files: Gitignore,
    ignore_files: Ignore,
}

impl NotificationFilter {
    pub fn new(
        filters: &[String],
        ignores: &[String],
        gitignore_files: Gitignore,
        ignore_files: Ignore,
    ) -> error::Result<Self> {
        let mut filter_set_builder = GlobSetBuilder::new();
        for f in filters {
            filter_set_builder.add(Glob::new(f)?);
            debug!("Adding filter: \"{}\"", f);
        }

        let mut ignore_set_builder = GlobSetBuilder::new();
        for i in ignores {
            let mut ignore_path = Path::new(i).to_path_buf();
            if ignore_path.is_relative() && !i.starts_with('*') {
                ignore_path = Path::new("**").join(&ignore_path);
            }
            if !i.ends_with('*') {
                ignore_path = ignore_path.join("**");
            }
            let pattern = ignore_path
                .to_str()
                .expect("corrupted memory (string -> path -> string)");
            ignore_set_builder.add(Glob::new(pattern)?);
            debug!("Adding ignore: \"{}\"", pattern);
        }

        Ok(Self {
            filters: filter_set_builder.build()?,
            filter_count: filters.len(),
            ignores: ignore_set_builder.build()?,
            gitignore_files,
            ignore_files,
        })
    }

    pub fn is_excluded(&self, path: &Path) -> bool {
        if self.ignores.is_match(path) {
            debug!("Ignoring {:?}: matched ignore filter", path);
            return true;
        }

        if self.filters.is_match(path) {
            return false;
        }

        if self.ignore_files.is_excluded(path) {
            debug!("Ignoring {:?}: matched ignore file", path);
            return true;
        }

        if self.gitignore_files.is_excluded(path) {
            debug!("Ignoring {:?}: matched gitignore file", path);
            return true;
        }

        if self.filter_count > 0 {
            debug!("Ignoring {:?}: did not match any given filters", path);
        }

        self.filter_count > 0
    }
}

#[cfg(test)]
mod tests {
    use super::NotificationFilter;
    use crate::gitignore;
    use crate::ignore;
    use std::path::Path;

    #[test]
    fn test_allows_everything_by_default() {
        let filter = NotificationFilter::new(&[], &[], gitignore::load(&[]), ignore::load(&[]))
            .expect("test filter errors");

        assert!(!filter.is_excluded(Path::new("foo")));
    }

    #[test]
    fn test_filename() {
        let filter = NotificationFilter::new(
            &[],
            &["test.json".into()],
            gitignore::load(&[]),
            ignore::load(&[]),
        )
        .expect("test filter errors");

        assert!(filter.is_excluded(Path::new("/path/to/test.json")));
        assert!(filter.is_excluded(Path::new("test.json")));
    }

    #[test]
    fn test_multiple_filters() {
        let filters = &["*.rs".into(), "*.toml".into()];
        let filter = NotificationFilter::new(filters, &[], gitignore::load(&[]), ignore::load(&[]))
            .expect("test filter errors");

        assert!(!filter.is_excluded(Path::new("hello.rs")));
        assert!(!filter.is_excluded(Path::new("Cargo.toml")));
        assert!(filter.is_excluded(Path::new("README.md")));
    }

    #[test]
    fn test_multiple_ignores() {
        let ignores = &["*.rs".into(), "*.toml".into()];
        let filter = NotificationFilter::new(&[], ignores, gitignore::load(&[]), ignore::load(&[]))
            .expect("test filter errors");

        assert!(filter.is_excluded(Path::new("hello.rs")));
        assert!(filter.is_excluded(Path::new("Cargo.toml")));
        assert!(!filter.is_excluded(Path::new("README.md")));
    }

    #[test]
    fn test_ignores_take_precedence() {
        let ignores = &["*.rs".into(), "*.toml".into()];
        let filter =
            NotificationFilter::new(ignores, ignores, gitignore::load(&[]), ignore::load(&[]))
                .expect("test filter errors");

        assert!(filter.is_excluded(Path::new("hello.rs")));
        assert!(filter.is_excluded(Path::new("Cargo.toml")));
        assert!(filter.is_excluded(Path::new("README.md")));
    }

    #[test]
    fn test_recursive_directory_ignore() {
        let ignores = &["target".into()];
        let filter = NotificationFilter::new(&[], ignores, gitignore::load(&[]), ignore::load(&[]))
            .expect("test filter errors");

        assert!(filter.is_excluded(Path::new("target")));
        // Make sure that sub-directories/-files are recursively ignored.
        assert!(filter.is_excluded(Path::new("target/rls")));
        assert!(filter.is_excluded(Path::new("target/rls/debug")));
        // Assert that files containing subsets of the path are not ignored.
        assert!(!filter.is_excluded(Path::new("target-file")));
        assert!(!filter.is_excluded(Path::new("hello.rs")));
        assert!(!filter.is_excluded(Path::new("Cargo.toml")));
    }
}
<|endoftext|>


<commit_before>use chrono::{DateTime, FixedOffset};
use std::fmt::{Display, Formatter, Result as FmtResult};
use super::deserialize_sync_user;
use model::*;

#[cfg(feature = "model")]
use std::borrow::Cow;
#[cfg(all(feature = "cache", feature = "model"))]
use CACHE;
#[cfg(all(feature = "cache", feature = "model"))]
use internal::prelude::*;
#[cfg(all(feature = "cache", feature = "model"))]
use http;
#[cfg(all(feature = "builder", feature = "cache", feature = "model"))]
use builder::EditMember;
#[cfg(all(feature = "cache", feature = "model", feature = "utils"))]
use utils::Colour;

/// A trait for allowing both u8 or &str or (u8, &str) to be passed into the `ban` methods in `Guild` and `Member`.
pub trait BanOptions {
    fn dmd(&self) -> u8 { 0 }
    fn reason(&self) -> &str { "" }
}

impl BanOptions for u8 {
    fn dmd(&self) -> u8 { *self }
}

impl BanOptions for str {
    fn reason(&self) -> &str { self }
}

impl<'a> BanOptions for &'a str {
    fn reason(&self) -> &str { self }
}

impl BanOptions for String {
    fn reason(&self) -> &str { self }
}

impl<'a> BanOptions for (u8, &'a str) {
    fn dmd(&self) -> u8 { self.0 }

    fn reason(&self) -> &str { self.1 }
}

impl BanOptions for (u8, String) {
    fn dmd(&self) -> u8 { self.0 }

    fn reason(&self) -> &str { &self.1 }
}

/// Information about a member of a guild.
#[derive(Clone, Debug, Deserialize)]
pub struct Member {
    /// Indicator of whether the member can hear in voice channels.
    pub deaf: bool,
    /// The unique Id of the guild that the member is a part of.
    pub guild_id: GuildId,
    /// Timestamp representing the date when the member joined.
    pub joined_at: Option<DateTime<FixedOffset>>,
    /// Indicator of whether the member can speak in voice channels.
    pub mute: bool,
    /// The member's nickname, if present.
    ///
    /// Can't be longer than 32 characters.
    pub nick: Option<String>,
    /// Vector of Ids of [`Role`]s given to the member.
    pub roles: Vec<RoleId>,
    /// Attached User struct.
    #[serde(deserialize_with = "deserialize_sync_user")]
    pub user: Arc<RwLock<User>>,
}

#[cfg(feature = "model")]
impl Member {
    /// Adds a [`Role`] to the member, editing its roles in-place if the request
    /// was successful.
    ///
    /// **Note**: Requires the [Manage Roles] permission.
    ///
    /// [`Role`]: struct.Role.html
    /// [Manage Roles]: permissions/constant.MANAGE_ROLES.html
    #[cfg(feature = "cache")]
    pub fn add_role<R: Into<RoleId>>(&mut self, role_id: R) -> Result<()> {
        let role_id = role_id.into();

        if self.roles.contains(&role_id) {
            return Ok(());
        }

        match http::add_member_role(self.guild_id.0, self.user.read().unwrap().id.0, role_id.0) {
            Ok(()) => {
                self.roles.push(role_id);

                Ok(())
            },
            Err(why) => Err(why),
        }
    }

    /// Adds one or multiple [`Role`]s to the member, editing
    /// its roles in-place if the request was successful.
    ///
    /// **Note**: Requires the [Manage Roles] permission.
    ///
    /// [`Role`]: struct.Role.html
    /// [Manage Roles]: permissions/constant.MANAGE_ROLES.html
    #[cfg(feature = "cache")]
    pub fn add_roles(&mut self, role_ids: &[RoleId]) -> Result<()> {
        self.roles.extend_from_slice(role_ids);

        let map = EditMember::default().roles(&self.roles).0;

        match http::edit_member(self.guild_id.0, self.user.read().unwrap().id.0, &map) {
            Ok(()) => Ok(()),
            Err(why) => {
                self.roles.retain(|r| !role_ids.contains(r));

                Err(why)
            },
        }
    }

    /// Ban the member from its guild, deleting the last X number of
    /// days' worth of messages.
    ///
    /// **Note**: Requires the [Ban Members] permission.
    ///
    /// # Errors
    ///
    /// Returns a [`ModelError::GuildNotFound`] if the guild could not be
    /// found.
    ///
    /// [`ModelError::GuildNotFound`]: enum.ModelError.html#variant.GuildNotFound
    ///
    /// [Ban Members]: permissions/constant.BAN_MEMBERS.html
    #[cfg(feature = "cache")]
    pub fn ban<BO: BanOptions>(&self, ban_options: BO) -> Result<()> {
        let dmd = ban_options.dmd();
        if dmd > 7 {
            return Err(Error::Model(ModelError::DeleteMessageDaysAmount(dmd)));
        }

        let reason = ban_options.reason();

        if reason.len() > 512 {
            return Err(Error::ExceededLimit(reason.to_string(), 512));
        }

        http::ban_user(
            self.guild_id.0,
            self.user.read().unwrap().id.0,
            dmd,
            &*reason,
        )
    }

    /// Determines the member's colour.
    #[cfg(all(feature = "cache", feature = "utils"))]
    pub fn colour(&self) -> Option<Colour> {
        let cache = CACHE.read().unwrap();
        let guild = try_opt!(cache.guilds.get(&self.guild_id)).read().unwrap();

        let mut roles = self.roles
            .iter()
            .filter_map(|role_id| guild.roles.get(role_id))
            .collect::<Vec<&Role>>();
        roles.sort_by(|a, b| b.cmp(a));

        let default = Colour::default();

        roles
            .iter()
            .find(|r| r.colour.0 != default.0)
            .map(|r| r.colour)
    }

    /// Calculates the member's display name.
    ///
    /// The nickname takes priority over the member's username if it exists.
    #[inline]
    pub fn display_name(&self) -> Cow<String> {
        self.nick
            .as_ref()
            .map(Cow::Borrowed)
            .unwrap_or_else(|| Cow::Owned(self.user.read().unwrap().name.clone()))
    }

    /// Returns the DiscordTag of a Member, taking possible nickname into account.
    #[inline]
    pub fn distinct(&self) -> String {
        format!(
            "{}#{}",
            self.display_name(),
            self.user.read().unwrap().discriminator
        )
    }

    /// Edits the member with the given data. See [`Guild::edit_member`] for
    /// more information.
    ///
    /// See [`EditMember`] for the permission(s) required for separate builder
    /// methods, as well as usage of this.
    ///
    /// [`Guild::edit_member`]: ../model/struct.Guild.html#method.edit_member
    /// [`EditMember`]: ../builder/struct.EditMember.html
    #[cfg(feature = "cache")]
    pub fn edit<F: FnOnce(EditMember) -> EditMember>(&self, f: F) -> Result<()> {
        let map = f(EditMember::default()).0;

        http::edit_member(self.guild_id.0, self.user.read().unwrap().id.0, &map)
    }

    /// Kick the member from the guild.
    ///
    /// **Note**: Requires the [Kick Members] permission.
    ///
    /// # Examples
    ///
    /// Kick a member from its guild:
    ///
    /// ```rust,ignore
    /// // assuming a `member` has already been bound
    /// match member.kick() {
    ///     Ok(()) => println!("Successfully kicked member"),
    ///     Err(Error::Model(ModelError::GuildNotFound)) => {
    ///         println!("Couldn't determine guild of member");
    ///     },
    ///     Err(Error::Model(ModelError::InvalidPermissions(missing_perms))) => {
    ///         println!("Didn't have permissions; missing: {:?}", missing_perms);
    ///     },
    ///     _ => {},
    /// }
    /// ```
    ///
    /// # Errors
    ///
    /// Returns a [`ModelError::GuildNotFound`] if the Id of the member's guild
    /// could not be determined.
    ///
    /// If the `cache` is enabled, returns a [`ModelError::InvalidPermissions`]
    /// if the current user does not have permission to perform the kick.
    ///
    /// [`ModelError::GuildNotFound`]: enum.ModelError.html#variant.GuildNotFound
    /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions
    /// [Kick Members]: permissions/constant.KICK_MEMBERS.html
    pub fn kick(&self) -> Result<()> {
        #[cfg(feature = "cache")]
        {
            let req = Permissions::KICK_MEMBERS;

            let has_perms = CACHE
                .read()
                .unwrap()
                .guilds
                .get(&self.guild_id)
                .map(|guild| guild.read().unwrap().has_perms(req));

            if let Some(false) = has_perms {
                return Err(Error::Model(ModelError::InvalidPermissions(req)));
            }
        }

        self.guild_id.kick(self.user.read().unwrap().id)
    }

    /// Returns the permissions for the member.
    ///
    /// # Examples
    ///
    /// ```rust,ignore
    /// // assuming there's a `member` variable gotten from anything.
    /// println!("The permission bits for the member are: {}",
    /// member.permissions().expect("permissions").bits);
    /// ```
    ///
    /// # Errors
    ///
    /// Returns a [`ModelError::GuildNotFound`] if the guild the member's in could not be
    /// found in the cache.
    ///
    /// And/or returns [`ModelError::ItemMissing`] if the "default channel" of the guild is not
    /// found.
    ///
    /// [`ModelError::GuildNotFound`]: enum.ModelError.html#variant.GuildNotFound
    /// [`ModelError::ItemMissing`]: enum.ModelError.html#variant.ItemMissing
    #[cfg(feature = "cache")]
    pub fn permissions(&self) -> Result<Permissions> {
        let guild = match self.guild_id.find() {
            Some(guild) => guild,
            None => return Err(From::from(ModelError::GuildNotFound)),
        };

        let guild = guild.read().unwrap();

        let default_channel = match guild.default_channel() {
            Some(dc) => dc,
            None => return Err(From::from(ModelError::ItemMissing)),
        };

        Ok(
            guild
                .permissions_in(default_channel.id, self.user.read().unwrap().id),
        )
    }

    /// Removes a [`Role`] from the member, editing its roles in-place if the
    /// request was successful.
    ///
    /// **Note**: Requires the [Manage Roles] permission.
    ///
    /// [`Role`]: struct.Role.html
    /// [Manage Roles]: permissions/constant.MANAGE_ROLES.html
    #[cfg(feature = "cache")]
    pub fn remove_role<R: Into<RoleId>>(&mut self, role_id: R) -> Result<()> {
        let role_id = role_id.into();

        if !self.roles.contains(&role_id) {
            return Ok(());
        }

        match http::remove_member_role(self.guild_id.0, self.user.read().unwrap().id.0, role_id.0) {
            Ok(()) => {
                self.roles.retain(|r| r.0 != role_id.0);

                Ok(())
            },
            Err(why) => Err(why),
        }
    }

    /// Removes one or multiple [`Role`]s from the member.
    ///
    /// **Note**: Requires the [Manage Roles] permission.
    ///
    /// [`Role`]: struct.Role.html
    /// [Manage Roles]: permissions/constant.MANAGE_ROLES.html
    #[cfg(feature = "cache")]
    pub fn remove_roles(&mut self, role_ids: &[RoleId]) -> Result<()> {
        self.roles.retain(|r| !role_ids.contains(r));

        let map = EditMember::default().roles(&self.roles).0;

        match http::edit_member(self.guild_id.0, self.user.read().unwrap().id.0, &map) {
            Ok(()) => Ok(()),
            Err(why) => {
                self.roles.extend_from_slice(role_ids);

                Err(why)
            },
        }
    }

    /// Retrieves the full role data for the user's roles.
    ///
    /// This is shorthand for manually searching through the CACHE.
    ///
    /// If role data can not be found for the member, then `None` is returned.
    #[cfg(feature = "cache")]
    pub fn roles(&self) -> Option<Vec<Role>> {
        self
            .guild_id
            .find()
            .map(|g| g
                .read()
                .unwrap()
                .roles
                .values()
                .filter(|role| self.roles.contains(&role.id))
                .cloned()
                .collect())
    }

    /// Unbans the [`User`] from the guild.
    ///
    /// **Note**: Requires the [Ban Members] permission.
    ///
    /// # Errors
    ///
    /// If the `cache` is enabled, returns a [`ModelError::InvalidPermissions`]
    /// if the current user does not have permission to perform bans.
    ///
    /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions
    /// [`User`]: struct.User.html
    /// [Ban Members]: permissions/constant.BAN_MEMBERS.html
    #[cfg(feature = "cache")]
    pub fn unban(&self) -> Result<()> {
        http::remove_ban(self.guild_id.0, self.user.read().unwrap().id.0)
    }
}

impl Display for Member {
    /// Mentions the user so that they receive a notification.
    ///
    /// # Examples
    ///
    /// ```rust,ignore
    /// // assumes a `member` has already been bound
    /// println!("{} is a member!", member);
    /// ```
    ///
    // This is in the format of `<@USER_ID>`.
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        Display::fmt(&self.user.read().unwrap().mention(), f)
    }
}
<commit_msg>Make Member::permissions return guild permissions<commit_after>use chrono::{DateTime, FixedOffset};
use std::fmt::{Display, Formatter, Result as FmtResult};
use super::deserialize_sync_user;
use model::*;

#[cfg(feature = "model")]
use std::borrow::Cow;
#[cfg(all(feature = "cache", feature = "model"))]
use CACHE;
#[cfg(all(feature = "cache", feature = "model"))]
use internal::prelude::*;
#[cfg(all(feature = "cache", feature = "model"))]
use http;
#[cfg(all(feature = "builder", feature = "cache", feature = "model"))]
use builder::EditMember;
#[cfg(all(feature = "cache", feature = "model", feature = "utils"))]
use utils::Colour;

/// A trait for allowing both u8 or &str or (u8, &str) to be passed into the `ban` methods in `Guild` and `Member`.
pub trait BanOptions {
    fn dmd(&self) -> u8 { 0 }
    fn reason(&self) -> &str { "" }
}

impl BanOptions for u8 {
    fn dmd(&self) -> u8 { *self }
}

impl BanOptions for str {
    fn reason(&self) -> &str { self }
}

impl<'a> BanOptions for &'a str {
    fn reason(&self) -> &str { self }
}

impl BanOptions for String {
    fn reason(&self) -> &str { self }
}

impl<'a> BanOptions for (u8, &'a str) {
    fn dmd(&self) -> u8 { self.0 }

    fn reason(&self) -> &str { self.1 }
}

impl BanOptions for (u8, String) {
    fn dmd(&self) -> u8 { self.0 }

    fn reason(&self) -> &str { &self.1 }
}

/// Information about a member of a guild.
#[derive(Clone, Debug, Deserialize)]
pub struct Member {
    /// Indicator of whether the member can hear in voice channels.
    pub deaf: bool,
    /// The unique Id of the guild that the member is a part of.
    pub guild_id: GuildId,
    /// Timestamp representing the date when the member joined.
    pub joined_at: Option<DateTime<FixedOffset>>,
    /// Indicator of whether the member can speak in voice channels.
    pub mute: bool,
    /// The member's nickname, if present.
    ///
    /// Can't be longer than 32 characters.
    pub nick: Option<String>,
    /// Vector of Ids of [`Role`]s given to the member.
    pub roles: Vec<RoleId>,
    /// Attached User struct.
    #[serde(deserialize_with = "deserialize_sync_user")]
    pub user: Arc<RwLock<User>>,
}

#[cfg(feature = "model")]
impl Member {
    /// Adds a [`Role`] to the member, editing its roles in-place if the request
    /// was successful.
    ///
    /// **Note**: Requires the [Manage Roles] permission.
    ///
    /// [`Role`]: struct.Role.html
    /// [Manage Roles]: permissions/constant.MANAGE_ROLES.html
    #[cfg(feature = "cache")]
    pub fn add_role<R: Into<RoleId>>(&mut self, role_id: R) -> Result<()> {
        let role_id = role_id.into();

        if self.roles.contains(&role_id) {
            return Ok(());
        }

        match http::add_member_role(self.guild_id.0, self.user.read().unwrap().id.0, role_id.0) {
            Ok(()) => {
                self.roles.push(role_id);

                Ok(())
            },
            Err(why) => Err(why),
        }
    }

    /// Adds one or multiple [`Role`]s to the member, editing
    /// its roles in-place if the request was successful.
    ///
    /// **Note**: Requires the [Manage Roles] permission.
    ///
    /// [`Role`]: struct.Role.html
    /// [Manage Roles]: permissions/constant.MANAGE_ROLES.html
    #[cfg(feature = "cache")]
    pub fn add_roles(&mut self, role_ids: &[RoleId]) -> Result<()> {
        self.roles.extend_from_slice(role_ids);

        let map = EditMember::default().roles(&self.roles).0;

        match http::edit_member(self.guild_id.0, self.user.read().unwrap().id.0, &map) {
            Ok(()) => Ok(()),
            Err(why) => {
                self.roles.retain(|r| !role_ids.contains(r));

                Err(why)
            },
        }
    }

    /// Ban the member from its guild, deleting the last X number of
    /// days' worth of messages.
    ///
    /// **Note**: Requires the [Ban Members] permission.
    ///
    /// # Errors
    ///
    /// Returns a [`ModelError::GuildNotFound`] if the guild could not be
    /// found.
    ///
    /// [`ModelError::GuildNotFound`]: enum.ModelError.html#variant.GuildNotFound
    ///
    /// [Ban Members]: permissions/constant.BAN_MEMBERS.html
    #[cfg(feature = "cache")]
    pub fn ban<BO: BanOptions>(&self, ban_options: BO) -> Result<()> {
        let dmd = ban_options.dmd();
        if dmd > 7 {
            return Err(Error::Model(ModelError::DeleteMessageDaysAmount(dmd)));
        }

        let reason = ban_options.reason();

        if reason.len() > 512 {
            return Err(Error::ExceededLimit(reason.to_string(), 512));
        }

        http::ban_user(
            self.guild_id.0,
            self.user.read().unwrap().id.0,
            dmd,
            &*reason,
        )
    }

    /// Determines the member's colour.
    #[cfg(all(feature = "cache", feature = "utils"))]
    pub fn colour(&self) -> Option<Colour> {
        let cache = CACHE.read().unwrap();
        let guild = try_opt!(cache.guilds.get(&self.guild_id)).read().unwrap();

        let mut roles = self.roles
            .iter()
            .filter_map(|role_id| guild.roles.get(role_id))
            .collect::<Vec<&Role>>();
        roles.sort_by(|a, b| b.cmp(a));

        let default = Colour::default();

        roles
            .iter()
            .find(|r| r.colour.0 != default.0)
            .map(|r| r.colour)
    }

    /// Calculates the member's display name.
    ///
    /// The nickname takes priority over the member's username if it exists.
    #[inline]
    pub fn display_name(&self) -> Cow<String> {
        self.nick
            .as_ref()
            .map(Cow::Borrowed)
            .unwrap_or_else(|| Cow::Owned(self.user.read().unwrap().name.clone()))
    }

    /// Returns the DiscordTag of a Member, taking possible nickname into account.
    #[inline]
    pub fn distinct(&self) -> String {
        format!(
            "{}#{}",
            self.display_name(),
            self.user.read().unwrap().discriminator
        )
    }

    /// Edits the member with the given data. See [`Guild::edit_member`] for
    /// more information.
    ///
    /// See [`EditMember`] for the permission(s) required for separate builder
    /// methods, as well as usage of this.
    ///
    /// [`Guild::edit_member`]: ../model/struct.Guild.html#method.edit_member
    /// [`EditMember`]: ../builder/struct.EditMember.html
    #[cfg(feature = "cache")]
    pub fn edit<F: FnOnce(EditMember) -> EditMember>(&self, f: F) -> Result<()> {
        let map = f(EditMember::default()).0;

        http::edit_member(self.guild_id.0, self.user.read().unwrap().id.0, &map)
    }

    /// Kick the member from the guild.
    ///
    /// **Note**: Requires the [Kick Members] permission.
    ///
    /// # Examples
    ///
    /// Kick a member from its guild:
    ///
    /// ```rust,ignore
    /// // assuming a `member` has already been bound
    /// match member.kick() {
    ///     Ok(()) => println!("Successfully kicked member"),
    ///     Err(Error::Model(ModelError::GuildNotFound)) => {
    ///         println!("Couldn't determine guild of member");
    ///     },
    ///     Err(Error::Model(ModelError::InvalidPermissions(missing_perms))) => {
    ///         println!("Didn't have permissions; missing: {:?}", missing_perms);
    ///     },
    ///     _ => {},
    /// }
    /// ```
    ///
    /// # Errors
    ///
    /// Returns a [`ModelError::GuildNotFound`] if the Id of the member's guild
    /// could not be determined.
    ///
    /// If the `cache` is enabled, returns a [`ModelError::InvalidPermissions`]
    /// if the current user does not have permission to perform the kick.
    ///
    /// [`ModelError::GuildNotFound`]: enum.ModelError.html#variant.GuildNotFound
    /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions
    /// [Kick Members]: permissions/constant.KICK_MEMBERS.html
    pub fn kick(&self) -> Result<()> {
        #[cfg(feature = "cache")]
        {
            let req = Permissions::KICK_MEMBERS;

            let has_perms = CACHE
                .read()
                .unwrap()
                .guilds
                .get(&self.guild_id)
                .map(|guild| guild.read().unwrap().has_perms(req));

            if let Some(false) = has_perms {
                return Err(Error::Model(ModelError::InvalidPermissions(req)));
            }
        }

        self.guild_id.kick(self.user.read().unwrap().id)
    }

    /// Returns the guild-level permissions for the member.
    ///
    /// # Examples
    ///
    /// ```rust,ignore
    /// // assuming there's a `member` variable gotten from anything.
    /// println!("The permission bits for the member are: {}",
    /// member.permissions().expect("permissions").bits);
    /// ```
    ///
    /// # Errors
    ///
    /// Returns a [`ModelError::GuildNotFound`] if the guild the member's in could not be
    /// found in the cache.
    ///
    /// And/or returns [`ModelError::ItemMissing`] if the "default channel" of the guild is not
    /// found.
    ///
    /// [`ModelError::GuildNotFound`]: enum.ModelError.html#variant.GuildNotFound
    /// [`ModelError::ItemMissing`]: enum.ModelError.html#variant.ItemMissing
    #[cfg(feature = "cache")]
    pub fn permissions(&self) -> Result<Permissions> {
        let guild = match self.guild_id.find() {
            Some(guild) => guild,
            None => return Err(From::from(ModelError::GuildNotFound)),
        };

        let reader = guild.read().unwrap();

        Ok(reader.member_permissions(self.user.read().unwrap().id))
    }

    /// Removes a [`Role`] from the member, editing its roles in-place if the
    /// request was successful.
    ///
    /// **Note**: Requires the [Manage Roles] permission.
    ///
    /// [`Role`]: struct.Role.html
    /// [Manage Roles]: permissions/constant.MANAGE_ROLES.html
    #[cfg(feature = "cache")]
    pub fn remove_role<R: Into<RoleId>>(&mut self, role_id: R) -> Result<()> {
        let role_id = role_id.into();

        if !self.roles.contains(&role_id) {
            return Ok(());
        }

        match http::remove_member_role(self.guild_id.0, self.user.read().unwrap().id.0, role_id.0) {
            Ok(()) => {
                self.roles.retain(|r| r.0 != role_id.0);

                Ok(())
            },
            Err(why) => Err(why),
        }
    }

    /// Removes one or multiple [`Role`]s from the member.
    ///
    /// **Note**: Requires the [Manage Roles] permission.
    ///
    /// [`Role`]: struct.Role.html
    /// [Manage Roles]: permissions/constant.MANAGE_ROLES.html
    #[cfg(feature = "cache")]
    pub fn remove_roles(&mut self, role_ids: &[RoleId]) -> Result<()> {
        self.roles.retain(|r| !role_ids.contains(r));

        let map = EditMember::default().roles(&self.roles).0;

        match http::edit_member(self.guild_id.0, self.user.read().unwrap().id.0, &map) {
            Ok(()) => Ok(()),
            Err(why) => {
                self.roles.extend_from_slice(role_ids);

                Err(why)
            },
        }
    }

    /// Retrieves the full role data for the user's roles.
    ///
    /// This is shorthand for manually searching through the CACHE.
    ///
    /// If role data can not be found for the member, then `None` is returned.
    #[cfg(feature = "cache")]
    pub fn roles(&self) -> Option<Vec<Role>> {
        self
            .guild_id
            .find()
            .map(|g| g
                .read()
                .unwrap()
                .roles
                .values()
                .filter(|role| self.roles.contains(&role.id))
                .cloned()
                .collect())
    }

    /// Unbans the [`User`] from the guild.
    ///
    /// **Note**: Requires the [Ban Members] permission.
    ///
    /// # Errors
    ///
    /// If the `cache` is enabled, returns a [`ModelError::InvalidPermissions`]
    /// if the current user does not have permission to perform bans.
    ///
    /// [`ModelError::InvalidPermissions`]: enum.ModelError.html#variant.InvalidPermissions
    /// [`User`]: struct.User.html
    /// [Ban Members]: permissions/constant.BAN_MEMBERS.html
    #[cfg(feature = "cache")]
    pub fn unban(&self) -> Result<()> {
        http::remove_ban(self.guild_id.0, self.user.read().unwrap().id.0)
    }
}

impl Display for Member {
    /// Mentions the user so that they receive a notification.
    ///
    /// # Examples
    ///
    /// ```rust,ignore
    /// // assumes a `member` has already been bound
    /// println!("{} is a member!", member);
    /// ```
    ///
    // This is in the format of `<@USER_ID>`.
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        Display::fmt(&self.user.read().unwrap().mention(), f)
    }
}
<|endoftext|>


<commit_before>endogenous	Y, I, PAI, R, DI

exogenous EI, EPAI, EY

parameters beta_lag, beta_lead, beta_r, gam_lag, gam_y, lamb_lag,
lamb_lead, lamb_y, sigi, sigpai, sigy

model(linear);

   Y=beta_lag*Y(-1)+beta_lead*Y(+1)-beta_r*R(-1)+sigy*EY;

   PAI=lamb_lag*PAI(-1)+lamb_lead*PAI(+1)+lamb_y*Y(-1)+sigpai*EPAI;

   // Taylor rule
   I=gam_lag*I(-1)+(1-gam_lag)*(PAI(+4)+gam_y*Y)+sigi*EI;

   R=I-PAI(+1);
   // interest rate smoothing
   DI=I-I(-1);

// no need to specify a list of observable variables

// specify the planner's objective.
planner_objective -.5*(1*PAI^2+.3*Y^2+0.9*DI^2);//{discount = 0.99}
// The discount factor does not play a role. So I can... neglect it.
//planner_objective{discount = 0.99} -.5*(1*PAI^2+.3*Y^2+0.9*DI^2);

parameterization;
// not estimated
	beta_lag 	 ,0.5000;
	beta_lead	 ,0.4000;
	beta_r  	 ,0.9000;
	// estimate the monetary policy parameter
	gam_lag 	 ,0.6000, 0.0000, 1.0000;
	gam_y   	 ,0.5000, 0.0000, 10.0000;
	lamb_lag	 ,0.8000;
	lamb_lead    ,0.1000;
	lamb_y  	 ,0.3000;
	sigi   		 ,0.5000;
	sigpai 		 ,0.5000;
	sigy   		 ,0.5000;
<commit_msg>Update of the OSR example (for Paul McNelis)<commit_after>endogenous	Y, I, PAI, R, DI

exogenous EI, EPAI, EY

parameters beta_lag, beta_lead, beta_r, gam_lag, gam_y, lamb_lag,
lamb_lead, lamb_y, sigi, sigpai, sigy

model(linear);

   Y=beta_lag*Y(-1)+beta_lead*Y(+1)-beta_r*R(-1)+sigy*EY;

   PAI=lamb_lag*PAI(-1)+lamb_lead*PAI(+1)+lamb_y*Y(-1)+sigpai*EPAI;

   % Taylor rule
   I=gam_lag*I(-1)+(1-gam_lag)*(PAI(+4)+gam_y*Y)+sigi*EI;

   R=I-PAI(+1);
   % interest rate smoothing
   DI=I-I(-1);

% no need to specify a list of observable variables

% specify the planner's objective.
planner_objective -.5*(1*PAI^2+.3*Y^2+0.9*DI^2);%{discount = 0.99}
% The discount factor does not play a role. So I can... neglect it.
%planner_objective{discount = 0.99} -.5*(1*PAI^2+.3*Y^2+0.9*DI^2);

parameterization;
% not estimated
	beta_lag 	 ,0.5000;
	beta_lead	 ,0.4000;
	beta_r  	 ,0.9000;
	% estimate the monetary policy parameter
	gam_lag 	 ,0.6000, 0.0000, 1.0000;
	gam_y   	 ,0.5000, 0.0000, 10.0000;
	lamb_lag	 ,0.8000;
	lamb_lead    ,0.1000;
	lamb_y  	 ,0.3000;
	sigi   		 ,0.5000;
	sigpai 		 ,0.5000;
	sigy   		 ,0.5000;
<|endoftext|>


<commit_before>extern crate piston;
extern crate graphics;
extern crate glutin_window;
extern crate opengl_graphics;

use piston::window::WindowSettings;
use piston::event_loop::*;
use piston::input::*;
use glutin_window::GlutinWindow as Window;
use opengl_graphics::{ GlGraphics, OpenGL };

struct App {
    gl: GlGraphics,
    triangles: Vec<[[f64; 2]; 4]>
}

impl App {
    fn render(&mut self, args: &RenderArgs) {
        use graphics::*;

        const WHITE: [f32; 4] = [1.0, 1.0, 1.0, 1.0];
        const BLACK: [f32; 4] = [0.0, 0.0, 0.0, 1.0];

        let triangles = &self.triangles;

        self.gl.draw(args.viewport(), |c, gl| {
            clear(WHITE, gl);

            let transform = c.transform.trans(0.0, 0.0);

            for t in triangles {
              polygon(BLACK, t, transform, gl);
            }
        });
    }
}

fn sierpinski(depth: i32, vertices: [[f64; 2]; 4]) -> Vec<[[f64; 2]; 4]> {
    let mut triangles = vec![];

    match depth {
        0 => triangles,
        d @ _ => {
            // TODO: Break into four triangles. Push first. And then recurse on the rest.
            triangles.push([[100.0, 200.0], [200.0, 200.0],
                            [150.0, 100.0], [100.0, 200.0]]);

            for t in sierpinski(d - 1, [[100.0, 200.0], [200.0, 200.0], [150.0, 100.0], [100.0, 200.0]]) {
                triangles.push(t);
            }
            for t in sierpinski(d - 1, [[100.0, 200.0], [200.0, 200.0], [150.0, 100.0], [100.0, 200.0]]) {
                triangles.push(t);
            }
            for t in sierpinski(d - 1, [[100.0, 200.0], [200.0, 200.0], [150.0, 100.0], [100.0, 200.0]]) {
                triangles.push(t);
            }

            triangles
        },
    }
}

fn main() {
    let opengl = OpenGL::V3_2;

    let window: Window = WindowSettings::new(
        "Sierpinski-Triangle",
        [1000, 800])
        .opengl(opengl)
        .exit_on_esc(true)
        .build()
        .unwrap();

    let triangles = sierpinski(4, [[100.0, 200.0], [200.0, 200.0],
                                   [150.0, 100.0], [100.0, 200.0]]);

    let mut app = App{gl: GlGraphics::new(opengl), triangles: triangles};

    for e in window.events().max_fps(1) {
        if let Some(r) = e.render_args() {
            app.render(&r);
        }
    }
}
<commit_msg>Generating vertices for sub-triangles<commit_after>extern crate piston;
extern crate graphics;
extern crate glutin_window;
extern crate opengl_graphics;

use piston::window::WindowSettings;
use piston::event_loop::*;
use piston::input::*;
use glutin_window::GlutinWindow as Window;
use opengl_graphics::{ GlGraphics, OpenGL };

struct App {
    gl: GlGraphics,
    triangles: Vec<[[f64; 2]; 4]>
}

impl App {
    fn render(&mut self, args: &RenderArgs) {
        use graphics::*;

        const WHITE: [f32; 4] = [1.0, 1.0, 1.0, 1.0];
        const BLACK: [f32; 4] = [0.0, 0.0, 0.0, 1.0];

        let triangles = &self.triangles;

        self.gl.draw(args.viewport(), |c, gl| {
            clear(WHITE, gl);

            let transform = c.transform.trans(0.0, 0.0);

            for t in triangles {
              polygon(BLACK, t, transform, gl);
            }
        });
    }
}

fn sub_triangles(v: [[f64; 2]; 4]) -> Vec<[[f64; 2]; 4]> {
    let (a, b, c, d) = (v[0], v[1], v[2], v[3]); // Vector destructuring is not yet stable.

    vec![
        [[a[0] + ((b[0] - a[0]) / 2.0), a[1]],
         [b[0] - ((b[0] - c[0]) / 2.0), b[1] - ((b[1] - c[1]) / 2.0)],
         [a[0] + ((c[0] - a[0]) / 2.0), a[1] - ((a[1] - c[1]) / 2.0)],
         [a[0] + ((b[0] - a[0]) / 2.0), a[1]]],
        [[100.0, 200.0], [200.0, 200.0], [150.0, 100.0], [100.0, 200.0]],
        [[100.0, 200.0], [200.0, 200.0], [150.0, 100.0], [100.0, 200.0]],
        [[100.0, 200.0], [200.0, 200.0], [150.0, 100.0], [100.0, 200.0]]]
}

fn sierpinski(depth: i32, vertices: [[f64; 2]; 4]) -> Vec<[[f64; 2]; 4]> {
    let mut triangles = vec![];

    match depth {
        0 => triangles,
        d @ _ => {
            let sub_tris = sub_triangles(vertices);

            triangles.push(sub_tris[0]);

            for sub_tri in &sub_tris[1..] {
                for t in sierpinski(d - 1, *sub_tri) {
                    triangles.push(t);
                }
            }

            triangles
        },
    }
}

fn main() {
    let opengl = OpenGL::V3_2;

    let window: Window = WindowSettings::new(
        "Sierpinski-Triangle",
        [1000, 800])
        .opengl(opengl)
        .exit_on_esc(true)
        .build()
        .unwrap();

    let triangles = sierpinski(4, [[50.0, 750.0], [950.0, 750.0],
                                   [500.0, 50.0], [50.0, 750.0]]);

    let mut app = App{gl: GlGraphics::new(opengl), triangles: triangles};

    for e in window.events().max_fps(1) {
        if let Some(r) = e.render_args() {
            app.render(&r);
        }
    }
}
<|endoftext|>


<commit_before>use options::parser::MatchedFlags;
use options::{flags, Misfire};

use fs::dir_action::{DirAction, RecurseOptions};

impl DirAction {

    /// Determine which action to perform when trying to list a directory.
    pub fn deduce(matches: &MatchedFlags) -> Result<DirAction, Misfire> {
        let recurse = matches.has(&flags::RECURSE);
        let list    = matches.has(&flags::LIST_DIRS);
        let tree    = matches.has(&flags::TREE);

        // Early check for --level when it wouldn’t do anything
        if !recurse && !tree && matches.get(&flags::LEVEL).is_some() {
            return Err(Misfire::Useless2(&flags::LEVEL, &flags::RECURSE, &flags::TREE));
        }

        match (recurse, list, tree) {

            // You can't --list-dirs along with --recurse or --tree because
            // they already automatically list directories.
            (true,  true,  _    )  => Err(Misfire::Conflict(&flags::RECURSE, &flags::LIST_DIRS)),
            (_,     true,  true )  => Err(Misfire::Conflict(&flags::TREE,    &flags::LIST_DIRS)),

            (_   ,  _,     true )  => Ok(DirAction::Recurse(RecurseOptions::deduce(matches, true)?)),
            (true,  false, false)  => Ok(DirAction::Recurse(RecurseOptions::deduce(matches, false)?)),
            (false, true,  _    )  => Ok(DirAction::AsFile),
            (false, false, _    )  => Ok(DirAction::List),
        }
    }
}

impl RecurseOptions {

    /// Determine which files should be recursed into.
    pub fn deduce(matches: &MatchedFlags, tree: bool) -> Result<RecurseOptions, Misfire> {
        let max_depth = if let Some(level) = matches.get(&flags::LEVEL) {
            match level.to_string_lossy().parse() {
                Ok(l)   => Some(l),
                Err(e)  => return Err(Misfire::FailedParse(e)),
            }
        }
        else {
            None
        };

        Ok(RecurseOptions { tree, max_depth })
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use options::flags;

    macro_rules! test {
        ($name:ident: $type:ident <- $inputs:expr => $result:expr) => {
            #[test]
            fn $name() {
                use options::parser::Arg;
                use options::test::assert_parses;
                use options::test::Strictnesses::*;

                static TEST_ARGS: &[&Arg] = &[&flags::RECURSE, &flags::LIST_DIRS, &flags::TREE, &flags::LEVEL ];
                assert_parses($inputs.as_ref(), TEST_ARGS, Both, |mf| $type::deduce(mf), $result)
            }
        };
    }

    // Default behaviour
    test!(empty:           DirAction <- []               => Ok(DirAction::List));

    // Listing files as directories
    test!(dirs_short:      DirAction <- ["-d"]           => Ok(DirAction::AsFile));
    test!(dirs_long:       DirAction <- ["--list-dirs"]  => Ok(DirAction::AsFile));

    // Recursing
    test!(rec_short:       DirAction <- ["-R"]                           => Ok(DirAction::Recurse(RecurseOptions { tree: false, max_depth: None })));
    test!(rec_long:        DirAction <- ["--recurse"]                    => Ok(DirAction::Recurse(RecurseOptions { tree: false, max_depth: None })));
    test!(rec_lim_short:   DirAction <- ["-RL4"]                         => Ok(DirAction::Recurse(RecurseOptions { tree: false, max_depth: Some(4) })));
    test!(rec_lim_short_2: DirAction <- ["-RL=5"]                        => Ok(DirAction::Recurse(RecurseOptions { tree: false, max_depth: Some(5) })));
    test!(rec_lim_long:    DirAction <- ["--recurse", "--level", "666"]  => Ok(DirAction::Recurse(RecurseOptions { tree: false, max_depth: Some(666) })));
    test!(rec_lim_long_2:  DirAction <- ["--recurse", "--level=0118"]    => Ok(DirAction::Recurse(RecurseOptions { tree: false, max_depth: Some(118) })));
    test!(rec_tree:        DirAction <- ["--recurse", "--tree"]          => Ok(DirAction::Recurse(RecurseOptions { tree: true,  max_depth: None })));
    test!(rec_short_tree:  DirAction <- ["--tree", "--recurse"]          => Ok(DirAction::Recurse(RecurseOptions { tree: true,  max_depth: None })));

    // Errors
    test!(error:           DirAction <- ["--list-dirs", "--recurse"]  => Err(Misfire::Conflict(&flags::RECURSE, &flags::LIST_DIRS)));
    test!(error_2:         DirAction <- ["--list-dirs", "--tree"]     => Err(Misfire::Conflict(&flags::TREE,    &flags::LIST_DIRS)));
    test!(underwaterlevel: DirAction <- ["--level=4"]                 => Err(Misfire::Useless2(&flags::LEVEL, &flags::RECURSE, &flags::TREE)));
}
<commit_msg>Make these tests less long<commit_after>use options::parser::MatchedFlags;
use options::{flags, Misfire};

use fs::dir_action::{DirAction, RecurseOptions};

impl DirAction {

    /// Determine which action to perform when trying to list a directory.
    pub fn deduce(matches: &MatchedFlags) -> Result<DirAction, Misfire> {
        let recurse = matches.has(&flags::RECURSE);
        let list    = matches.has(&flags::LIST_DIRS);
        let tree    = matches.has(&flags::TREE);

        // Early check for --level when it wouldn’t do anything
        if !recurse && !tree && matches.get(&flags::LEVEL).is_some() {
            return Err(Misfire::Useless2(&flags::LEVEL, &flags::RECURSE, &flags::TREE));
        }

        match (recurse, list, tree) {

            // You can't --list-dirs along with --recurse or --tree because
            // they already automatically list directories.
            (true,  true,  _    )  => Err(Misfire::Conflict(&flags::RECURSE, &flags::LIST_DIRS)),
            (_,     true,  true )  => Err(Misfire::Conflict(&flags::TREE,    &flags::LIST_DIRS)),

            (_   ,  _,     true )  => Ok(DirAction::Recurse(RecurseOptions::deduce(matches, true)?)),
            (true,  false, false)  => Ok(DirAction::Recurse(RecurseOptions::deduce(matches, false)?)),
            (false, true,  _    )  => Ok(DirAction::AsFile),
            (false, false, _    )  => Ok(DirAction::List),
        }
    }
}

impl RecurseOptions {

    /// Determine which files should be recursed into.
    pub fn deduce(matches: &MatchedFlags, tree: bool) -> Result<RecurseOptions, Misfire> {
        let max_depth = if let Some(level) = matches.get(&flags::LEVEL) {
            match level.to_string_lossy().parse() {
                Ok(l)   => Some(l),
                Err(e)  => return Err(Misfire::FailedParse(e)),
            }
        }
        else {
            None
        };

        Ok(RecurseOptions { tree, max_depth })
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use options::flags;

    macro_rules! test {
        ($name:ident: $type:ident <- $inputs:expr => $result:expr) => {
            #[test]
            fn $name() {
                use options::parser::Arg;
                use options::test::assert_parses;
                use options::test::Strictnesses::*;

                static TEST_ARGS: &[&Arg] = &[&flags::RECURSE, &flags::LIST_DIRS, &flags::TREE, &flags::LEVEL ];
                assert_parses($inputs.as_ref(), TEST_ARGS, Both, |mf| $type::deduce(mf), $result)
            }
        };
    }

    // Default behaviour
    test!(empty:           DirAction <- []               => Ok(DirAction::List));

    // Listing files as directories
    test!(dirs_short:      DirAction <- ["-d"]           => Ok(DirAction::AsFile));
    test!(dirs_long:       DirAction <- ["--list-dirs"]  => Ok(DirAction::AsFile));

    // Recursing
    use self::DirAction::Recurse;
    test!(rec_short:       DirAction <- ["-R"]                           => Ok(Recurse(RecurseOptions { tree: false, max_depth: None })));
    test!(rec_long:        DirAction <- ["--recurse"]                    => Ok(Recurse(RecurseOptions { tree: false, max_depth: None })));
    test!(rec_lim_short:   DirAction <- ["-RL4"]                         => Ok(Recurse(RecurseOptions { tree: false, max_depth: Some(4) })));
    test!(rec_lim_short_2: DirAction <- ["-RL=5"]                        => Ok(Recurse(RecurseOptions { tree: false, max_depth: Some(5) })));
    test!(rec_lim_long:    DirAction <- ["--recurse", "--level", "666"]  => Ok(Recurse(RecurseOptions { tree: false, max_depth: Some(666) })));
    test!(rec_lim_long_2:  DirAction <- ["--recurse", "--level=0118"]    => Ok(Recurse(RecurseOptions { tree: false, max_depth: Some(118) })));
    test!(rec_tree:        DirAction <- ["--recurse", "--tree"]          => Ok(Recurse(RecurseOptions { tree: true,  max_depth: None })));
    test!(rec_short_tree:  DirAction <- ["--tree", "--recurse"]          => Ok(Recurse(RecurseOptions { tree: true,  max_depth: None })));

    // Errors
    test!(error:           DirAction <- ["--list-dirs", "--recurse"]  => Err(Misfire::Conflict(&flags::RECURSE, &flags::LIST_DIRS)));
    test!(error_2:         DirAction <- ["--list-dirs", "--tree"]     => Err(Misfire::Conflict(&flags::TREE,    &flags::LIST_DIRS)));
    test!(underwaterlevel: DirAction <- ["--level=4"]                 => Err(Misfire::Useless2(&flags::LEVEL, &flags::RECURSE, &flags::TREE)));
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::fmt;
use std::f32;
use num::Float;
use num::FromPrimitive;
use core::geometry::*;
use core::geometry::Transform;
use core::clock::*;
use core::util;
use backend::obj;
use backend::obj::*;
use backend::world::gen::Dna;
use backend::world::segment;
use backend::world::segment::Segment;

#[repr(packed)]
#[derive(Eq, Hash, PartialEq, Clone, Copy, Debug)]
pub struct Key {
	pub agent_id: obj::Id,
	pub segment_index: obj::SegmentIndex,
	pub bone_index: obj::BoneIndex,
}

impl Identified for Key {
	fn id(&self) -> obj::Id {
		self.agent_id
	}
}

impl Default for Key {
	fn default() -> Key {
		Key {
			agent_id: 0xdeadbeef,
			segment_index: 0,
			bone_index: 0,
		}
	}
}

impl Key {
	pub fn with_id(id: obj::Id) -> Key {
		Key {
			agent_id: id,
			..Default::default()
		}
	}

	pub fn with_segment(id: obj::Id, segment_index: obj::SegmentIndex) -> Key {
		Key {
			agent_id: id,
			segment_index,
			..Default::default()
		}
	}

	pub fn with_bone(agent_id: obj::Id, segment_index: obj::SegmentIndex, bone_index: obj::BoneIndex) -> Key {
		Key { agent_id, segment_index, bone_index }
	}

	pub fn no_bone(&self) -> Key {
		Key {
			bone_index: 0,
			..*self
		}
	}
}

pub trait TypedAgent {
	fn type_of(&self) -> AgentType;
}

impl TypedAgent for Id {
	fn type_of(&self) -> AgentType {
		AgentType::from_usize(*self & 0xff).unwrap_or(AgentType::Prop)
	}
}

enum_from_primitive! {
	#[derive(Debug, Eq, PartialEq, Hash, Copy, Clone)]
	pub enum AgentType {
		Minion ,
		Spore,
		Player,
		FriendlyBullet,
		Enemy,
		EnemyBullet,
		Resource,
		Prop,
	}
}

// TODO: sure there must be a better way?
impl fmt::Display for AgentType {
	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
		let text = match self {
			&AgentType::Minion => "Minion",
			&AgentType::Spore => "Spore",
			&AgentType::Player => "Player",
			&AgentType::FriendlyBullet => "FriendlyBullet",
			&AgentType::Enemy => "Enemy",
			&AgentType::EnemyBullet => "EnemyBullet",
			&AgentType::Resource => "Resource",
			&AgentType::Prop => "Prop",
		};
		f.write_str(text)
	}
}

// TODO: is there a better way to derive this?
const AGENT_TYPES: &'static [AgentType] = &[
	AgentType::Minion,
	AgentType::Spore,
	AgentType::Player,
	AgentType::FriendlyBullet,
	AgentType::Enemy,
	AgentType::EnemyBullet,
	AgentType::Resource,
	AgentType::Prop,
];

impl AgentType {
	pub fn all() -> &'static [AgentType] {
		AGENT_TYPES
	}
}

// for simplicity, inputs = intermediate = output
pub const N_WEIGHTS: usize = 4;

pub type InputVector<S> = [S; N_WEIGHTS];
pub type OutputVector<S> = [S; N_WEIGHTS];

pub type WeightVector<T> = [T; N_WEIGHTS];
pub type WeightMatrix<T> = [WeightVector<T>; N_WEIGHTS];

#[derive(Clone, Default, Debug)]
pub struct GBrain<T: Copy + Default> {
	pub hunger: T,
	pub haste: T,
	pub prudence: T,
	pub fear: T,
	pub rest: T,
	pub thrust: T,
	pub weights_in: WeightMatrix<T>,
	pub weights_hidden: WeightMatrix<T>,
	pub weights_out: WeightMatrix<T>,
}

pub trait TypedBrain {
	type Parameter: Float;
	type WeightVector;
	type WeightMatrix;
}

pub trait Personality<S>
	where
		S: Copy + Float,
{
	fn hunger(&self) -> S;
	fn haste(&self) -> S;
	fn prudence(&self) -> S;
	fn fear(&self) -> S;
	fn rest(&self) -> S;
	fn thrust(&self) -> S;
	fn response(&self, input: &InputVector<S>) -> OutputVector<S>;
}

pub trait Layer<S, T>
	where
		T: Copy,
		S: Float + From<T>,
{
	fn activation(x: S) -> S {
		x / (S::one() + x.abs())
	}

	fn layer(inputs: &[S], weights: &[WeightVector<T>]) -> OutputVector<S> {
		let mut outputs = [S::zero(); N_WEIGHTS];
		for i in 0..outputs.len() {
			for j in 0..inputs.len() {
				outputs[i] = outputs[i] + inputs[j] * weights[i][j].into();
			}
			outputs[i] = Self::activation(outputs[i])
		}
		outputs
	}
}

impl<S, T> Layer<S, T> for GBrain<T>
	where
		T: Copy + Default,
		S: Float + From<T>,
{}

impl<T, S> Personality<S> for GBrain<T>
	where
		T: Copy + Default,
		S: Copy + Float + From<T>,
{
	fn hunger(&self) -> S {
		self.hunger.into()
	}
	fn haste(&self) -> S {
		self.haste.into()
	}
	fn prudence(&self) -> S {
		self.prudence.into()
	}
	fn fear(&self) -> S {
		self.fear.into()
	}
	fn rest(&self) -> S {
		self.rest.into()
	}
	fn thrust(&self) -> S {
		self.thrust.into()
	}

	fn response(&self, input: &InputVector<S>) -> OutputVector<S> {
		let output_in = Self::layer(input, &self.weights_in);
		let output_hidden = Self::layer(&output_in, &self.weights_hidden);
		let output_out = Self::layer(&output_hidden, &self.weights_out);
		output_out
	}
}

impl<T> TypedBrain for GBrain<T>
	where
		T: Default + Copy + Float,
{
	type Parameter = T;
	type WeightVector = WeightVector<Self::Parameter>;
	type WeightMatrix = WeightMatrix<Self::Parameter>;
}

pub type Brain = GBrain<f32>;

bitflags! {
	pub struct Flags: u32 {
		const DEAD       = 0x1;
		const ACTIVE     = 0x2;
		const SELECTED   = 0x1000;
	}
}

#[derive(Clone, Debug)]
pub struct Limits {
	max_energy: f32,
}

#[derive(Clone, Debug)]
pub struct State {
	lifecycle: Hourglass<SimulationTimer>,
	flags: Flags,
	phase: f32,
	energy: f32,
	target: Option<Id>,
	target_position: Position,
	limits: Limits,
	foreign_dna: Option<Dna>,
	trajectory: util::History<Position>,
}

impl State {
	#[inline]
	pub fn lifecycle(&self) -> &Hourglass<SimulationTimer> {
		&self.lifecycle
	}

	pub fn renew(&mut self) {
		self.lifecycle.renew()
	}

	pub fn energy(&self) -> f32 {
		self.energy
	}

	pub fn energy_ratio(&self) -> f32 {
		self.energy / self.limits.max_energy
	}

	pub fn phase(&self) -> f32 {
		self.phase
	}

	pub fn consume(&mut self, q: f32) -> bool {
		if self.energy >= q {
			self.energy -= q;
			true
		} else {
			false
		}
	}

	pub fn consume_ratio(&mut self, ratio: f32) -> bool {
		let max = self.limits.max_energy;
		self.consume(max * ratio)
	}

	pub fn absorb(&mut self, q: f32) {
		self.energy = self.limits.max_energy.min(self.energy + q);
	}

	pub fn is_fertilised(&self) -> bool {
		self.foreign_dna.is_some()
	}

	pub fn fertilise(&mut self, dna: &Dna) {
		self.foreign_dna = Some(dna.clone());
	}

	pub fn foreign_dna(&self) -> &Option<Dna> {
		&self.foreign_dna
	}

	pub fn toggle_selection(&mut self) {
		self.flags ^= Flags::SELECTED;
	}
	#[allow(unused)]
	pub fn select(&mut self) {
		self.flags |= Flags::SELECTED;
	}

	pub fn deselect(&mut self) {
		self.flags -= Flags::SELECTED;
	}

	pub fn selected(&self) -> bool {
		self.flags.contains(Flags::SELECTED)
	}

	pub fn die(&mut self) {
		self.flags |= Flags::DEAD;
		self.flags -= Flags::ACTIVE;
	}

	#[inline]
	pub fn is_alive(&self) -> bool {
		!self.flags.contains(Flags::DEAD)
	}

	#[inline]
	pub fn is_active(&self) -> bool {
		self.flags.contains(Flags::ACTIVE)
	}

	pub fn target_position(&self) -> &Position {
		&self.target_position
	}

	pub fn target(&self) -> &Option<Id> {
		&self.target
	}

	pub fn retarget(&mut self, target: Option<Id>, position: Position) {
		self.target = target;
		self.target_position = position;
	}

	pub fn heartbeat(&mut self, d: f32) {
		self.phase = (self.phase + d) % (2.0 * f32::consts::PI)
	}

	pub fn track_position(&mut self, position: &Position) {
		self.trajectory.push(position.clone())
	}

	pub fn trajectory(&self) -> Box<[Position]> {
		self.trajectory
			.into_iter()
			.collect::<Vec<_>>()
			.into_boxed_slice()
	}
}

pub struct Agent {
	id: Id,
	brain: Brain,
	dna: Dna,
	gender: u8,
	pub state: State,
	pub segments: Box<[Segment]>,
}

impl Identified for Agent {
	fn id(&self) -> Id {
		self.id
	}
}

impl Transformable for Agent {
	fn transform(&self) -> &Transform {
		self.segments.first().unwrap().transform()
	}
	fn transform_to(&mut self, t: &Transform) {
		self.segments.first_mut().unwrap().transform_to(t);
	}
}

impl Agent {
	#[inline]
	pub fn dna(&self) -> &Dna {
		&self.dna
	}

	#[inline]
	pub fn gender(&self) -> u8 {
		self.gender
	}

	#[inline]
	pub fn segments(&self) -> &[Segment] {
		&self.segments
	}

	#[inline]
	pub fn segments_mut(&mut self) -> &mut [Segment] {
		&mut self.segments
	}

	pub fn segment(&self, index: SegmentIndex) -> Option<&Segment> {
		self.segments.get(index as usize)
	}

	pub fn last_segment(&self) -> &Segment {
		self.segments.last().unwrap()
	}

	pub fn segment_mut(&mut self, index: SegmentIndex) -> Option<&mut Segment> {
		self.segments.get_mut(index as usize)
	}

	pub fn brain(&self) -> &Brain {
		&self.brain
	}

	pub fn first_segment(&self, flags: segment::Flags) -> Option<Segment> {
		self.segments
			.iter()
			.find(|segment| segment.flags.contains(flags))
			.map(|sensor| sensor.clone())
	}

	pub fn new(id: Id, gender: u8, brain: &Brain, dna: &Dna, segments: Box<[Segment]>, timer: SharedTimer<SimulationTimer>) -> Self {
		const SCALE: f32 = 100.;
		let max_energy = SCALE *
			segments
				.iter()
				.filter(|s| s.flags.contains(segment::Flags::STORAGE))
				.fold(0., |a, s| a + s.mesh.shape.radius().powi(2));
		Agent {
			id,
			state: State {
				flags: Flags::ACTIVE,
				lifecycle: Hourglass::new(SimulationTimer::from(timer), Seconds::new(5.)),
				energy: max_energy * 0.5,
				phase: 0.,
				target: None,
				target_position: segments[0].transform.position,
				limits: Limits { max_energy },
				foreign_dna: None,
				trajectory: util::History::new(600),
			},
			brain: brain.clone(),
			gender,
			dna: dna.clone(),
			segments,
		}
	}
}

pub type AgentMap = HashMap<Id, Agent>;
<commit_msg>Unsharing stuff for multithreading, WIP<commit_after>use std::collections::HashMap;
use std::fmt;
use std::f32;
use num::Float;
use num::FromPrimitive;
use core::geometry::*;
use core::geometry::Transform;
use core::clock::*;
use core::util;
use backend::obj;
use backend::obj::*;
use backend::world::gen::Dna;
use backend::world::segment;
use backend::world::segment::Segment;

#[repr(packed)]
#[derive(Eq, Hash, PartialEq, Clone, Copy, Debug)]
pub struct Key {
	pub agent_id: obj::Id,
	pub segment_index: obj::SegmentIndex,
	pub bone_index: obj::BoneIndex,
}

impl Identified for Key {
	fn id(&self) -> obj::Id {
		self.agent_id
	}
}

impl Default for Key {
	fn default() -> Key {
		Key {
			agent_id: 0xdeadbeef,
			segment_index: 0,
			bone_index: 0,
		}
	}
}

impl Key {
	pub fn with_id(id: obj::Id) -> Key {
		Key {
			agent_id: id,
			..Default::default()
		}
	}

	pub fn with_segment(id: obj::Id, segment_index: obj::SegmentIndex) -> Key {
		Key {
			agent_id: id,
			segment_index,
			..Default::default()
		}
	}

	pub fn with_bone(agent_id: obj::Id, segment_index: obj::SegmentIndex, bone_index: obj::BoneIndex) -> Key {
		Key { agent_id, segment_index, bone_index }
	}

	pub fn no_bone(&self) -> Key {
		Key {
			bone_index: 0,
			..*self
		}
	}
}

pub trait TypedAgent {
	fn type_of(&self) -> AgentType;
}

impl TypedAgent for Id {
	fn type_of(&self) -> AgentType {
		AgentType::from_usize(*self & 0xff).unwrap_or(AgentType::Prop)
	}
}

enum_from_primitive! {
	#[derive(Debug, Eq, PartialEq, Hash, Copy, Clone)]
	pub enum AgentType {
		Minion ,
		Spore,
		Player,
		FriendlyBullet,
		Enemy,
		EnemyBullet,
		Resource,
		Prop,
	}
}

// TODO: sure there must be a better way?
impl fmt::Display for AgentType {
	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
		let text = match self {
			&AgentType::Minion => "Minion",
			&AgentType::Spore => "Spore",
			&AgentType::Player => "Player",
			&AgentType::FriendlyBullet => "FriendlyBullet",
			&AgentType::Enemy => "Enemy",
			&AgentType::EnemyBullet => "EnemyBullet",
			&AgentType::Resource => "Resource",
			&AgentType::Prop => "Prop",
		};
		f.write_str(text)
	}
}

// TODO: is there a better way to derive this?
const AGENT_TYPES: &'static [AgentType] = &[
	AgentType::Minion,
	AgentType::Spore,
	AgentType::Player,
	AgentType::FriendlyBullet,
	AgentType::Enemy,
	AgentType::EnemyBullet,
	AgentType::Resource,
	AgentType::Prop,
];

impl AgentType {
	pub fn all() -> &'static [AgentType] {
		AGENT_TYPES
	}
}

// for simplicity, inputs = intermediate = output
pub const N_WEIGHTS: usize = 4;

pub type InputVector<S> = [S; N_WEIGHTS];
pub type OutputVector<S> = [S; N_WEIGHTS];

pub type WeightVector<T> = [T; N_WEIGHTS];
pub type WeightMatrix<T> = [WeightVector<T>; N_WEIGHTS];

#[derive(Clone, Default, Debug)]
pub struct GBrain<T: Copy + Default> {
	pub hunger: T,
	pub haste: T,
	pub prudence: T,
	pub fear: T,
	pub rest: T,
	pub thrust: T,
	pub weights_in: WeightMatrix<T>,
	pub weights_hidden: WeightMatrix<T>,
	pub weights_out: WeightMatrix<T>,
}

pub trait TypedBrain {
	type Parameter: Float;
	type WeightVector;
	type WeightMatrix;
}

pub trait Personality<S>
	where
		S: Copy + Float,
{
	fn hunger(&self) -> S;
	fn haste(&self) -> S;
	fn prudence(&self) -> S;
	fn fear(&self) -> S;
	fn rest(&self) -> S;
	fn thrust(&self) -> S;
	fn response(&self, input: &InputVector<S>) -> OutputVector<S>;
}

pub trait Layer<S, T>
	where
		T: Copy,
		S: Float + From<T>,
{
	fn activation(x: S) -> S {
		x / (S::one() + x.abs())
	}

	fn layer(inputs: &[S], weights: &[WeightVector<T>]) -> OutputVector<S> {
		let mut outputs = [S::zero(); N_WEIGHTS];
		for i in 0..outputs.len() {
			for j in 0..inputs.len() {
				outputs[i] = outputs[i] + inputs[j] * weights[i][j].into();
			}
			outputs[i] = Self::activation(outputs[i])
		}
		outputs
	}
}

impl<S, T> Layer<S, T> for GBrain<T>
	where
		T: Copy + Default,
		S: Float + From<T>,
{}

impl<T, S> Personality<S> for GBrain<T>
	where
		T: Copy + Default,
		S: Copy + Float + From<T>,
{
	fn hunger(&self) -> S {
		self.hunger.into()
	}
	fn haste(&self) -> S {
		self.haste.into()
	}
	fn prudence(&self) -> S {
		self.prudence.into()
	}
	fn fear(&self) -> S {
		self.fear.into()
	}
	fn rest(&self) -> S {
		self.rest.into()
	}
	fn thrust(&self) -> S {
		self.thrust.into()
	}

	fn response(&self, input: &InputVector<S>) -> OutputVector<S> {
		let output_in = Self::layer(input, &self.weights_in);
		let output_hidden = Self::layer(&output_in, &self.weights_hidden);
		let output_out = Self::layer(&output_hidden, &self.weights_out);
		output_out
	}
}

impl<T> TypedBrain for GBrain<T>
	where
		T: Default + Copy + Float,
{
	type Parameter = T;
	type WeightVector = WeightVector<Self::Parameter>;
	type WeightMatrix = WeightMatrix<Self::Parameter>;
}

pub type Brain = GBrain<f32>;

bitflags! {
	pub struct Flags: u32 {
		const DEAD       = 0x1;
		const ACTIVE     = 0x2;
		const SELECTED   = 0x1000;
	}
}

#[derive(Clone, Debug)]
pub struct Limits {
	max_energy: f32,
}

#[derive(Clone, Debug)]
pub struct State {
	lifecycle: Hourglass<SharedTimer<SimulationTimer>>,
	flags: Flags,
	phase: f32,
	energy: f32,
	target: Option<Id>,
	target_position: Position,
	limits: Limits,
	foreign_dna: Option<Dna>,
	trajectory: util::History<Position>,
}

impl State {
	#[inline]
	pub fn lifecycle(&self) -> &Hourglass<SharedTimer<SimulationTimer>> {
		&self.lifecycle
	}

	pub fn renew(&mut self) {
		self.lifecycle.renew()
	}

	pub fn energy(&self) -> f32 {
		self.energy
	}

	pub fn energy_ratio(&self) -> f32 {
		self.energy / self.limits.max_energy
	}

	pub fn phase(&self) -> f32 {
		self.phase
	}

	pub fn consume(&mut self, q: f32) -> bool {
		if self.energy >= q {
			self.energy -= q;
			true
		} else {
			false
		}
	}

	pub fn consume_ratio(&mut self, ratio: f32) -> bool {
		let max = self.limits.max_energy;
		self.consume(max * ratio)
	}

	pub fn absorb(&mut self, q: f32) {
		self.energy = self.limits.max_energy.min(self.energy + q);
	}

	pub fn is_fertilised(&self) -> bool {
		self.foreign_dna.is_some()
	}

	pub fn fertilise(&mut self, dna: &Dna) {
		self.foreign_dna = Some(dna.clone());
	}

	pub fn foreign_dna(&self) -> &Option<Dna> {
		&self.foreign_dna
	}

	pub fn toggle_selection(&mut self) {
		self.flags ^= Flags::SELECTED;
	}
	#[allow(unused)]
	pub fn select(&mut self) {
		self.flags |= Flags::SELECTED;
	}

	pub fn deselect(&mut self) {
		self.flags -= Flags::SELECTED;
	}

	pub fn selected(&self) -> bool {
		self.flags.contains(Flags::SELECTED)
	}

	pub fn die(&mut self) {
		self.flags |= Flags::DEAD;
		self.flags -= Flags::ACTIVE;
	}

	#[inline]
	pub fn is_alive(&self) -> bool {
		!self.flags.contains(Flags::DEAD)
	}

	#[inline]
	pub fn is_active(&self) -> bool {
		self.flags.contains(Flags::ACTIVE)
	}

	pub fn target_position(&self) -> &Position {
		&self.target_position
	}

	pub fn target(&self) -> &Option<Id> {
		&self.target
	}

	pub fn retarget(&mut self, target: Option<Id>, position: Position) {
		self.target = target;
		self.target_position = position;
	}

	pub fn heartbeat(&mut self, d: f32) {
		self.phase = (self.phase + d) % (2.0 * f32::consts::PI)
	}

	pub fn track_position(&mut self, position: &Position) {
		self.trajectory.push(position.clone())
	}

	pub fn trajectory(&self) -> Box<[Position]> {
		self.trajectory
			.into_iter()
			.collect::<Vec<_>>()
			.into_boxed_slice()
	}
}

pub struct Agent {
	id: Id,
	brain: Brain,
	dna: Dna,
	gender: u8,
	pub state: State,
	pub segments: Box<[Segment]>,
}

impl Identified for Agent {
	fn id(&self) -> Id {
		self.id
	}
}

impl Transformable for Agent {
	fn transform(&self) -> &Transform {
		self.segments.first().unwrap().transform()
	}
	fn transform_to(&mut self, t: &Transform) {
		self.segments.first_mut().unwrap().transform_to(t);
	}
}

impl Agent {
	#[inline]
	pub fn dna(&self) -> &Dna {
		&self.dna
	}

	#[inline]
	pub fn gender(&self) -> u8 {
		self.gender
	}

	#[inline]
	pub fn segments(&self) -> &[Segment] {
		&self.segments
	}

	#[inline]
	pub fn segments_mut(&mut self) -> &mut [Segment] {
		&mut self.segments
	}

	pub fn segment(&self, index: SegmentIndex) -> Option<&Segment> {
		self.segments.get(index as usize)
	}

	pub fn last_segment(&self) -> &Segment {
		self.segments.last().unwrap()
	}

	pub fn segment_mut(&mut self, index: SegmentIndex) -> Option<&mut Segment> {
		self.segments.get_mut(index as usize)
	}

	pub fn brain(&self) -> &Brain {
		&self.brain
	}

	pub fn first_segment(&self, flags: segment::Flags) -> Option<Segment> {
		self.segments
			.iter()
			.find(|segment| segment.flags.contains(flags))
			.map(|sensor| sensor.clone())
	}

	pub fn new(id: Id, gender: u8, brain: &Brain, dna: &Dna, segments: Box<[Segment]>, timer: SharedTimer<SimulationTimer>) -> Self {
		const SCALE: f32 = 100.;
		let max_energy = SCALE *
			segments
				.iter()
				.filter(|s| s.flags.contains(segment::Flags::STORAGE))
				.fold(0., |a, s| a + s.mesh.shape.radius().powi(2));
		Agent {
			id,
			state: State {
				flags: Flags::ACTIVE,
				lifecycle: Hourglass::new(timer, Seconds::new(5.)),
				energy: max_energy * 0.5,
				phase: 0.,
				target: None,
				target_position: segments[0].transform.position,
				limits: Limits { max_energy },
				foreign_dna: None,
				trajectory: util::History::new(600),
			},
			brain: brain.clone(),
			gender,
			dna: dna.clone(),
			segments,
		}
	}
}

pub type AgentMap = HashMap<Id, Agent>;
<|endoftext|>