<commit_before>use std::{env,
          ffi::OsString,
          fs as stdfs,
          path::PathBuf};

use crate::{common::ui::UI,
            hcore::{crypto::CACHE_KEY_PATH_ENV_VAR,
                    env as henv,
                    fs}};

use crate::{config,
            error::Result,
            BLDR_URL_ENVVAR,
            CTL_SECRET_ENVVAR,
            ORIGIN_ENVVAR};

use habitat_core::AUTH_TOKEN_ENVVAR;

pub const ARTIFACT_PATH_ENVVAR: &str = "ARTIFACT_PATH";

const STUDIO_CMD: &str = "hab-studio";
const STUDIO_CMD_ENVVAR: &str = "HAB_STUDIO_BINARY";
const STUDIO_PACKAGE_IDENT: &str = "core/hab-studio";

enum Sensitivity {
    PrintValue,
    NoPrintValue,
}

fn set_env_var_from_config(env_var: &str, config_val: Option<String>, sensitive: Sensitivity) {
    if henv::var(env_var).is_err() {
        if let Some(val) = config_val {
            match sensitive {
                Sensitivity::NoPrintValue => {
                    debug!("Setting {}=REDACTED (sensitive) via config file", env_var)
                }
                Sensitivity::PrintValue => debug!("Setting {}={} via config file", env_var, val),
            }
            env::set_var(env_var, val);
        }
    }
}

pub fn start(ui: &mut UI, args: &[OsString]) -> Result<()> {
    let config = config::load()?;

    set_env_var_from_config(AUTH_TOKEN_ENVVAR,
                            config.auth_token,
                            Sensitivity::NoPrintValue);
    set_env_var_from_config(BLDR_URL_ENVVAR, config.bldr_url, Sensitivity::PrintValue);
    set_env_var_from_config(CTL_SECRET_ENVVAR,
                            config.ctl_secret,
                            Sensitivity::NoPrintValue);
    set_env_var_from_config(ORIGIN_ENVVAR, config.origin, Sensitivity::PrintValue);

    if henv::var(CACHE_KEY_PATH_ENV_VAR).is_err() {
        let path = fs::cache_key_path(None::<&str>);
        debug!("Setting {}={}", CACHE_KEY_PATH_ENV_VAR, path.display());
        env::set_var(CACHE_KEY_PATH_ENV_VAR, &path);
    };

    let artifact_path = match henv::var(ARTIFACT_PATH_ENVVAR) {
        Ok(p) => PathBuf::from(p),
        Err(_) => {
            let path = fs::cache_artifact_path(None::<&str>);
            debug!("Setting {}={}", ARTIFACT_PATH_ENVVAR, path.display());
            env::set_var(ARTIFACT_PATH_ENVVAR, &path);
            path
        }
    };
    if !artifact_path.is_dir() {
        debug!("Creating artifact_path at: {}", artifact_path.display());
        stdfs::create_dir_all(&artifact_path)?;
    }

    inner::start(ui, args)
}

#[cfg(target_os = "linux")]
mod inner {
    use crate::{command::studio::docker,
                common::ui::{UIWriter,
                             UI},
                error::{Error,
                        Result},
                exec,
                hcore::{crypto::init,
                        env as henv,
                        fs::{am_i_root,
                             find_command},
                        os::process,
                        package::{PackageIdent,
                                  PackageInstall},
                        users::linux as group},
                VERSION};
    use std::{env,
              ffi::OsString,
              path::PathBuf,
              str::FromStr};

    const SUDO_CMD: &str = "sudo";

    pub fn start(ui: &mut UI, args: &[OsString]) -> Result<()> {
        rerun_with_sudo_if_needed(ui, &args)?;
        if is_docker_studio(&args) {
            docker::start_docker_studio(ui, args)
        } else {
            let command = match henv::var(super::STUDIO_CMD_ENVVAR) {
                Ok(command) => PathBuf::from(command),
                Err(_) => {
                    init();
                    let version: Vec<&str> = VERSION.split('/').collect();
                    let ident = PackageIdent::from_str(&format!("{}/{}",
                                                                super::STUDIO_PACKAGE_IDENT,
                                                                version[0]))?;
                    // This is a duplicate of the code in `hab pkg exec` and
                    // should be refactored as part of or after:
                    // https://github.com/habitat-sh/habitat/issues/6633
                    // https://github.com/habitat-sh/habitat/issues/6634
                    let pkg_install = PackageInstall::load(&ident, None)?;
                    let cmd_env = pkg_install.environment_for_command()?;
                    for (key, value) in cmd_env.into_iter() {
                        debug!("Setting: {}='{}'", key, value);
                        env::set_var(key, value);
                    }

                    let mut display_args = super::STUDIO_CMD.to_string();
                    for arg in args {
                        display_args.push(' ');
                        display_args.push_str(arg.to_string_lossy().as_ref());
                    }
                    debug!("Running: {}", display_args);

                    exec::command_from_min_pkg(ui, super::STUDIO_CMD, &ident)?
                }
            };

            if let Some(cmd) = find_command(command.to_string_lossy().as_ref()) {
                process::become_command(cmd, args)?;
                Ok(())
            } else {
                Err(Error::ExecCommandNotFound(command))
            }
        }
    }

    fn is_docker_studio(args: &[OsString]) -> bool {
        if cfg!(not(target_os = "linux")) {
            return false;
        }

        for arg in args.iter() {
            let str_arg = arg.to_string_lossy();
            if str_arg == "-D" {
                return true;
            }
        }

        false
    }

    fn has_docker_group() -> bool {
        let current_user = group::get_current_username().unwrap();
        let docker_members = group::get_members_by_groupname("docker");
        docker_members.map_or(false, |d| d.contains(&current_user))
    }

    fn rerun_with_sudo_if_needed(ui: &mut UI, args: &[OsString]) -> Result<()> {
        // If I have root permissions or if I am executing a docker studio
        // and have the appropriate group - early return, we are done.
        if am_i_root() || (is_docker_studio(args) && has_docker_group()) {
            return Ok(());
        }

        // Otherwise we will try to re-run this program using `sudo`
        match find_command(SUDO_CMD) {
            Some(sudo_prog) => {
                let mut args: Vec<OsString> = vec!["-p".into(),
                                                   "[sudo hab-studio] password for %u: ".into(),
                                                   "-E".into(),];
                args.append(&mut env::args_os().collect());
                process::become_command(sudo_prog, &args)?;
                Ok(())
            }
            None => {
                ui.warn(format!("Could not find the `{}' command, is it in your PATH?",
                                SUDO_CMD))?;
                ui.warn("Running Habitat Studio requires root or administrator privileges. \
                         Please retry this command as a super user or use a privilege-granting \
                         facility such as sudo.")?;
                ui.br()?;
                Err(Error::RootRequired)
            }
        }
    }
}

#[cfg(not(target_os = "linux"))]
mod inner {
    use crate::{command::studio::docker,
                common::ui::UI,
                error::{Error,
                        Result},
                exec,
                hcore::{crypto::init,
                        env as henv,
                        fs::find_command,
                        os::process,
                        package::PackageIdent},
                VERSION};
    use std::{ffi::OsString,
              path::PathBuf,
              str::FromStr};

    pub fn start(_ui: &mut UI, args: &[OsString]) -> Result<()> {
        if is_windows_studio(&args) {
            start_windows_studio(_ui, args)
        } else {
            docker::start_docker_studio(_ui, args)
        }
    }

    pub fn start_windows_studio(ui: &mut UI, args: &[OsString]) -> Result<()> {
        let command = match henv::var(super::STUDIO_CMD_ENVVAR) {
            Ok(command) => PathBuf::from(command),
            Err(_) => {
                init();
                let version: Vec<&str> = VERSION.split('/').collect();
                let ident = PackageIdent::from_str(&format!("{}/{}",
                                                            super::STUDIO_PACKAGE_IDENT,
                                                            version[0]))?;
                exec::command_from_min_pkg(ui, super::STUDIO_CMD, &ident)?
            }
        };

        if let Some(cmd) = find_command(command.to_string_lossy().as_ref()) {
            process::become_command(cmd, args)?;
        } else {
            return Err(Error::ExecCommandNotFound(command));
        }
        Ok(())
    }

    fn is_windows_studio(args: &[OsString]) -> bool {
        if cfg!(not(target_os = "windows")) {
            return false;
        }

        for arg in args.iter() {
            let str_arg = arg.to_string_lossy();
            if str_arg == "-D" {
                return false;
            }
        }

        // -w/--windows is deprecated and should be removed in a post 0.64.0 release
        for arg in args.iter() {
            let str_arg = arg.to_string_lossy().to_lowercase();
            if str_arg == "--windows" || str_arg == "-w" {
                return true;
            }
        }

        true
    }
}
<commit_msg>Add traits to Enum<commit_after>use std::{env,
          ffi::OsString,
          fs as stdfs,
          path::PathBuf};

use crate::{common::ui::UI,
            hcore::{crypto::CACHE_KEY_PATH_ENV_VAR,
                    env as henv,
                    fs}};

use crate::{config,
            error::Result,
            BLDR_URL_ENVVAR,
            CTL_SECRET_ENVVAR,
            ORIGIN_ENVVAR};

use habitat_core::AUTH_TOKEN_ENVVAR;

pub const ARTIFACT_PATH_ENVVAR: &str = "ARTIFACT_PATH";

const STUDIO_CMD: &str = "hab-studio";
const STUDIO_CMD_ENVVAR: &str = "HAB_STUDIO_BINARY";
const STUDIO_PACKAGE_IDENT: &str = "core/hab-studio";

#[derive(Clone, Copy)]
enum Sensitivity {
    PrintValue,
    NoPrintValue,
}

fn set_env_var_from_config(env_var: &str, config_val: Option<String>, sensitive: Sensitivity) {
    if henv::var(env_var).is_err() {
        if let Some(val) = config_val {
            match sensitive {
                Sensitivity::NoPrintValue => {
                    debug!("Setting {}=REDACTED (sensitive) via config file", env_var)
                }
                Sensitivity::PrintValue => debug!("Setting {}={} via config file", env_var, val),
            }
            env::set_var(env_var, val);
        }
    }
}

pub fn start(ui: &mut UI, args: &[OsString]) -> Result<()> {
    let config = config::load()?;

    set_env_var_from_config(AUTH_TOKEN_ENVVAR,
                            config.auth_token,
                            Sensitivity::NoPrintValue);
    set_env_var_from_config(BLDR_URL_ENVVAR, config.bldr_url, Sensitivity::PrintValue);
    set_env_var_from_config(CTL_SECRET_ENVVAR,
                            config.ctl_secret,
                            Sensitivity::NoPrintValue);
    set_env_var_from_config(ORIGIN_ENVVAR, config.origin, Sensitivity::PrintValue);

    if henv::var(CACHE_KEY_PATH_ENV_VAR).is_err() {
        let path = fs::cache_key_path(None::<&str>);
        debug!("Setting {}={}", CACHE_KEY_PATH_ENV_VAR, path.display());
        env::set_var(CACHE_KEY_PATH_ENV_VAR, &path);
    };

    let artifact_path = match henv::var(ARTIFACT_PATH_ENVVAR) {
        Ok(p) => PathBuf::from(p),
        Err(_) => {
            let path = fs::cache_artifact_path(None::<&str>);
            debug!("Setting {}={}", ARTIFACT_PATH_ENVVAR, path.display());
            env::set_var(ARTIFACT_PATH_ENVVAR, &path);
            path
        }
    };
    if !artifact_path.is_dir() {
        debug!("Creating artifact_path at: {}", artifact_path.display());
        stdfs::create_dir_all(&artifact_path)?;
    }

    inner::start(ui, args)
}

#[cfg(target_os = "linux")]
mod inner {
    use crate::{command::studio::docker,
                common::ui::{UIWriter,
                             UI},
                error::{Error,
                        Result},
                exec,
                hcore::{crypto::init,
                        env as henv,
                        fs::{am_i_root,
                             find_command},
                        os::process,
                        package::{PackageIdent,
                                  PackageInstall},
                        users::linux as group},
                VERSION};
    use std::{env,
              ffi::OsString,
              path::PathBuf,
              str::FromStr};

    const SUDO_CMD: &str = "sudo";

    pub fn start(ui: &mut UI, args: &[OsString]) -> Result<()> {
        rerun_with_sudo_if_needed(ui, &args)?;
        if is_docker_studio(&args) {
            docker::start_docker_studio(ui, args)
        } else {
            let command = match henv::var(super::STUDIO_CMD_ENVVAR) {
                Ok(command) => PathBuf::from(command),
                Err(_) => {
                    init();
                    let version: Vec<&str> = VERSION.split('/').collect();
                    let ident = PackageIdent::from_str(&format!("{}/{}",
                                                                super::STUDIO_PACKAGE_IDENT,
                                                                version[0]))?;
                    // This is a duplicate of the code in `hab pkg exec` and
                    // should be refactored as part of or after:
                    // https://github.com/habitat-sh/habitat/issues/6633
                    // https://github.com/habitat-sh/habitat/issues/6634
                    let pkg_install = PackageInstall::load(&ident, None)?;
                    let cmd_env = pkg_install.environment_for_command()?;
                    for (key, value) in cmd_env.into_iter() {
                        debug!("Setting: {}='{}'", key, value);
                        env::set_var(key, value);
                    }

                    let mut display_args = super::STUDIO_CMD.to_string();
                    for arg in args {
                        display_args.push(' ');
                        display_args.push_str(arg.to_string_lossy().as_ref());
                    }
                    debug!("Running: {}", display_args);

                    exec::command_from_min_pkg(ui, super::STUDIO_CMD, &ident)?
                }
            };

            if let Some(cmd) = find_command(command.to_string_lossy().as_ref()) {
                process::become_command(cmd, args)?;
                Ok(())
            } else {
                Err(Error::ExecCommandNotFound(command))
            }
        }
    }

    fn is_docker_studio(args: &[OsString]) -> bool {
        if cfg!(not(target_os = "linux")) {
            return false;
        }

        for arg in args.iter() {
            let str_arg = arg.to_string_lossy();
            if str_arg == "-D" {
                return true;
            }
        }

        false
    }

    fn has_docker_group() -> bool {
        let current_user = group::get_current_username().unwrap();
        let docker_members = group::get_members_by_groupname("docker");
        docker_members.map_or(false, |d| d.contains(&current_user))
    }

    fn rerun_with_sudo_if_needed(ui: &mut UI, args: &[OsString]) -> Result<()> {
        // If I have root permissions or if I am executing a docker studio
        // and have the appropriate group - early return, we are done.
        if am_i_root() || (is_docker_studio(args) && has_docker_group()) {
            return Ok(());
        }

        // Otherwise we will try to re-run this program using `sudo`
        match find_command(SUDO_CMD) {
            Some(sudo_prog) => {
                let mut args: Vec<OsString> = vec!["-p".into(),
                                                   "[sudo hab-studio] password for %u: ".into(),
                                                   "-E".into(),];
                args.append(&mut env::args_os().collect());
                process::become_command(sudo_prog, &args)?;
                Ok(())
            }
            None => {
                ui.warn(format!("Could not find the `{}' command, is it in your PATH?",
                                SUDO_CMD))?;
                ui.warn("Running Habitat Studio requires root or administrator privileges. \
                         Please retry this command as a super user or use a privilege-granting \
                         facility such as sudo.")?;
                ui.br()?;
                Err(Error::RootRequired)
            }
        }
    }
}

#[cfg(not(target_os = "linux"))]
mod inner {
    use crate::{command::studio::docker,
                common::ui::UI,
                error::{Error,
                        Result},
                exec,
                hcore::{crypto::init,
                        env as henv,
                        fs::find_command,
                        os::process,
                        package::PackageIdent},
                VERSION};
    use std::{ffi::OsString,
              path::PathBuf,
              str::FromStr};

    pub fn start(_ui: &mut UI, args: &[OsString]) -> Result<()> {
        if is_windows_studio(&args) {
            start_windows_studio(_ui, args)
        } else {
            docker::start_docker_studio(_ui, args)
        }
    }

    pub fn start_windows_studio(ui: &mut UI, args: &[OsString]) -> Result<()> {
        let command = match henv::var(super::STUDIO_CMD_ENVVAR) {
            Ok(command) => PathBuf::from(command),
            Err(_) => {
                init();
                let version: Vec<&str> = VERSION.split('/').collect();
                let ident = PackageIdent::from_str(&format!("{}/{}",
                                                            super::STUDIO_PACKAGE_IDENT,
                                                            version[0]))?;
                exec::command_from_min_pkg(ui, super::STUDIO_CMD, &ident)?
            }
        };

        if let Some(cmd) = find_command(command.to_string_lossy().as_ref()) {
            process::become_command(cmd, args)?;
        } else {
            return Err(Error::ExecCommandNotFound(command));
        }
        Ok(())
    }

    fn is_windows_studio(args: &[OsString]) -> bool {
        if cfg!(not(target_os = "windows")) {
            return false;
        }

        for arg in args.iter() {
            let str_arg = arg.to_string_lossy();
            if str_arg == "-D" {
                return false;
            }
        }

        // -w/--windows is deprecated and should be removed in a post 0.64.0 release
        for arg in args.iter() {
            let str_arg = arg.to_string_lossy().to_lowercase();
            if str_arg == "--windows" || str_arg == "-w" {
                return true;
            }
        }

        true
    }
}
<|endoftext|>


<commit_before>use rustc::dep_graph::{WorkProduct, WorkProductFileKind, WorkProductId};
use rustc::middle::cstore::EncodedMetadata;
use rustc::mir::mono::CodegenUnit;
use rustc::session::config::{DebugInfo, OutputType};
use rustc_session::cgu_reuse_tracker::CguReuse;
use rustc_codegen_ssa::back::linker::LinkerInfo;
use rustc_codegen_ssa::CrateInfo;

use crate::prelude::*;

use crate::backend::{Emit, WriteDebugInfo};

pub(super) fn run_aot(
    tcx: TyCtxt<'_>,
    metadata: EncodedMetadata,
    need_metadata_module: bool,
) -> Box<(CodegenResults, FxHashMap<WorkProductId, WorkProduct>)> {
    let mut work_products = FxHashMap::default();

    fn new_module(tcx: TyCtxt<'_>, name: String) -> Module<crate::backend::Backend> {
        let module = crate::backend::make_module(tcx.sess, name);
        assert_eq!(pointer_ty(tcx), module.target_config().pointer_type());
        module
    };

    struct ModuleCodegenResult(CompiledModule, Option<(WorkProductId, WorkProduct)>);

    use rustc_data_structures::stable_hasher::{HashStable, StableHasher};

    impl<HCX> HashStable<HCX> for ModuleCodegenResult {
        fn hash_stable(&self, _: &mut HCX, _: &mut StableHasher) {
            // do nothing
        }
    }

    fn emit_module<B: Backend>(
        tcx: TyCtxt<'_>,
        name: String,
        kind: ModuleKind,
        mut module: Module<B>,
        debug: Option<DebugContext>,
    ) -> ModuleCodegenResult
        where B::Product: Emit + WriteDebugInfo,
    {
            module.finalize_definitions();
            let mut product = module.finish();

            if let Some(mut debug) = debug {
                debug.emit(&mut product);
            }

            let tmp_file = tcx
                .output_filenames(LOCAL_CRATE)
                .temp_path(OutputType::Object, Some(&name));
            let obj = product.emit();
            std::fs::write(&tmp_file, obj).unwrap();

            let work_product = if std::env::var("CG_CLIF_INCR_CACHE_DISABLED").is_ok() {
                None
            } else {
                rustc_incremental::copy_cgu_workproducts_to_incr_comp_cache_dir(
                    tcx.sess,
                    &name,
                    &[(WorkProductFileKind::Object, tmp_file.clone())],
                )
            };

            ModuleCodegenResult(
                CompiledModule {
                    name,
                    kind,
                    object: Some(tmp_file),
                    bytecode: None,
                    bytecode_compressed: None,
                },
                work_product,
            )
        };

    let (_, cgus) = tcx.collect_and_partition_mono_items(LOCAL_CRATE);

    if tcx.dep_graph.is_fully_enabled() {
        for cgu in &*cgus {
            tcx.codegen_unit(cgu.name());
        }
    }

    let modules = super::time(tcx, "codegen mono items", || {
        cgus.iter().map(|cgu| {
            let cgu_reuse = determine_cgu_reuse(tcx, cgu);
            tcx.sess.cgu_reuse_tracker.set_actual_reuse(&cgu.name().as_str(), cgu_reuse);

            match cgu_reuse {
                _ if std::env::var("CG_CLIF_INCR_CACHE_DISABLED").is_ok() => {}
                CguReuse::No => {}
                CguReuse::PreLto => {
                    let incr_comp_session_dir = tcx.sess.incr_comp_session_dir();
                    let mut object = None;
                    let work_product = cgu.work_product(tcx);
                    for (kind, saved_file) in &work_product.saved_files {
                        let obj_out = match kind {
                            WorkProductFileKind::Object => {
                                let path = tcx.output_filenames(LOCAL_CRATE).temp_path(OutputType::Object, Some(&cgu.name().as_str()));
                                object = Some(path.clone());
                                path
                            }
                            WorkProductFileKind::Bytecode | WorkProductFileKind::BytecodeCompressed => {
                                panic!("cg_clif doesn't use bytecode");
                            }
                        };
                        let source_file = rustc_incremental::in_incr_comp_dir(&incr_comp_session_dir, &saved_file);
                        if let Err(err) = rustc_fs_util::link_or_copy(&source_file, &obj_out) {
                            tcx.sess.err(&format!(
                                "unable to copy {} to {}: {}",
                                source_file.display(),
                                obj_out.display(),
                                err
                            ));
                        }
                    }

                    work_products.insert(cgu.work_product_id(), work_product);

                    return CompiledModule {
                        name: cgu.name().to_string(),
                        kind: ModuleKind::Regular,
                        object,
                        bytecode: None,
                        bytecode_compressed: None,
                    };
                }
                CguReuse::PostLto => unreachable!(),
            }

            let dep_node = cgu.codegen_dep_node(tcx);
            let (ModuleCodegenResult(module, work_product), _) =
                tcx.dep_graph.with_task(dep_node, tcx, cgu.name(), module_codegen, rustc::dep_graph::hash_result);

            fn module_codegen(tcx: TyCtxt<'_>, cgu_name: rustc_span::Symbol) -> ModuleCodegenResult {
                let cgu = tcx.codegen_unit(cgu_name);
                let mono_items = cgu.items_in_deterministic_order(tcx);

                let mut module = new_module(tcx, cgu_name.as_str().to_string());

                let mut debug = if tcx.sess.opts.debuginfo != DebugInfo::None {
                    let debug = DebugContext::new(
                        tcx,
                        module.target_config().pointer_type().bytes() as u8,
                    );
                    Some(debug)
                } else {
                    None
                };

                super::codegen_mono_items(tcx, &mut module, debug.as_mut(), mono_items);
                crate::main_shim::maybe_create_entry_wrapper(tcx, &mut module);

                emit_module(
                    tcx,
                    cgu.name().as_str().to_string(),
                    ModuleKind::Regular,
                    module,
                    debug,
                )
            }

            if let Some((id, product)) = work_product {
                work_products.insert(id, product);
            }

            module
        }).collect::<Vec<_>>()
    });

    tcx.sess.abort_if_errors();

    let mut allocator_module = new_module(tcx, "allocator_shim".to_string());
    let created_alloc_shim = crate::allocator::codegen(tcx, &mut allocator_module);

    let allocator_module = if created_alloc_shim {
        let ModuleCodegenResult(module, work_product) = emit_module(
            tcx,
            "allocator_shim".to_string(),
            ModuleKind::Allocator,
            allocator_module,
            None,
        );
        if let Some((id, product)) = work_product {
            work_products.insert(id, product);
        }
        Some(module)
    } else {
        None
    };

    rustc_incremental::assert_dep_graph(tcx);
    rustc_incremental::save_dep_graph(tcx);

    let metadata_module = if need_metadata_module {
        let _timer = tcx.prof.generic_activity("codegen crate metadata");
        let (metadata_cgu_name, tmp_file) = tcx.sess.time("write compressed metadata", || {
            use rustc::mir::mono::CodegenUnitNameBuilder;

            let cgu_name_builder = &mut CodegenUnitNameBuilder::new(tcx);
            let metadata_cgu_name = cgu_name_builder
                .build_cgu_name(LOCAL_CRATE, &["crate"], Some("metadata"))
                .as_str()
                .to_string();

            let tmp_file = tcx
                .output_filenames(LOCAL_CRATE)
                .temp_path(OutputType::Metadata, Some(&metadata_cgu_name));

            let obj = crate::backend::with_object(tcx.sess, &metadata_cgu_name, |object| {
                crate::metadata::write_metadata(tcx, object);
            });

            std::fs::write(&tmp_file, obj).unwrap();

            (metadata_cgu_name, tmp_file)
        });

        Some(CompiledModule {
            name: metadata_cgu_name,
            kind: ModuleKind::Metadata,
            object: Some(tmp_file),
            bytecode: None,
            bytecode_compressed: None,
        })
    } else {
        None
    };

    Box::new((CodegenResults {
        crate_name: tcx.crate_name(LOCAL_CRATE),
        modules,
        allocator_module,
        metadata_module,
        crate_hash: tcx.crate_hash(LOCAL_CRATE),
        metadata,
        windows_subsystem: None, // Windows is not yet supported
        linker_info: LinkerInfo::new(tcx),
        crate_info: CrateInfo::new(tcx),
    }, work_products))
}

// Adapted from https://github.com/rust-lang/rust/blob/303d8aff6092709edd4dbd35b1c88e9aa40bf6d8/src/librustc_codegen_ssa/base.rs#L922-L953
fn determine_cgu_reuse<'tcx>(tcx: TyCtxt<'tcx>, cgu: &CodegenUnit<'tcx>) -> CguReuse {
    if !tcx.dep_graph.is_fully_enabled() {
        return CguReuse::No;
    }

    let work_product_id = &cgu.work_product_id();
    if tcx.dep_graph.previous_work_product(work_product_id).is_none() {
        // We don't have anything cached for this CGU. This can happen
        // if the CGU did not exist in the previous session.
        return CguReuse::No;
    }

    // Try to mark the CGU as green. If it we can do so, it means that nothing
    // affecting the LLVM module has changed and we can re-use a cached version.
    // If we compile with any kind of LTO, this means we can re-use the bitcode
    // of the Pre-LTO stage (possibly also the Post-LTO version but we'll only
    // know that later). If we are not doing LTO, there is only one optimized
    // version of each module, so we re-use that.
    let dep_node = cgu.codegen_dep_node(tcx);
    assert!(
        !tcx.dep_graph.dep_node_exists(&dep_node),
        "CompileCodegenUnit dep-node for CGU `{}` already exists before marking.",
        cgu.name()
    );

    if tcx.dep_graph.try_mark_green(tcx, &dep_node).is_some() {
        CguReuse::PreLto
    } else {
        CguReuse::No
    }
}
<commit_msg>Refactor driver/aot.rs<commit_after>use rustc::dep_graph::{WorkProduct, WorkProductFileKind, WorkProductId};
use rustc::middle::cstore::EncodedMetadata;
use rustc::mir::mono::CodegenUnit;
use rustc::session::config::{DebugInfo, OutputType};
use rustc_session::cgu_reuse_tracker::CguReuse;
use rustc_codegen_ssa::back::linker::LinkerInfo;
use rustc_codegen_ssa::CrateInfo;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};

use crate::prelude::*;

use crate::backend::{Emit, WriteDebugInfo};

fn new_module(tcx: TyCtxt<'_>, name: String) -> Module<crate::backend::Backend> {
    let module = crate::backend::make_module(tcx.sess, name);
    assert_eq!(pointer_ty(tcx), module.target_config().pointer_type());
    module
}

struct ModuleCodegenResult(CompiledModule, Option<(WorkProductId, WorkProduct)>);

impl<HCX> HashStable<HCX> for ModuleCodegenResult {
    fn hash_stable(&self, _: &mut HCX, _: &mut StableHasher) {
        // do nothing
    }
}

fn emit_module<B: Backend>(
    tcx: TyCtxt<'_>,
    name: String,
    kind: ModuleKind,
    mut module: Module<B>,
    debug: Option<DebugContext>,
) -> ModuleCodegenResult
    where B::Product: Emit + WriteDebugInfo,
{
    module.finalize_definitions();
    let mut product = module.finish();

    if let Some(mut debug) = debug {
        debug.emit(&mut product);
    }

    let tmp_file = tcx
        .output_filenames(LOCAL_CRATE)
        .temp_path(OutputType::Object, Some(&name));
    let obj = product.emit();
    std::fs::write(&tmp_file, obj).unwrap();

    let work_product = if std::env::var("CG_CLIF_INCR_CACHE_DISABLED").is_ok() {
        None
    } else {
        rustc_incremental::copy_cgu_workproducts_to_incr_comp_cache_dir(
            tcx.sess,
            &name,
            &[(WorkProductFileKind::Object, tmp_file.clone())],
        )
    };

    ModuleCodegenResult(
        CompiledModule {
            name,
            kind,
            object: Some(tmp_file),
            bytecode: None,
            bytecode_compressed: None,
        },
        work_product,
    )
}

fn reuse_workproduct_for_cgu(
    tcx: TyCtxt<'_>,
    cgu: &CodegenUnit,
    work_products: &mut FxHashMap<WorkProductId, WorkProduct>,
) -> CompiledModule {
    let incr_comp_session_dir = tcx.sess.incr_comp_session_dir();
    let mut object = None;
    let work_product = cgu.work_product(tcx);
    for (kind, saved_file) in &work_product.saved_files {
        let obj_out = match kind {
            WorkProductFileKind::Object => {
                let path = tcx.output_filenames(LOCAL_CRATE).temp_path(OutputType::Object, Some(&cgu.name().as_str()));
                object = Some(path.clone());
                path
            }
            WorkProductFileKind::Bytecode | WorkProductFileKind::BytecodeCompressed => {
                panic!("cg_clif doesn't use bytecode");
            }
        };
        let source_file = rustc_incremental::in_incr_comp_dir(&incr_comp_session_dir, &saved_file);
        if let Err(err) = rustc_fs_util::link_or_copy(&source_file, &obj_out) {
            tcx.sess.err(&format!(
                "unable to copy {} to {}: {}",
                source_file.display(),
                obj_out.display(),
                err
            ));
        }
    }

    work_products.insert(cgu.work_product_id(), work_product);

    CompiledModule {
        name: cgu.name().to_string(),
        kind: ModuleKind::Regular,
        object,
        bytecode: None,
        bytecode_compressed: None,
    }
}

fn module_codegen(tcx: TyCtxt<'_>, cgu_name: rustc_span::Symbol) -> ModuleCodegenResult {
    let cgu = tcx.codegen_unit(cgu_name);
    let mono_items = cgu.items_in_deterministic_order(tcx);

    let mut module = new_module(tcx, cgu_name.as_str().to_string());

    let mut debug = if tcx.sess.opts.debuginfo != DebugInfo::None {
        let debug = DebugContext::new(
            tcx,
            module.target_config().pointer_type().bytes() as u8,
        );
        Some(debug)
    } else {
        None
    };

    super::codegen_mono_items(tcx, &mut module, debug.as_mut(), mono_items);
    crate::main_shim::maybe_create_entry_wrapper(tcx, &mut module);

    emit_module(
        tcx,
        cgu.name().as_str().to_string(),
        ModuleKind::Regular,
        module,
        debug,
    )
}

pub(super) fn run_aot(
    tcx: TyCtxt<'_>,
    metadata: EncodedMetadata,
    need_metadata_module: bool,
) -> Box<(CodegenResults, FxHashMap<WorkProductId, WorkProduct>)> {
    let mut work_products = FxHashMap::default();

    let (_, cgus) = tcx.collect_and_partition_mono_items(LOCAL_CRATE);

    if tcx.dep_graph.is_fully_enabled() {
        for cgu in &*cgus {
            tcx.codegen_unit(cgu.name());
        }
    }

    let modules = super::time(tcx, "codegen mono items", || {
        cgus.iter().map(|cgu| {
            let cgu_reuse = determine_cgu_reuse(tcx, cgu);
            tcx.sess.cgu_reuse_tracker.set_actual_reuse(&cgu.name().as_str(), cgu_reuse);

            match cgu_reuse {
                _ if std::env::var("CG_CLIF_INCR_CACHE_DISABLED").is_ok() => {}
                CguReuse::No => {}
                CguReuse::PreLto => {
                    return reuse_workproduct_for_cgu(tcx, &*cgu, &mut work_products);
                }
                CguReuse::PostLto => unreachable!(),
            }

            let dep_node = cgu.codegen_dep_node(tcx);
            let (ModuleCodegenResult(module, work_product), _) =
                tcx.dep_graph.with_task(dep_node, tcx, cgu.name(), module_codegen, rustc::dep_graph::hash_result);

            if let Some((id, product)) = work_product {
                work_products.insert(id, product);
            }

            module
        }).collect::<Vec<_>>()
    });

    tcx.sess.abort_if_errors();

    let mut allocator_module = new_module(tcx, "allocator_shim".to_string());
    let created_alloc_shim = crate::allocator::codegen(tcx, &mut allocator_module);

    let allocator_module = if created_alloc_shim {
        let ModuleCodegenResult(module, work_product) = emit_module(
            tcx,
            "allocator_shim".to_string(),
            ModuleKind::Allocator,
            allocator_module,
            None,
        );
        if let Some((id, product)) = work_product {
            work_products.insert(id, product);
        }
        Some(module)
    } else {
        None
    };

    rustc_incremental::assert_dep_graph(tcx);
    rustc_incremental::save_dep_graph(tcx);

    let metadata_module = if need_metadata_module {
        let _timer = tcx.prof.generic_activity("codegen crate metadata");
        let (metadata_cgu_name, tmp_file) = tcx.sess.time("write compressed metadata", || {
            use rustc::mir::mono::CodegenUnitNameBuilder;

            let cgu_name_builder = &mut CodegenUnitNameBuilder::new(tcx);
            let metadata_cgu_name = cgu_name_builder
                .build_cgu_name(LOCAL_CRATE, &["crate"], Some("metadata"))
                .as_str()
                .to_string();

            let tmp_file = tcx
                .output_filenames(LOCAL_CRATE)
                .temp_path(OutputType::Metadata, Some(&metadata_cgu_name));

            let obj = crate::backend::with_object(tcx.sess, &metadata_cgu_name, |object| {
                crate::metadata::write_metadata(tcx, object);
            });

            std::fs::write(&tmp_file, obj).unwrap();

            (metadata_cgu_name, tmp_file)
        });

        Some(CompiledModule {
            name: metadata_cgu_name,
            kind: ModuleKind::Metadata,
            object: Some(tmp_file),
            bytecode: None,
            bytecode_compressed: None,
        })
    } else {
        None
    };

    Box::new((CodegenResults {
        crate_name: tcx.crate_name(LOCAL_CRATE),
        modules,
        allocator_module,
        metadata_module,
        crate_hash: tcx.crate_hash(LOCAL_CRATE),
        metadata,
        windows_subsystem: None, // Windows is not yet supported
        linker_info: LinkerInfo::new(tcx),
        crate_info: CrateInfo::new(tcx),
    }, work_products))
}

// Adapted from https://github.com/rust-lang/rust/blob/303d8aff6092709edd4dbd35b1c88e9aa40bf6d8/src/librustc_codegen_ssa/base.rs#L922-L953
fn determine_cgu_reuse<'tcx>(tcx: TyCtxt<'tcx>, cgu: &CodegenUnit<'tcx>) -> CguReuse {
    if !tcx.dep_graph.is_fully_enabled() {
        return CguReuse::No;
    }

    let work_product_id = &cgu.work_product_id();
    if tcx.dep_graph.previous_work_product(work_product_id).is_none() {
        // We don't have anything cached for this CGU. This can happen
        // if the CGU did not exist in the previous session.
        return CguReuse::No;
    }

    // Try to mark the CGU as green. If it we can do so, it means that nothing
    // affecting the LLVM module has changed and we can re-use a cached version.
    // If we compile with any kind of LTO, this means we can re-use the bitcode
    // of the Pre-LTO stage (possibly also the Post-LTO version but we'll only
    // know that later). If we are not doing LTO, there is only one optimized
    // version of each module, so we re-use that.
    let dep_node = cgu.codegen_dep_node(tcx);
    assert!(
        !tcx.dep_graph.dep_node_exists(&dep_node),
        "CompileCodegenUnit dep-node for CGU `{}` already exists before marking.",
        cgu.name()
    );

    if tcx.dep_graph.try_mark_green(tcx, &dep_node).is_some() {
        CguReuse::PreLto
    } else {
        CguReuse::No
    }
}
<|endoftext|>


<commit_before>use xdg::BaseDirectories;

use program_info::ProgramInfo;
use qork;
use system_info::SystemInfo;

// The complete execution context of Qork.
#[derive(Debug)]
pub struct Context {
    // xdg base dir object, typically '~/.config/qork', with a default profile of
    // 'default', which means the effective directory is '~/.config/qork/default'
    xdg: BaseDirectories,
    system_info: SystemInfo,
    program_info: ProgramInfo
    // TODO: user_name
}

impl Context {
    pub fn new() -> Context {
        let pi = ProgramInfo::new();

        // TODO: Can this be simplified?
        let profile = {
            match pi.parsed_args().xdg_profile()
            {
                &Some(ref p) => { p.clone().to_string() },
                &None => "default".to_string()
            }
        };

        let bd = BaseDirectories::with_profile(qork::APP_NAME, profile).unwrap();

        Context {
            xdg: bd,
            system_info: SystemInfo::new(),
            program_info: pi
        }
    }

    pub fn system_info(&self) -> &SystemInfo {
        &self.system_info
    }

    pub fn program_info(&self) -> &ProgramInfo {
        &self.program_info
    }

    pub fn xdg(&self) -> &BaseDirectories {
        &self.xdg
    }

    pub fn log_created_message(&self) -> () {
        let mdate = &self.program_info.modified_date().map(|m| m.format("%Y-%m-%d %H:%M:%S%.3f UTC").to_string());
        let path = &self.program_info.path().as_ref().and_then(|p| p.to_str()).map(String::from);

        //info!("{:?}", self);

        // info!(self.logger, "Created Context";
        //     "system_info.hostname" => self.system_info.hostname(),
        //     "system_info.arch" => self.system_info.arch(),
        //     "system_info.endian" => self.system_info.endian(),
        //     "system_info.env" => self.system_info.env(),
        //     "system_info.family" => self.system_info.family(),
        //     "system_info.os" => self.system_info.os(),
        //     "config_directory" => %&self.xdg.get_config_home().display(),
        //     "program_info.version" => &self.program_info.version(),
        //     "program_info.modified_date" => mdate,
        //     "program_info.size" => &self.program_info.size(),
        //     "program_info.path" => path
        //  );
    }
}<commit_msg>Refactor Context. Minor change.<commit_after>use xdg::BaseDirectories;

use program_info::ProgramInfo;
use qork;
use system_info::SystemInfo;

// The complete execution context of Qork.
#[derive(Debug)]
pub struct Context {
    // xdg base dir object, typically '~/.config/qork', with a default profile of
    // 'default', which means the effective directory is '~/.config/qork/default'
    xdg: BaseDirectories,
    system_info: SystemInfo,
    program_info: ProgramInfo
    // TODO: user_name
}

impl Context {
    pub fn new() -> Context {
        let pi = ProgramInfo::new();

        let profile = {
            match pi.parsed_args().xdg_profile()
            {
                &Some(ref p) => { p.clone() },
                &None => "default".to_string()
            }
        };

        let bd = BaseDirectories::with_profile(qork::APP_NAME, profile).unwrap();

        Context {
            xdg: bd,
            system_info: SystemInfo::new(),
            program_info: pi
        }
    }

    pub fn system_info(&self) -> &SystemInfo {
        &self.system_info
    }

    pub fn program_info(&self) -> &ProgramInfo {
        &self.program_info
    }

    pub fn xdg(&self) -> &BaseDirectories {
        &self.xdg
    }

    pub fn log_created_message(&self) -> () {
        let mdate = &self.program_info.modified_date().map(|m| m.format("%Y-%m-%d %H:%M:%S%.3f UTC").to_string());
        let path = &self.program_info.path().as_ref().and_then(|p| p.to_str()).map(String::from);

        //info!("{:?}", self);

        // info!(self.logger, "Created Context";
        //     "system_info.hostname" => self.system_info.hostname(),
        //     "system_info.arch" => self.system_info.arch(),
        //     "system_info.endian" => self.system_info.endian(),
        //     "system_info.env" => self.system_info.env(),
        //     "system_info.family" => self.system_info.family(),
        //     "system_info.os" => self.system_info.os(),
        //     "config_directory" => %&self.xdg.get_config_home().display(),
        //     "program_info.version" => &self.program_info.version(),
        //     "program_info.modified_date" => mdate,
        //     "program_info.size" => &self.program_info.size(),
        //     "program_info.path" => path
        //  );
    }
}<|endoftext|>


<commit_before>use request_response_matcher::*;
use chrono::*;

#[derive(Debug)]
#[derive(PartialEq)]
pub struct FilterConditions {
    pub include_terms: Option<Vec<String>>,
    pub exclude_terms: Option<Vec<String>>,
    pub latest_time: Option<Duration>,
}

pub fn matches_filter(pair: &RequestResponsePair, conditions: &FilterConditions) -> bool {
    let matches_include_terms: bool = match conditions.include_terms {
        Some(ref include_terms) => {
            include_terms.iter()
                .fold(false, |result, include_term| {
                    result || pair.request.original_log_line.contains(include_term) ||
                    pair.response.original_log_line.contains(include_term)
                })
        }
        None => true,
    };

    let matches_exclude_terms: bool = match conditions.exclude_terms {
        Some(ref exclude_terms) => {
            !exclude_terms.iter()
                .fold(false, |result, exclude_term| {
                    result || pair.request.original_log_line.contains(exclude_term) ||
                    pair.response.original_log_line.contains(exclude_term)
                })
        }
        None => true,
    };

    let matches_time: bool = match conditions.latest_time {
        Some(latest_time) => {
            let timezone = pair.request.time.timezone();
            let now = UTC::now().with_timezone(&timezone);
            let include_since_time = now - latest_time;
            pair.request.time >= include_since_time
        }
        None => true,
    };

    matches_include_terms && matches_exclude_terms && matches_time
}

#[cfg(test)]
mod tests {
    use log_parser::log_events::*;
    use request_response_matcher::*;
    use chrono::*;
    use super::*;

    fn get_fixture() -> RequestResponsePair {
        RequestResponsePair {
            request: Request::new_from_log_line(&"08/Apr/2016:09:57:47 +0200 [001] -> GET \
                                                  /content/some/page.html HTTP/1.1"
                    .to_string())
                .unwrap(),
            response: Response::new_from_log_line(&"08/Apr/2016:09:57:47 +0200 [001] <- 200 \
                                                    text/html 1ms"
                    .to_string())
                .unwrap(),
        }
    }

    #[test]
    fn test_filter_none() {
        let pair = get_fixture();

        let conditions = FilterConditions {
            include_terms: None,
            exclude_terms: None,
            latest_time: None,
        };

        let result = matches_filter(&pair, &conditions);

        assert_eq!(result, true);
    }

    #[test]
    fn test_filter_include_request() {
        let pair = get_fixture();

        let conditions = FilterConditions {
            include_terms: Some(vec!["page.html".to_string()]),
            exclude_terms: None,
            latest_time: None,
        };

        let result = matches_filter(&pair, &conditions);

        assert_eq!(result, true);
    }

    #[test]
    fn test_filter_include_response() {
        let pair = get_fixture();

        let conditions = FilterConditions {
            include_terms: Some(vec!["text/html".to_string()]),
            exclude_terms: None,
            latest_time: None,
        };

        let result = matches_filter(&pair, &conditions);

        assert_eq!(result, true);
    }

    #[test]
    fn test_filter_exclude_request() {
        let pair = get_fixture();

        let conditions = FilterConditions {
            include_terms: None,
            exclude_terms: Some(vec!["page.html".to_string()]),
            latest_time: None,
        };

        let result = matches_filter(&pair, &conditions);

        assert_eq!(result, false);
    }

    #[test]
    fn test_filter_exclude_response() {
        let pair = get_fixture();

        let conditions = FilterConditions {
            include_terms: None,
            exclude_terms: Some(vec!["text/html".to_string()]),
            latest_time: None,
        };

        let result = matches_filter(&pair, &conditions);

        assert_eq!(result, false);
    }

    #[test]
    fn test_filter_include_multiple() {
        let pair = get_fixture();

        let conditions = FilterConditions {
            include_terms: Some(vec!["irrelevant.html".to_string(), "page.html".to_string()]),
            exclude_terms: None,
            latest_time: None,
        };

        let result = matches_filter(&pair, &conditions);

        assert_eq!(result, true);
    }

    #[test]
    fn test_filter_exclude_multiple() {
        let pair = get_fixture();

        let conditions = FilterConditions {
            include_terms: None,
            exclude_terms: Some(vec!["irrelevant.html".to_string(), "page.html".to_string()]),
            latest_time: None,
        };

        let result = matches_filter(&pair, &conditions);

        assert_eq!(result, false);
    }

    #[test]
    fn test_filter_time_matches() {
        let mut pair = get_fixture();
        pair.request.time = UTC::now().with_timezone(&pair.request.time.timezone());

        let conditions = FilterConditions {
            include_terms: None,
            exclude_terms: None,
            latest_time: Some(Duration::minutes(10)),
        };

        let result = matches_filter(&pair, &conditions);

        assert_eq!(result, true);
    }

    #[test]
    fn test_filter_time_matches_not() {
        let mut pair = get_fixture();
        pair.request.time = UTC::now().with_timezone(&pair.request.time.timezone()) -
                            Duration::minutes(12);;

        let conditions = FilterConditions {
            include_terms: None,
            exclude_terms: None,
            latest_time: Some(Duration::minutes(10)),
        };

        let result = matches_filter(&pair, &conditions);

        assert_eq!(result, false);
    }
}
<commit_msg>Extra semicolon<commit_after>use request_response_matcher::*;
use chrono::*;

#[derive(Debug)]
#[derive(PartialEq)]
pub struct FilterConditions {
    pub include_terms: Option<Vec<String>>,
    pub exclude_terms: Option<Vec<String>>,
    pub latest_time: Option<Duration>,
}

pub fn matches_filter(pair: &RequestResponsePair, conditions: &FilterConditions) -> bool {
    let matches_include_terms: bool = match conditions.include_terms {
        Some(ref include_terms) => {
            include_terms.iter()
                .fold(false, |result, include_term| {
                    result || pair.request.original_log_line.contains(include_term) ||
                    pair.response.original_log_line.contains(include_term)
                })
        }
        None => true,
    };

    let matches_exclude_terms: bool = match conditions.exclude_terms {
        Some(ref exclude_terms) => {
            !exclude_terms.iter()
                .fold(false, |result, exclude_term| {
                    result || pair.request.original_log_line.contains(exclude_term) ||
                    pair.response.original_log_line.contains(exclude_term)
                })
        }
        None => true,
    };

    let matches_time: bool = match conditions.latest_time {
        Some(latest_time) => {
            let timezone = pair.request.time.timezone();
            let now = UTC::now().with_timezone(&timezone);
            let include_since_time = now - latest_time;
            pair.request.time >= include_since_time
        }
        None => true,
    };

    matches_include_terms && matches_exclude_terms && matches_time
}

#[cfg(test)]
mod tests {
    use log_parser::log_events::*;
    use request_response_matcher::*;
    use chrono::*;
    use super::*;

    fn get_fixture() -> RequestResponsePair {
        RequestResponsePair {
            request: Request::new_from_log_line(&"08/Apr/2016:09:57:47 +0200 [001] -> GET \
                                                  /content/some/page.html HTTP/1.1"
                    .to_string())
                .unwrap(),
            response: Response::new_from_log_line(&"08/Apr/2016:09:57:47 +0200 [001] <- 200 \
                                                    text/html 1ms"
                    .to_string())
                .unwrap(),
        }
    }

    #[test]
    fn test_filter_none() {
        let pair = get_fixture();

        let conditions = FilterConditions {
            include_terms: None,
            exclude_terms: None,
            latest_time: None,
        };

        let result = matches_filter(&pair, &conditions);

        assert_eq!(result, true);
    }

    #[test]
    fn test_filter_include_request() {
        let pair = get_fixture();

        let conditions = FilterConditions {
            include_terms: Some(vec!["page.html".to_string()]),
            exclude_terms: None,
            latest_time: None,
        };

        let result = matches_filter(&pair, &conditions);

        assert_eq!(result, true);
    }

    #[test]
    fn test_filter_include_response() {
        let pair = get_fixture();

        let conditions = FilterConditions {
            include_terms: Some(vec!["text/html".to_string()]),
            exclude_terms: None,
            latest_time: None,
        };

        let result = matches_filter(&pair, &conditions);

        assert_eq!(result, true);
    }

    #[test]
    fn test_filter_exclude_request() {
        let pair = get_fixture();

        let conditions = FilterConditions {
            include_terms: None,
            exclude_terms: Some(vec!["page.html".to_string()]),
            latest_time: None,
        };

        let result = matches_filter(&pair, &conditions);

        assert_eq!(result, false);
    }

    #[test]
    fn test_filter_exclude_response() {
        let pair = get_fixture();

        let conditions = FilterConditions {
            include_terms: None,
            exclude_terms: Some(vec!["text/html".to_string()]),
            latest_time: None,
        };

        let result = matches_filter(&pair, &conditions);

        assert_eq!(result, false);
    }

    #[test]
    fn test_filter_include_multiple() {
        let pair = get_fixture();

        let conditions = FilterConditions {
            include_terms: Some(vec!["irrelevant.html".to_string(), "page.html".to_string()]),
            exclude_terms: None,
            latest_time: None,
        };

        let result = matches_filter(&pair, &conditions);

        assert_eq!(result, true);
    }

    #[test]
    fn test_filter_exclude_multiple() {
        let pair = get_fixture();

        let conditions = FilterConditions {
            include_terms: None,
            exclude_terms: Some(vec!["irrelevant.html".to_string(), "page.html".to_string()]),
            latest_time: None,
        };

        let result = matches_filter(&pair, &conditions);

        assert_eq!(result, false);
    }

    #[test]
    fn test_filter_time_matches() {
        let mut pair = get_fixture();
        pair.request.time = UTC::now().with_timezone(&pair.request.time.timezone());

        let conditions = FilterConditions {
            include_terms: None,
            exclude_terms: None,
            latest_time: Some(Duration::minutes(10)),
        };

        let result = matches_filter(&pair, &conditions);

        assert_eq!(result, true);
    }

    #[test]
    fn test_filter_time_matches_not() {
        let mut pair = get_fixture();
        pair.request.time = UTC::now().with_timezone(&pair.request.time.timezone()) -
                            Duration::minutes(12);

        let conditions = FilterConditions {
            include_terms: None,
            exclude_terms: None,
            latest_time: Some(Duration::minutes(10)),
        };

        let result = matches_filter(&pair, &conditions);

        assert_eq!(result, false);
    }
}
<|endoftext|>


<commit_before>use std::io;
use std::fmt;
use std::error;
use std::num::ParseIntError;
use std::io::BufRead;

#[derive(Debug)]
pub enum Error {
    Io(io::Error),
    Format
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::Io(ref err) => err.fmt(f),
            Error::Format => write!(f, "Format error"),
        }
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            Error::Io(ref err) => err.description(),
            Error::Format => "Format error",
        }
    }
}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Error {
        Error::Io(err)
    }
}

impl From<ParseIntError> for Error {
    fn from(_: ParseIntError) -> Error {
        Error::Format
    }
}

fn is_comment(s: &String) -> bool {
    s.chars().next().unwrap() == '#'
}

pub fn next_valid_line(reader: &mut BufRead, buf: &mut String) -> io::Result<usize> {
    loop {
        buf.clear();
        let len = try!(reader.read_line(buf));

        if !buf.trim().is_empty() && !is_comment(buf) {
            return Ok(len);
        }
    }
}
<commit_msg>Format: handle eof correctly in next_valid_line<commit_after>use std::io;
use std::fmt;
use std::error;
use std::num::ParseIntError;
use std::io::BufRead;

#[derive(Debug)]
pub enum Error {
    Io(io::Error),
    Format
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::Io(ref err) => err.fmt(f),
            Error::Format => write!(f, "Format error"),
        }
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            Error::Io(ref err) => err.description(),
            Error::Format => "Format error",
        }
    }
}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Error {
        Error::Io(err)
    }
}

impl From<ParseIntError> for Error {
    fn from(_: ParseIntError) -> Error {
        Error::Format
    }
}

fn is_comment(s: &String) -> bool {
    s.chars().next().unwrap() == '#'
}

pub fn next_valid_line(reader: &mut BufRead, buf: &mut String) -> io::Result<usize> {
    loop {
        buf.clear();
        let len = try!(reader.read_line(buf));

        if len == 0 {
            return Err(io::Error::new(io::ErrorKind::UnexpectedEof, ""));
        }

        if !buf.trim().is_empty() && !is_comment(buf) {
            return Ok(len);
        }
    }
}
<|endoftext|>


<commit_before>/// Serial port management module supporting one
/// writer and multiple readers
///
/// Clients can lock a port for writing, but
/// subscribe to data from multiple ports for reads

use std::collections::HashSet;
use std::sync::mpsc::{Receiver, TryRecvError};
use std::thread;
use std::time::Duration;

use base64;

use serial_support::errors::*;
use serial_support::messages::*;
use serial_support::sub_manager::*;
use serial_support::port_manager::*;
use serial_support::writelock_manager::*;
use serial_support::common::*;

/// Manager manages connection state.
/// TODO Needs a better name
pub struct Manager {
  /// Manage write lock status
  writelock_manager: WriteLockManager,
  /// Manage ports
  port_manager: PortManager,
  /// Manage subscriptions
  sub_manager: SubscriptionManager,
  /// Receiver for serial requests
  receiver: Receiver<(String, SerialRequest)>,
  /// Receiver for response subscription requests
  subsc_receiver: SubscReceiver,
}

impl Manager {
  ///Constructor
  pub fn new(receiver: Receiver<(String, SerialRequest)>,
             subsc_receiver: SubscReceiver)
             -> Manager {
    Manager {
      writelock_manager: WriteLockManager::new(),
      port_manager: PortManager::new(),
      sub_manager: SubscriptionManager::new(),
      receiver: receiver,
      subsc_receiver: subsc_receiver,
    }
  }

  ///Spawn an instance in a new thread.
  pub fn spawn(receiver: Receiver<(String, SerialRequest)>,
               subsc_receiver: SubscReceiver)
               -> thread::JoinHandle<()> {
    thread::spawn(move || { Manager::new(receiver, subsc_receiver).run(); })
  }

  /// Main loop
  fn run(&mut self) {

    // Bad ports we couldn't read from
    // A set of SerialResponse::Errors built from
    // from the serial port read/write error responses
    let mut bad_ports = HashSet::<String>::new();
    // Shutdown flag
    let mut shutdown = false;
    // Check about 30 times a second
    let sleep_dur = Duration::from_millis(33);

    while !shutdown {
      // Sleep for a little bit to avoid pegging cpu
      // TODO Dynamic timing loop
      thread::sleep(sleep_dur);

      // Handle serial operation requests
      match self.receiver.try_recv() {
        Err(e) => {
          match e {
            TryRecvError::Empty => {
              // nothing to do
            }
            TryRecvError::Disconnected => {
              // Remote end hung up, time to shutdown
              info!("Shutting down SerialPortManager");
              shutdown = true;
            }
          }
        }
        Ok(req) => self.handle_serial_request(req.0, req.1),
      }

      // Check for new data on each port
      for (port_name, result) in self.port_manager.read_all_ports() {
        match result {
          Ok(data) => {
            let response = match String::from_utf8(data) {
              // We need to send as binary
              Err(e) => {
                SerialResponse::Read {
                  port: port_name.to_string(),
                  data: base64::encode(&e.into_bytes()),
                  base64: Some(true),
                }
              }
              Ok(s) => {
                SerialResponse::Read {
                  port: port_name.to_string(),
                  data: s,
                  base64: Some(false),
                }
              }
            };
            self.broadcast_message_for_port(&port_name, response);
          }
          // Send data reads
          Err(e) => {
            warn!("Error reading port!");
            warn!("{}", e);
            bad_ports.insert(port_name);
          }
        }
      }

      //Handle write requests
      let mut recv_count = 0;
      let mut do_recv = true;
      while do_recv {
        recv_count += 1;
        match self.subsc_receiver.try_recv() {
          Ok(sub_request) => self.sub_manager.add_subscription(sub_request),
          Err(e) => {
            match e {
              TryRecvError::Disconnected => {
                // Does this mean all senders have disconnected?
                // Or just one?
                debug!("Got disconnected when trying to get serial request");
              }
              TryRecvError::Empty => do_recv = false,
            }
          }
        }

        // Don't let this loop get stuck here forever
        // starving the outer loop
        if recv_count == 50 {
          do_recv = false;
        }
      }

      // Cleanup bad serial ports that failed read or write
      // We remove them from everything before
      self.cleanup_bad_ports(&bad_ports);
      bad_ports.clear();
    }
  }

  /// Handles and dispatches SerialRequest sent by
  /// the channel
  fn handle_serial_request(&mut self, sub_id: String, msg: SerialRequest) {
    let response = match msg {
      SerialRequest::Open { port } => self.handle_open_port(sub_id, port),
      SerialRequest::WriteLock { port } => self.handle_write_lock(sub_id, port),
      SerialRequest::ReleaseWriteLock { port } => self.handle_release_write_lock(sub_id, port),
      SerialRequest::Write { port, data, base64 } => {
        self.handle_write_port(sub_id, port, data, base64.unwrap_or(false))
      }
      SerialRequest::Close { port } => self.handle_close_port(sub_id, port),
      SerialRequest::List {} => self.handle_list_ports(sub_id),
    };
    match response {
      Ok(response) => {
        // Send acknowledge?
      }
      Err(e) => {
        warn!("Error '{}' occured handling serial request message", e);
        // Send error?
      }
    }
  }

  /// Handle write port requests
  fn handle_write_port(&self,
                       sub_id: String,
                       port_name: String,
                       data: String,
                       base_64: bool)
                       -> Result<SerialResponse> {
    self.check_sub_id(&sub_id)?;
    self.check_owns_writelock(&port_name, &sub_id)?;
    match base_64 {
      true => {
        base64::decode(&data)
          .map_err(|e| ErrorKind::Base64(e).into())
          .and_then(|d| self.port_manager.write_port(&port_name, &d))
          .and(Ok(SerialResponse::Ok { msg: "Write successful".to_string() }))
      }
      false => {
        self
          .port_manager
          .write_port(&port_name, data.as_bytes())
          .and(Ok(SerialResponse::Ok { msg: "Write successful".to_string() }))
      }
    }
  }

  /// Handle write lock requests
  fn handle_write_lock(&mut self, sub_id: String, port_name: String) -> Result<SerialResponse> {
    self.check_sub_id(&sub_id)?;
    self
      .writelock_manager
      .lock_port(&port_name, &sub_id)
      .and(Ok(SerialResponse::Ok {
                msg: format!("Locking port {} succeeded", port_name).to_string(),
              }))
  }

  /// Handle write requests
  fn handle_release_write_lock(&mut self,
                               sub_id: String,
                               port_name: Option<String>)
                               -> Result<SerialResponse> {
    match port_name {
      None => {
        self.writelock_manager.unlock_all_ports_for_sub(&sub_id);
        Ok(SerialResponse::Ok { msg: "Unlocking all ports locked by client succeeded".to_string() })
      }
      Some(port_name) => {
        self
          .writelock_manager
          .unlock_port(&port_name, &sub_id)
          .and(Ok(SerialResponse::Ok {
                    msg: format!("Locking port {} succeeded", port_name).to_string(),
                  }))
      }
    }
  }

  /// Handle open port requests
  fn handle_open_port(&mut self, sub_id: String, port_name: String) -> Result<SerialResponse> {
    self.check_sub_id(&sub_id)?;
    self.port_manager.open_port(&port_name)?;
    self
      .sub_manager
      .add_port(&sub_id, &port_name)
      .and(Ok(SerialResponse::Ok {
                msg: format!("Opening port {} succeeded", port_name).to_string(),
              }))
  }

  /// Handle list ports request
  fn handle_list_ports(&mut self, sub_id: String) -> Result<SerialResponse> {
    self.check_sub_id(&sub_id)?;
    let port_names: Result<Vec<String>> =
      self
        .port_manager
        .list_ports()
        .map(|v| v.iter().map(|v| v.port_name.clone()).collect());
    let resp = port_names.map(|pns| SerialResponse::List { ports: pns });
    self.send_message(&sub_id,
                      resp.unwrap_or(SerialResponse::Error {
                                       display: "".to_string(),
                                       description: "".to_string(),
                                     }));
    Ok(SerialResponse::Ok { msg: "Derp".to_string() })
  }

  /// Handle close port requests
  fn handle_close_port(&mut self,
                       sub_id: String,
                       port_name: Option<String>)
                       -> Result<SerialResponse> {
    self.check_sub_id(&sub_id)?;
    // Unsubscribe from ports
    match port_name {
      None => self.sub_manager.clear_ports(Some(&sub_id)),
      Some(pn) => self.sub_manager.remove_port(&sub_id, &pn),
    }?;
    // Close ports with no subscribers
    let open_ports = self.port_manager.open_ports();
    let subscribed_ports = self.sub_manager.subscribed_ports();
    let ports_with_no_subs = open_ports.difference(&subscribed_ports);

    // For each open port that isn't subscribed,
    for port_to_close in ports_with_no_subs {
      // close it
      self.port_manager.close_port(&port_to_close);
      // remove the write lock
      self.writelock_manager.clear_lock(&port_to_close);
      // Let them know its closed
      let close_resp = SerialResponse::Closed { port: port_to_close.clone() };
      self.send_message(&sub_id, close_resp);
    }
    Ok(SerialResponse::Ok { msg: "Derp".to_string() })
  }

  /// Cleanup any bad ports
  /// Clears out the passed in hash set
  fn cleanup_bad_ports(&mut self, bad_ports: &HashSet<String>) {
    for port_name in bad_ports.iter() {
      // Tell everyone port is sick
      let err_resp = SerialResponse::Error {
        display: format!("Error reading from port '{}', closing!", port_name),
        description: format!("An error occured while trying to read data from '{}'",
                             port_name),
      };
      self.broadcast_message_for_port(port_name, err_resp);
      // Tesll everyone the sick ports were closed
      let close_resp = SerialResponse::Closed { port: port_name.clone() };
      self.broadcast_message_for_port(port_name, close_resp);
      // Close bad ports
      self.port_manager.close_port(port_name);
      // Remove write locks on bad ports
      self.writelock_manager.clear_lock(port_name);
      // Remove bad ports from subscriptions
      self.sub_manager.remove_port_from_all(port_name);
    }
    // Clear set of bad_ports
  }

  pub fn send_message(&mut self, sub_id: &String, msg: SerialResponse) {
    match self.sub_manager.send_message(sub_id, msg) {
      Err(e) => {
        let mut bad_subs = Vec::new();
        bad_subs.push(e);
        self.cleanup_bad_subs(bad_subs);
      }
      _ => {}
    }
  }

  /// Broadcast a message to all subscribers and
  /// then cleanup any subs that errored
  pub fn broadcast_message(&mut self, msg: SerialResponse) {
    let bad_subs = self.sub_manager.broadcast_message(msg);
    self.cleanup_bad_subs(bad_subs);
  }

  /// Broadcast a message to all subscribers interested in the
  /// given port and then cleanup any subs that errored
  pub fn broadcast_message_for_port(&mut self, port_name: &String, msg: SerialResponse) {
    let bad_subs = self
      .sub_manager
      .broadcast_message_for_port(port_name, msg);
    self.cleanup_bad_subs(bad_subs);
  }

  /// Cleanup any bad subs where a message send failed
  fn cleanup_bad_subs(&mut self, bad_subs: Vec<Error>) {
    for e in bad_subs {
      match e {
        Error(ErrorKind::SubscriberSendError(sub_id), _) => {
          self.sub_manager.end_subscription(&sub_id);
          self.writelock_manager.unlock_all_ports_for_sub(&sub_id);
        }
        _ => { /*nop*/ }
      }
    }
  }

  fn check_sub_id(&self, sub_id: &String) -> Result<()> {
    self.sub_manager.check_subscription_exists(sub_id)
  }

  fn check_owns_writelock(&self, port_name: &String, sub_id: &String) -> Result<()> {
    self
      .writelock_manager
      .check_owns_write_lock(port_name, sub_id)
  }
}
<commit_msg>Some code cleanup. Make every SerialRequest variant handler responsible for sending messages<commit_after>/// Serial port management module supporting one
/// writer and multiple readers
///
/// Clients can lock a port for writing, but
/// subscribe to data from multiple ports for reads

use std::collections::HashSet;
use std::sync::mpsc::{Receiver, TryRecvError};
use std::thread;
use std::time::Duration;

use base64;

use serial_support::errors::*;
use serial_support::messages::*;
use serial_support::sub_manager::*;
use serial_support::port_manager::*;
use serial_support::writelock_manager::*;
use serial_support::common::*;

/// Manager manages connection state.
/// TODO Needs a better name
pub struct Manager {
  /// Manage write lock status
  writelock_manager: WriteLockManager,
  /// Manage ports
  port_manager: PortManager,
  /// Manage subscriptions
  sub_manager: SubscriptionManager,
  /// Receiver for serial requests
  receiver: Receiver<(String, SerialRequest)>,
  /// Receiver for response subscription requests
  subsc_receiver: SubscReceiver,
}

impl Manager {
  ///Constructor
  pub fn new(receiver: Receiver<(String, SerialRequest)>,
             subsc_receiver: SubscReceiver)
             -> Manager {
    Manager {
      writelock_manager: WriteLockManager::new(),
      port_manager: PortManager::new(),
      sub_manager: SubscriptionManager::new(),
      receiver: receiver,
      subsc_receiver: subsc_receiver,
    }
  }

  ///Spawn an instance in a new thread.
  pub fn spawn(receiver: Receiver<(String, SerialRequest)>,
               subsc_receiver: SubscReceiver)
               -> thread::JoinHandle<()> {
    thread::spawn(move || { Manager::new(receiver, subsc_receiver).run(); })
  }

  /// Main loop
  fn run(&mut self) {

    // Bad ports we couldn't read from
    // A set of SerialResponse::Errors built from
    // from the serial port read/write error responses
    let mut bad_ports = HashSet::<String>::new();
    // Shutdown flag
    let mut shutdown = false;
    // Check about 30 times a second
    let sleep_dur = Duration::from_millis(33);

    while !shutdown {
      // Sleep for a little bit to avoid pegging cpu
      // TODO Dynamic timing loop
      thread::sleep(sleep_dur);

      // Handle serial operation requests
      match self.receiver.try_recv() {
        Err(e) => {
          match e {
            TryRecvError::Empty => {
              // nothing to do
            }
            TryRecvError::Disconnected => {
              // Remote end hung up, time to shutdown
              info!("Shutting down SerialPortManager");
              shutdown = true;
            }
          }
        }
        Ok(req) => self.handle_serial_request(&req.0, req.1),
      }

      // Check for new data on each port
      for (port_name, result) in self.port_manager.read_all_ports() {
        match result {
          Ok(data) => {
            let response = match String::from_utf8(data) {
              // We need to send as binary
              Err(e) => {
                SerialResponse::Read {
                  port: port_name.to_string(),
                  data: base64::encode(&e.into_bytes()),
                  base64: Some(true),
                }
              }
              Ok(s) => {
                SerialResponse::Read {
                  port: port_name.to_string(),
                  data: s,
                  base64: Some(false),
                }
              }
            };
            self.broadcast_message_for_port(&port_name, response);
          }
          // Send data reads
          Err(e) => {
            warn!("Error reading port!");
            warn!("{}", e);
            bad_ports.insert(port_name);
          }
        }
      }

      //Handle write requests
      let mut recv_count = 0;
      let mut do_recv = true;
      while do_recv {
        recv_count += 1;
        match self.subsc_receiver.try_recv() {
          Ok(sub_request) => self.sub_manager.add_subscription(sub_request),
          Err(e) => {
            match e {
              TryRecvError::Disconnected => {
                // Does this mean all senders have disconnected?
                // Or just one?
                debug!("Got disconnected when trying to get serial request");
              }
              TryRecvError::Empty => do_recv = false,
            }
          }
        }

        // Don't let this loop get stuck here forever
        // starving the outer loop
        if recv_count == 50 {
          do_recv = false;
        }
      }

      // Cleanup bad serial ports that failed read or write
      // We remove them from everything before
      self.cleanup_bad_ports(&bad_ports);
      bad_ports.clear();
    }
  }

  /// Handles and dispatches SerialRequest sent by
  /// the channel
  fn handle_serial_request(&mut self, sub_id: &String, msg: SerialRequest) {
    let response = match msg {
      SerialRequest::Open { port } => self.handle_open_port(sub_id, port),
      SerialRequest::WriteLock { port } => self.handle_write_lock(sub_id, port),
      SerialRequest::ReleaseWriteLock { port } => self.handle_release_write_lock(sub_id, port),
      SerialRequest::Write { port, data, base64 } => {
        self.handle_write_port(sub_id, port, data, base64.unwrap_or(false))
      }
      SerialRequest::Close { port } => self.handle_close_port(sub_id, port),
      SerialRequest::List {} => self.handle_list_ports(sub_id),
    };
    match response {
      Ok(response) => {
        // Send acknowledge?
      }
      Err(e) => {
        warn!("Error '{}' occured handling serial request message", e);
        // Send error?
        self.send_message(&sub_id,to_serial_response_error(e));
      }
    }
  }

  /// Handle write port requests
  fn handle_write_port(&mut self,
                       sub_id: &String,
                       port_name: String,
                       data: String,
                       base_64: bool)
                       -> Result<()> {
    self.check_sub_id(&sub_id)?;
    self.check_owns_writelock(&port_name, &sub_id)?;
    match base_64 {
      true => {
        base64::decode(&data)
          .map_err(|e| ErrorKind::Base64(e).into())
          .and_then(|d| self.port_manager.write_port(&port_name, &d))
          .map(|_|self.send_message(&sub_id,SerialResponse::Ok { msg: "Write successful".to_string() }))
      }
      false => {
        self
          .port_manager
          .write_port(&port_name, data.as_bytes())
          .map(|_|self.send_message(&sub_id,SerialResponse::Ok { msg: "Write successful".to_string() }))
      }
    }
  }

  /// Handle write lock requests
  fn handle_write_lock(&mut self, sub_id: &String, port_name: String) -> Result<()> {
    self.check_sub_id(&sub_id)?;
    self
      .writelock_manager
      .lock_port(&port_name, &sub_id)
      .map(|_|self.send_message(&sub_id,SerialResponse::Ok {
                msg: format!("Locking port {} succeeded", port_name).to_string(),
              }))
  }

  /// Handle write requests
  fn handle_release_write_lock(&mut self,
                               sub_id: &String,
                               port_name: Option<String>)
                               -> Result<()> {
    match port_name {
      None => {
        self.writelock_manager.unlock_all_ports_for_sub(&sub_id);
        Ok(self.send_message(&sub_id,SerialResponse::Ok { msg: "Unlocking all ports locked by client succeeded".to_string() }))
      }
      Some(port_name) => {
        self
          .writelock_manager
          .unlock_port(&port_name, &sub_id)
          .map(|_|self.send_message(&sub_id,SerialResponse::Ok {
                    msg: format!("Locking port {} succeeded", port_name).to_string(),
                  }))
      }
    }
  }

  /// Handle open port requests
  fn handle_open_port(&mut self, sub_id: &String, port_name: String) -> Result<()> {
    self.check_sub_id(&sub_id)?;
    self.port_manager.open_port(&port_name)?;
    self
      .sub_manager
      .add_port(&sub_id, &port_name)
      .map(|_|self.send_message(&sub_id,SerialResponse::Ok {
                msg: format!("Opening port {} succeeded", port_name).to_string(),
              }))
  }

  /// Handle list ports request
  fn handle_list_ports(&mut self, sub_id: &String) -> Result<()> {
    self.check_sub_id(&sub_id)?;
    let port_names: Result<Vec<String>> =
      self
        .port_manager
        .list_ports()
        .map(|v| v.iter().map(|v| v.port_name.clone()).collect());
    let resp = port_names.map(|pns| SerialResponse::List { ports: pns });
    self.send_message(&sub_id,
                      resp.unwrap_or(SerialResponse::Error {
                                       display: "".to_string(),
                                       description: "".to_string(),
                                     }));
    Ok(())
  }

  /// Handle close port requests
  fn handle_close_port(&mut self,
                       sub_id: &String,
                       port_name: Option<String>)
                       -> Result<()> {
    self.check_sub_id(&sub_id)?;
    // Unsubscribe from ports
    match port_name {
      None => self.sub_manager.clear_ports(Some(&sub_id)),
      Some(pn) => self.sub_manager.remove_port(&sub_id, &pn),
    }?;
    // Close ports with no subscribers
    let open_ports = self.port_manager.open_ports();
    let subscribed_ports = self.sub_manager.subscribed_ports();
    let ports_with_no_subs = open_ports.difference(&subscribed_ports);

    // For each open port that isn't subscribed,
    for port_to_close in ports_with_no_subs {
      // close it
      self.port_manager.close_port(&port_to_close);
      // remove the write lock
      self.writelock_manager.clear_lock(&port_to_close);
      // Let them know its closed
      let close_resp = SerialResponse::Closed { port: port_to_close.clone() };
      self.send_message(&sub_id, close_resp);
    }
    Ok(())
  }

  /// Cleanup any bad ports
  /// Clears out the passed in hash set
  fn cleanup_bad_ports(&mut self, bad_ports: &HashSet<String>) {
    for port_name in bad_ports.iter() {
      // Tell everyone port is sick
      let err_resp = SerialResponse::Error {
        display: format!("Error reading from port '{}', closing!", port_name),
        description: format!("An error occured while trying to read data from '{}'",
                             port_name),
      };
      self.broadcast_message_for_port(port_name, err_resp);
      // Tesll everyone the sick ports were closed
      let close_resp = SerialResponse::Closed { port: port_name.clone() };
      self.broadcast_message_for_port(port_name, close_resp);
      // Close bad ports
      self.port_manager.close_port(port_name);
      // Remove write locks on bad ports
      self.writelock_manager.clear_lock(port_name);
      // Remove bad ports from subscriptions
      self.sub_manager.remove_port_from_all(port_name);
    }
    // Clear set of bad_ports
  }

  pub fn send_message(&mut self, sub_id: &String, msg: SerialResponse) {
    match self.sub_manager.send_message(sub_id, msg) {
      Err(e) => {
        warn!("Error sending serial response to sub_id '{}'",sub_id);
        let mut bad_subs = Vec::new();
        bad_subs.push(e);
        self.cleanup_bad_subs(bad_subs);
      }
      _ => {}
    }
  }

  /// Broadcast a message to all subscribers and
  /// then cleanup any subs that errored
  pub fn broadcast_message(&mut self, msg: SerialResponse) {
    let bad_subs = self.sub_manager.broadcast_message(msg);
    self.cleanup_bad_subs(bad_subs);
  }

  /// Broadcast a message to all subscribers interested in the
  /// given port and then cleanup any subs that errored
  pub fn broadcast_message_for_port(&mut self, port_name: &String, msg: SerialResponse) {
    let bad_subs = self
      .sub_manager
      .broadcast_message_for_port(port_name, msg);
    self.cleanup_bad_subs(bad_subs);
  }

  /// Cleanup any bad subs where a message send failed
  fn cleanup_bad_subs(&mut self, bad_subs: Vec<Error>) {
    for e in bad_subs {
      match e {
        Error(ErrorKind::SubscriberSendError(sub_id), _) => {
          self.sub_manager.end_subscription(&sub_id);
          self.writelock_manager.unlock_all_ports_for_sub(&sub_id);
        }
        _ => { /*nop*/ }
      }
    }
  }

  fn check_sub_id(&self, sub_id: &String) -> Result<()> {
    self.sub_manager.check_subscription_exists(sub_id)
  }

  fn check_owns_writelock(&self, port_name: &String, sub_id: &String) -> Result<()> {
    self
      .writelock_manager
      .check_owns_write_lock(port_name, sub_id)
  }
}
<|endoftext|>


<commit_before>use game::bitboard::BitBoard;
use game::position::Position;
use game::position::MoveDirection;

#[allow(dead_code)]
const DEFAULT_FREE_SPACE_ROW: u8 = 3;
#[allow(dead_code)]
const DEFAULT_FREE_SPACE_COL: u8 = 3;

#[allow(dead_code)]
pub struct GameState {
  board: BitBoard,
  free_space: Position,
}

#[allow(dead_code)]
impl GameState {
  pub fn new() -> GameState {
    GameState {
      board: BitBoard::new(),
      free_space: Position::new(DEFAULT_FREE_SPACE_ROW, DEFAULT_FREE_SPACE_COL),
    }
  }

  pub fn is_solved(&self) -> bool {
    self.board.is_solved()
  }

  pub fn move_space(&self, move_direction: MoveDirection) -> Option<GameState> {
    match self.free_space.get_neighbor(move_direction) {
      Some(position) => {
        Some(GameState {
          board: self.board.swap(self.free_space, position),
          free_space: position,
        })
      }
      _ => None
    }
  }
}

#[cfg(test)]
mod tests {
  use super::*;
  use game::position::Position;
  use game::position::MoveDirection;

  #[test]
  fn new_state_is_solved() {
    let s: GameState = GameState::new();

    assert!(s.is_solved());
  }

  #[test]
  fn move_space_valid_direction() {
    let s: Option<GameState> = GameState::new()
      .move_space(MoveDirection::Left);

    match s {
      Some(state) => {
        if state.free_space != Position::new(3, 2) {
          panic!()
        }
      }
      _ => panic!(),
    }
  }
}
<commit_msg>test move space in illegal direction<commit_after>use game::bitboard::BitBoard;
use game::position::Position;
use game::position::MoveDirection;

#[allow(dead_code)]
const DEFAULT_FREE_SPACE_ROW: u8 = 3;
#[allow(dead_code)]
const DEFAULT_FREE_SPACE_COL: u8 = 3;

#[allow(dead_code)]
pub struct GameState {
  board: BitBoard,
  free_space: Position,
}

#[allow(dead_code)]
impl GameState {
  pub fn new() -> GameState {
    GameState {
      board: BitBoard::new(),
      free_space: Position::new(DEFAULT_FREE_SPACE_ROW, DEFAULT_FREE_SPACE_COL),
    }
  }

  pub fn is_solved(&self) -> bool {
    self.board.is_solved()
  }

  pub fn move_space(&self, move_direction: MoveDirection) -> Option<GameState> {
    match self.free_space.get_neighbor(move_direction) {
      Some(position) => {
        Some(GameState {
          board: self.board.swap(self.free_space, position),
          free_space: position,
        })
      }
      _ => None
    }
  }
}

#[cfg(test)]
mod tests {
  use super::*;
  use game::position::Position;
  use game::position::MoveDirection;

  #[test]
  fn new_state_is_solved() {
    let s: GameState = GameState::new();

    assert!(s.is_solved());
  }

  #[test]
  fn move_space_valid_direction() {
    let s: Option<GameState> = GameState::new()
      .move_space(MoveDirection::Left);

    match s {
      Some(state) => {
        if state.free_space != Position::new(3, 2) {
          panic!()
        }
      }
      _ => panic!(),
    }
  }

  #[test]
  fn move_space_illegal_direction() {
    let s: Option<GameState> = GameState::new()
      .move_space(MoveDirection::Down);

    match s {
      Some(_) => panic!(),
      None => (),
    }
  }
}
<|endoftext|>