<commit_before>use super::function;
use crate::{
    analysis::enums::Info,
    analysis::namespaces,
    codegen::general::{
        self, cfg_deprecated, derives, version_condition, version_condition_no_doc,
        version_condition_string,
    },
    config::gobjects::GObject,
    env::Env,
    file_saver,
    library::*,
    nameutil::{enum_member_name, use_glib_if_needed, use_glib_type},
    traits::*,
    version::Version,
};
use std::{
    collections::HashSet,
    io::{prelude::*, Result},
    path::Path,
};

pub fn generate(env: &Env, root_path: &Path, mod_rs: &mut Vec<String>) {
    fn should_keep(c: &GObject) -> bool {
        c.status.need_generate() && c.type_id.map_or(false, |tid| tid.ns_id == namespaces::MAIN)
    }

    let mut has_get_quark = false;
    let mut has_any = false;
    let mut has_get_type = false;
    let mut generate_display_trait = false;

    for enum_analysis in &env.analysis.enumerations {
        let config = &env.config.objects[&enum_analysis.full_name];
        if !should_keep(config) {
            continue;
        }

        let enum_ = enum_analysis.type_(&env.library);
        has_any = true;
        if enum_.error_domain.is_some() {
            has_get_quark = true;
        }
        if enum_.glib_get_type.is_some() {
            has_get_type = true;
        }
        generate_display_trait |= config.generate_display_trait;

        if has_get_type && has_get_quark {
            break;
        }
    }

    if !has_any {
        return;
    }

    // TODO: We should do this just once in analysis without mutation necessary afterwards
    let mut imports = env.analysis.enum_imports.clone();

    if has_get_quark {
        imports.add("glib::Quark");
        imports.add("glib::error::ErrorDomain");
    }
    if has_get_type {
        imports.add("glib::Type");
        imports.add("glib::StaticType");
        imports.add("glib::value::SetValue");
        imports.add("glib::value::FromValue");
        imports.add("glib::value::FromValueOptional");
    }
    imports.add("glib::translate::*");

    if generate_display_trait {
        imports.add("std::fmt");
    }

    let path = root_path.join("enums.rs");
    file_saver::save_to_file(path, env.config.make_backup, |w| {
        general::start_comments(w, &env.config)?;
        general::uses(w, env, &imports)?;
        writeln!(w)?;

        mod_rs.push("\nmod enums;".into());
        for enum_analysis in &env.analysis.enumerations {
            let config = &env.config.objects[&enum_analysis.full_name];
            if !should_keep(config) {
                continue;
            }
            let enum_ = enum_analysis.type_(&env.library);

            if let Some(cfg) = version_condition_string(env, enum_.version, false, 0) {
                mod_rs.push(cfg);
            }
            mod_rs.push(format!("pub use self::enums::{};", enum_.name));
            generate_enum(env, w, enum_, config, enum_analysis)?;
        }

        Ok(())
    });
}

#[allow(clippy::write_literal)]
fn generate_enum(
    env: &Env,
    w: &mut dyn Write,
    enum_: &Enumeration,
    config: &GObject,
    analysis: &Info,
) -> Result<()> {
    struct Member {
        name: String,
        c_name: String,
        value: String,
        version: Option<Version>,
        deprecated_version: Option<Version>,
    }

    let mut members: Vec<Member> = Vec::new();
    let mut vals: HashSet<String> = HashSet::new();
    let sys_crate_name = env.main_sys_crate_name();

    for member in &enum_.members {
        let member_config = config.members.matched(&member.name);
        let is_alias = member_config.iter().any(|m| m.alias);
        let generate = member_config.iter().all(|m| m.status.need_generate());
        if is_alias || !generate || vals.contains(&member.value) {
            continue;
        }
        vals.insert(member.value.clone());
        let deprecated_version = member_config
            .iter()
            .filter_map(|m| m.deprecated_version)
            .next();
        let version = member_config.iter().filter_map(|m| m.version).next();
        members.push(Member {
            name: enum_member_name(&member.name),
            c_name: member.c_identifier.clone(),
            value: member.value.clone(),
            version,
            deprecated_version,
        });
    }

    cfg_deprecated(w, env, enum_.deprecated_version, false, 0)?;
    version_condition(w, env, enum_.version, false, 0)?;
    if config.must_use {
        writeln!(w, "#[must_use]")?;
    }

    if let Some(ref d) = config.derives {
        derives(w, &d, 1)?;
    } else {
        writeln!(w, "#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]")?;
    }
    writeln!(w, "#[derive(Clone, Copy)]")?;
    writeln!(w, "#[non_exhaustive]")?;

    writeln!(w, "pub enum {} {{", enum_.name)?;
    for member in &members {
        cfg_deprecated(w, env, member.deprecated_version, false, 1)?;
        version_condition(w, env, member.version, false, 1)?;
        writeln!(w, "\t{},", member.name)?;
    }
    writeln!(
        w,
        "\
    #[doc(hidden)]
    __Unknown(i32),
}}
"
    )?;

    version_condition(w, env, enum_.version, false, 0)?;
    write!(w, "impl {} {{", analysis.name)?;
    for func_analysis in &analysis.functions() {
        function::generate(w, env, func_analysis, false, false, 1)?;
    }
    writeln!(w, "}}")?;
    writeln!(w)?;

    if config.generate_display_trait {
        // Generate Display trait implementation.
        version_condition(w, env, enum_.version, false, 0)?;
        writeln!(
            w,
            "impl fmt::Display for {0} {{\n\
             \tfn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {{\n\
             \t\twrite!(f, \"{0}::{{}}\", match *self {{",
            enum_.name
        )?;
        for member in &members {
            version_condition_no_doc(w, env, member.version, false, 3)?;
            writeln!(w, "\t\t\t{0}::{1} => \"{1}\",", enum_.name, member.name)?;
        }
        writeln!(
            w,
            "\t\t\t_ => \"Unknown\",\n\
             \t\t}})\n\
             \t}}\n\
             }}\n"
        )?;
    }

    // Generate ToGlib trait implementation.
    version_condition(w, env, enum_.version, false, 0)?;
    writeln!(
        w,
        "#[doc(hidden)]
impl ToGlib for {name} {{
    type GlibType = {sys_crate_name}::{ffi_name};

    fn to_glib(&self) -> {sys_crate_name}::{ffi_name} {{
        match *self {{",
        sys_crate_name = sys_crate_name,
        name = enum_.name,
        ffi_name = enum_.c_type
    )?;
    for member in &members {
        version_condition_no_doc(w, env, member.version, false, 3)?;
        writeln!(
            w,
            "\t\t\t{}::{} => {}::{},",
            enum_.name, member.name, sys_crate_name, member.c_name
        )?;
    }
    writeln!(w, "\t\t\t{}::__Unknown(value) => value,", enum_.name)?;
    writeln!(
        w,
        "\
        }}
    }}
}}
"
    )?;

    let assert = if env.config.generate_safety_asserts {
        "skip_assert_initialized!();\n\t\t"
    } else {
        ""
    };

    // Generate FromGlib trait implementation.
    version_condition(w, env, enum_.version, false, 0)?;
    writeln!(
        w,
        "#[doc(hidden)]
impl FromGlib<{sys_crate_name}::{ffi_name}> for {name} {{
    fn from_glib(value: {sys_crate_name}::{ffi_name}) -> Self {{
        {assert}match value {{",
        sys_crate_name = sys_crate_name,
        name = enum_.name,
        ffi_name = enum_.c_type,
        assert = assert
    )?;
    for member in &members {
        version_condition_no_doc(w, env, member.version, false, 3)?;
        writeln!(
            w,
            "\t\t\t{} => {}::{},",
            member.value, enum_.name, member.name
        )?;
    }
    writeln!(w, "\t\t\tvalue => {}::__Unknown(value),", enum_.name)?;
    writeln!(
        w,
        "\
        }}
    }}
}}
"
    )?;

    // Generate ErrorDomain trait implementation.
    if let Some(ref domain) = enum_.error_domain {
        let has_failed_member = members.iter().any(|m| m.name == "Failed");

        version_condition(w, env, enum_.version, false, 0)?;
        writeln!(
            w,
            "impl ErrorDomain for {name} {{
    fn domain() -> Quark {{
        {assert}",
            name = enum_.name,
            assert = assert
        )?;

        match domain {
            ErrorDomain::Quark(ref quark) => {
                writeln!(
                    w,
                    "        static QUARK: once_cell::sync::Lazy<{0}ffi::GQuark> = once_cell::sync::Lazy::new(|| unsafe {{
            {0}ffi::g_quark_from_static_string(b\"{1}\\0\".as_ptr() as *const _)
        }});
        from_glib(*QUARK)",
                    use_glib_if_needed(env, ""),
                    quark,
                )?;
            }
            ErrorDomain::Function(ref f) => {
                writeln!(
                    w,
                    "        unsafe {{ from_glib({sys_crate_name}::{get_quark}()) }}",
                    sys_crate_name = sys_crate_name,
                    get_quark = f
                )?;
            }
        }

        writeln!(
            w,
            "    }}

    fn code(self) -> i32 {{
        self.to_glib()
    }}

    fn from(code: i32) -> Option<Self> {{
        {assert}match code {{",
            assert = assert
        )?;

        for member in &members {
            version_condition_no_doc(w, env, member.version, false, 3)?;
            writeln!(
                w,
                "\t\t\t{} => Some({}::{}),",
                member.value, enum_.name, member.name
            )?;
        }
        if has_failed_member {
            writeln!(w, "\t\t\t_ => Some({}::Failed),", enum_.name)?;
        } else {
            writeln!(w, "\t\t\tvalue => Some({}::__Unknown(value)),", enum_.name)?;
        }

        writeln!(
            w,
            "\
        }}
    }}
}}
"
        )?;
    }

    // Generate StaticType trait implementation.
    if let Some(ref get_type) = enum_.glib_get_type {
        let configured_functions = config.functions.matched("get_type");
        let version = std::iter::once(enum_.version)
            .chain(configured_functions.iter().map(|f| f.version))
            .max()
            .flatten();

        version_condition(w, env, version, false, 0)?;
        writeln!(
            w,
            "impl StaticType for {name} {{
    fn static_type() -> Type {{
        unsafe {{ from_glib({sys_crate_name}::{get_type}()) }}
    }}
}}",
            sys_crate_name = sys_crate_name,
            name = enum_.name,
            get_type = get_type
        )?;
        writeln!(w)?;

        version_condition(w, env, version, false, 0)?;
        writeln!(
            w,
            "impl<'a> FromValueOptional<'a> for {name} {{
    unsafe fn from_value_optional(value: &{gvalue}) -> Option<Self> {{
        Some(FromValue::from_value(value))
    }}
}}",
            name = enum_.name,
            gvalue = use_glib_type(env, "Value"),
        )?;
        writeln!(w)?;

        version_condition(w, env, version, false, 0)?;
        writeln!(
            w,
            "impl<'a> FromValue<'a> for {name} {{
    unsafe fn from_value(value: &{gvalue}) -> Self {{
        from_glib({glib}(value.to_glib_none().0))
    }}
}}",
            name = enum_.name,
            glib = use_glib_type(env, "gobject_ffi::g_value_get_enum"),
            gvalue = use_glib_type(env, "Value"),
        )?;
        writeln!(w)?;

        version_condition(w, env, version, false, 0)?;
        writeln!(
            w,
            "impl SetValue for {name} {{
    unsafe fn set_value(value: &mut {gvalue}, this: &Self) {{
        {glib}(value.to_glib_none_mut().0, this.to_glib())
    }}
}}",
            name = enum_.name,
            glib = use_glib_type(env, "gobject_ffi::g_value_set_enum"),
            gvalue = use_glib_type(env, "Value"),
        )?;
        writeln!(w)?;
    }

    Ok(())
}
<commit_msg>Only generate enum functions if >=1 function<commit_after>use super::function;
use crate::{
    analysis::enums::Info,
    analysis::namespaces,
    codegen::general::{
        self, cfg_deprecated, derives, version_condition, version_condition_no_doc,
        version_condition_string,
    },
    config::gobjects::GObject,
    env::Env,
    file_saver,
    library::*,
    nameutil::{enum_member_name, use_glib_if_needed, use_glib_type},
    traits::*,
    version::Version,
};
use std::{
    collections::HashSet,
    io::{prelude::*, Result},
    path::Path,
};

pub fn generate(env: &Env, root_path: &Path, mod_rs: &mut Vec<String>) {
    fn should_keep(c: &GObject) -> bool {
        c.status.need_generate() && c.type_id.map_or(false, |tid| tid.ns_id == namespaces::MAIN)
    }

    let mut has_get_quark = false;
    let mut has_any = false;
    let mut has_get_type = false;
    let mut generate_display_trait = false;

    for enum_analysis in &env.analysis.enumerations {
        let config = &env.config.objects[&enum_analysis.full_name];
        if !should_keep(config) {
            continue;
        }

        let enum_ = enum_analysis.type_(&env.library);
        has_any = true;
        if enum_.error_domain.is_some() {
            has_get_quark = true;
        }
        if enum_.glib_get_type.is_some() {
            has_get_type = true;
        }
        generate_display_trait |= config.generate_display_trait;

        if has_get_type && has_get_quark {
            break;
        }
    }

    if !has_any {
        return;
    }

    // TODO: We should do this just once in analysis without mutation necessary afterwards
    let mut imports = env.analysis.enum_imports.clone();

    if has_get_quark {
        imports.add("glib::Quark");
        imports.add("glib::error::ErrorDomain");
    }
    if has_get_type {
        imports.add("glib::Type");
        imports.add("glib::StaticType");
        imports.add("glib::value::SetValue");
        imports.add("glib::value::FromValue");
        imports.add("glib::value::FromValueOptional");
    }
    imports.add("glib::translate::*");

    if generate_display_trait {
        imports.add("std::fmt");
    }

    let path = root_path.join("enums.rs");
    file_saver::save_to_file(path, env.config.make_backup, |w| {
        general::start_comments(w, &env.config)?;
        general::uses(w, env, &imports)?;
        writeln!(w)?;

        mod_rs.push("\nmod enums;".into());
        for enum_analysis in &env.analysis.enumerations {
            let config = &env.config.objects[&enum_analysis.full_name];
            if !should_keep(config) {
                continue;
            }
            let enum_ = enum_analysis.type_(&env.library);

            if let Some(cfg) = version_condition_string(env, enum_.version, false, 0) {
                mod_rs.push(cfg);
            }
            mod_rs.push(format!("pub use self::enums::{};", enum_.name));
            generate_enum(env, w, enum_, config, enum_analysis)?;
        }

        Ok(())
    });
}

#[allow(clippy::write_literal)]
fn generate_enum(
    env: &Env,
    w: &mut dyn Write,
    enum_: &Enumeration,
    config: &GObject,
    analysis: &Info,
) -> Result<()> {
    struct Member {
        name: String,
        c_name: String,
        value: String,
        version: Option<Version>,
        deprecated_version: Option<Version>,
    }

    let mut members: Vec<Member> = Vec::new();
    let mut vals: HashSet<String> = HashSet::new();
    let sys_crate_name = env.main_sys_crate_name();

    for member in &enum_.members {
        let member_config = config.members.matched(&member.name);
        let is_alias = member_config.iter().any(|m| m.alias);
        let generate = member_config.iter().all(|m| m.status.need_generate());
        if is_alias || !generate || vals.contains(&member.value) {
            continue;
        }
        vals.insert(member.value.clone());
        let deprecated_version = member_config
            .iter()
            .filter_map(|m| m.deprecated_version)
            .next();
        let version = member_config.iter().filter_map(|m| m.version).next();
        members.push(Member {
            name: enum_member_name(&member.name),
            c_name: member.c_identifier.clone(),
            value: member.value.clone(),
            version,
            deprecated_version,
        });
    }

    cfg_deprecated(w, env, enum_.deprecated_version, false, 0)?;
    version_condition(w, env, enum_.version, false, 0)?;
    if config.must_use {
        writeln!(w, "#[must_use]")?;
    }

    if let Some(ref d) = config.derives {
        derives(w, &d, 1)?;
    } else {
        writeln!(w, "#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]")?;
    }
    writeln!(w, "#[derive(Clone, Copy)]")?;
    writeln!(w, "#[non_exhaustive]")?;

    writeln!(w, "pub enum {} {{", enum_.name)?;
    for member in &members {
        cfg_deprecated(w, env, member.deprecated_version, false, 1)?;
        version_condition(w, env, member.version, false, 1)?;
        writeln!(w, "\t{},", member.name)?;
    }
    writeln!(
        w,
        "\
    #[doc(hidden)]
    __Unknown(i32),
}}"
    )?;

    let functions = analysis.functions();

    if !functions.is_empty() {
        writeln!(w)?;
        version_condition(w, env, enum_.version, false, 0)?;
        write!(w, "impl {} {{", analysis.name)?;
        for func_analysis in functions {
            function::generate(w, env, func_analysis, false, false, 1)?;
        }
        writeln!(w, "}}")?;
    }

    writeln!(w)?;

    if config.generate_display_trait {
        // Generate Display trait implementation.
        version_condition(w, env, enum_.version, false, 0)?;
        writeln!(
            w,
            "impl fmt::Display for {0} {{\n\
             \tfn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {{\n\
             \t\twrite!(f, \"{0}::{{}}\", match *self {{",
            enum_.name
        )?;
        for member in &members {
            version_condition_no_doc(w, env, member.version, false, 3)?;
            writeln!(w, "\t\t\t{0}::{1} => \"{1}\",", enum_.name, member.name)?;
        }
        writeln!(
            w,
            "\t\t\t_ => \"Unknown\",\n\
             \t\t}})\n\
             \t}}\n\
             }}\n"
        )?;
    }

    // Generate ToGlib trait implementation.
    version_condition(w, env, enum_.version, false, 0)?;
    writeln!(
        w,
        "#[doc(hidden)]
impl ToGlib for {name} {{
    type GlibType = {sys_crate_name}::{ffi_name};

    fn to_glib(&self) -> {sys_crate_name}::{ffi_name} {{
        match *self {{",
        sys_crate_name = sys_crate_name,
        name = enum_.name,
        ffi_name = enum_.c_type
    )?;
    for member in &members {
        version_condition_no_doc(w, env, member.version, false, 3)?;
        writeln!(
            w,
            "\t\t\t{}::{} => {}::{},",
            enum_.name, member.name, sys_crate_name, member.c_name
        )?;
    }
    writeln!(w, "\t\t\t{}::__Unknown(value) => value,", enum_.name)?;
    writeln!(
        w,
        "\
        }}
    }}
}}
"
    )?;

    let assert = if env.config.generate_safety_asserts {
        "skip_assert_initialized!();\n\t\t"
    } else {
        ""
    };

    // Generate FromGlib trait implementation.
    version_condition(w, env, enum_.version, false, 0)?;
    writeln!(
        w,
        "#[doc(hidden)]
impl FromGlib<{sys_crate_name}::{ffi_name}> for {name} {{
    fn from_glib(value: {sys_crate_name}::{ffi_name}) -> Self {{
        {assert}match value {{",
        sys_crate_name = sys_crate_name,
        name = enum_.name,
        ffi_name = enum_.c_type,
        assert = assert
    )?;
    for member in &members {
        version_condition_no_doc(w, env, member.version, false, 3)?;
        writeln!(
            w,
            "\t\t\t{} => {}::{},",
            member.value, enum_.name, member.name
        )?;
    }
    writeln!(w, "\t\t\tvalue => {}::__Unknown(value),", enum_.name)?;
    writeln!(
        w,
        "\
        }}
    }}
}}
"
    )?;

    // Generate ErrorDomain trait implementation.
    if let Some(ref domain) = enum_.error_domain {
        let has_failed_member = members.iter().any(|m| m.name == "Failed");

        version_condition(w, env, enum_.version, false, 0)?;
        writeln!(
            w,
            "impl ErrorDomain for {name} {{
    fn domain() -> Quark {{
        {assert}",
            name = enum_.name,
            assert = assert
        )?;

        match domain {
            ErrorDomain::Quark(ref quark) => {
                writeln!(
                    w,
                    "        static QUARK: once_cell::sync::Lazy<{0}ffi::GQuark> = once_cell::sync::Lazy::new(|| unsafe {{
            {0}ffi::g_quark_from_static_string(b\"{1}\\0\".as_ptr() as *const _)
        }});
        from_glib(*QUARK)",
                    use_glib_if_needed(env, ""),
                    quark,
                )?;
            }
            ErrorDomain::Function(ref f) => {
                writeln!(
                    w,
                    "        unsafe {{ from_glib({sys_crate_name}::{get_quark}()) }}",
                    sys_crate_name = sys_crate_name,
                    get_quark = f
                )?;
            }
        }

        writeln!(
            w,
            "    }}

    fn code(self) -> i32 {{
        self.to_glib()
    }}

    fn from(code: i32) -> Option<Self> {{
        {assert}match code {{",
            assert = assert
        )?;

        for member in &members {
            version_condition_no_doc(w, env, member.version, false, 3)?;
            writeln!(
                w,
                "\t\t\t{} => Some({}::{}),",
                member.value, enum_.name, member.name
            )?;
        }
        if has_failed_member {
            writeln!(w, "\t\t\t_ => Some({}::Failed),", enum_.name)?;
        } else {
            writeln!(w, "\t\t\tvalue => Some({}::__Unknown(value)),", enum_.name)?;
        }

        writeln!(
            w,
            "\
        }}
    }}
}}
"
        )?;
    }

    // Generate StaticType trait implementation.
    if let Some(ref get_type) = enum_.glib_get_type {
        let configured_functions = config.functions.matched("get_type");
        let version = std::iter::once(enum_.version)
            .chain(configured_functions.iter().map(|f| f.version))
            .max()
            .flatten();

        version_condition(w, env, version, false, 0)?;
        writeln!(
            w,
            "impl StaticType for {name} {{
    fn static_type() -> Type {{
        unsafe {{ from_glib({sys_crate_name}::{get_type}()) }}
    }}
}}",
            sys_crate_name = sys_crate_name,
            name = enum_.name,
            get_type = get_type
        )?;
        writeln!(w)?;

        version_condition(w, env, version, false, 0)?;
        writeln!(
            w,
            "impl<'a> FromValueOptional<'a> for {name} {{
    unsafe fn from_value_optional(value: &{gvalue}) -> Option<Self> {{
        Some(FromValue::from_value(value))
    }}
}}",
            name = enum_.name,
            gvalue = use_glib_type(env, "Value"),
        )?;
        writeln!(w)?;

        version_condition(w, env, version, false, 0)?;
        writeln!(
            w,
            "impl<'a> FromValue<'a> for {name} {{
    unsafe fn from_value(value: &{gvalue}) -> Self {{
        from_glib({glib}(value.to_glib_none().0))
    }}
}}",
            name = enum_.name,
            glib = use_glib_type(env, "gobject_ffi::g_value_get_enum"),
            gvalue = use_glib_type(env, "Value"),
        )?;
        writeln!(w)?;

        version_condition(w, env, version, false, 0)?;
        writeln!(
            w,
            "impl SetValue for {name} {{
    unsafe fn set_value(value: &mut {gvalue}, this: &Self) {{
        {glib}(value.to_glib_none_mut().0, this.to_glib())
    }}
}}",
            name = enum_.name,
            glib = use_glib_type(env, "gobject_ffi::g_value_set_enum"),
            gvalue = use_glib_type(env, "Value"),
        )?;
        writeln!(w)?;
    }

    Ok(())
}
<|endoftext|>


<commit_before>//! lint on `use`ing all variants of an enum

use rustc::front::map::Node::NodeItem;
use rustc::front::map::definitions::DefPathData;
use rustc::lint::{LateLintPass, LintPass, LateContext, LintArray, LintContext};
use rustc::middle::ty::TyEnum;
use rustc_front::hir::*;
use syntax::ast::NodeId;
use syntax::codemap::Span;
use utils::span_lint;

/// **What it does:** Warns when `use`ing all variants of an enum
///
/// **Why is this bad?** It is usually better style to use the prefixed name of an enum variant, rather than importing variants
///
/// **Known problems:** Old-style enums that prefix the variants are still around
///
/// **Example:** `use std::cmp::Ordering::*;`
declare_lint! { pub ENUM_GLOB_USE, Allow,
    "finds use items that import all variants of an enum" }

pub struct EnumGlobUse;

impl LintPass for EnumGlobUse {
    fn get_lints(&self) -> LintArray {
        lint_array!(ENUM_GLOB_USE)
    }
}

impl LateLintPass for EnumGlobUse {
    fn check_mod(&mut self, cx: &LateContext, m: &Mod, _: Span, _: NodeId) {
        // only check top level `use` statements
        for item in &m.item_ids {
            self.lint_item(cx, cx.krate.item(item.id));
        }
    }
}

impl EnumGlobUse {
    fn lint_item(&self, cx: &LateContext, item: &Item) {
        if item.vis == Visibility::Public {
            return; // re-exports are fine
        }
        if let ItemUse(ref item_use) = item.node {
            if let ViewPath_::ViewPathGlob(_) = item_use.node {
                let def = cx.tcx.def_map.borrow()[&item.id];
                if let Some(node_id) = cx.tcx.map.as_local_node_id(def.def_id()) {
                    if let Some(NodeItem(it)) = cx.tcx.map.find(node_id) {
                        if let ItemEnum(..) = it.node {
                            span_lint(cx, ENUM_GLOB_USE, item.span, "don't use glob imports for enum variants");
                        }
                    }
                } else {
                    if let Some(dp) = cx.sess().cstore.def_path(def.def_id()).last() {
                        if let DefPathData::Type(_) = dp.data {
                            if let TyEnum(..) = cx.sess().cstore.item_type(&cx.tcx, def.def_id()).ty.sty {
                                span_lint(cx, ENUM_GLOB_USE, item.span, "don't use glob imports for enum variants");
                            }
                        }
                    }
                }
            }
        }
    }
}
<commit_msg>Fix ICE with unknown defids<commit_after>//! lint on `use`ing all variants of an enum

use rustc::front::map::Node::NodeItem;
use rustc::front::map::definitions::DefPathData;
use rustc::lint::{LateLintPass, LintPass, LateContext, LintArray, LintContext};
use rustc::middle::ty::TyEnum;
use rustc_front::hir::*;
use syntax::ast::NodeId;
use syntax::codemap::Span;
use utils::span_lint;

/// **What it does:** Warns when `use`ing all variants of an enum
///
/// **Why is this bad?** It is usually better style to use the prefixed name of an enum variant, rather than importing variants
///
/// **Known problems:** Old-style enums that prefix the variants are still around
///
/// **Example:** `use std::cmp::Ordering::*;`
declare_lint! { pub ENUM_GLOB_USE, Allow,
    "finds use items that import all variants of an enum" }

pub struct EnumGlobUse;

impl LintPass for EnumGlobUse {
    fn get_lints(&self) -> LintArray {
        lint_array!(ENUM_GLOB_USE)
    }
}

impl LateLintPass for EnumGlobUse {
    fn check_mod(&mut self, cx: &LateContext, m: &Mod, _: Span, _: NodeId) {
        // only check top level `use` statements
        for item in &m.item_ids {
            self.lint_item(cx, cx.krate.item(item.id));
        }
    }
}

impl EnumGlobUse {
    fn lint_item(&self, cx: &LateContext, item: &Item) {
        if item.vis == Visibility::Public {
            return; // re-exports are fine
        }
        if let ItemUse(ref item_use) = item.node {
            if let ViewPath_::ViewPathGlob(_) = item_use.node {
                if let Some(def) = cx.tcx.def_map.borrow().get(&item.id) {
                    if let Some(node_id) = cx.tcx.map.as_local_node_id(def.def_id()) {
                        if let Some(NodeItem(it)) = cx.tcx.map.find(node_id) {
                            if let ItemEnum(..) = it.node {
                                span_lint(cx, ENUM_GLOB_USE, item.span, "don't use glob imports for enum variants");
                            }
                        }
                    } else {
                        if let Some(dp) = cx.sess().cstore.def_path(def.def_id()).last() {
                            if let DefPathData::Type(_) = dp.data {
                                if let TyEnum(..) = cx.sess().cstore.item_type(&cx.tcx, def.def_id()).ty.sty {
                                    span_lint(cx, ENUM_GLOB_USE, item.span, "don't use glob imports for enum variants");
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (C) 2020 Jason Ish
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

//! Eve processor. The combination of:
//! - reader: to read those eve log files
//! - importer: to send those events somewhere
//! - bookmarker: to remember the last location reader

use crate::bookmark;
use crate::eve::filters::EveFilter;
use crate::eve::reader::EveReader;
use crate::importer::Importer;
use crate::logger::log;
use std::path::PathBuf;
use std::sync::{Arc, Mutex};
use std::time::Duration;

const DEFAULT_BATCH_SIZE: usize = 300;

pub struct Processor {
    pub reader: EveReader,
    pub importer: Importer,
    pub filters: Arc<Mutex<Vec<EveFilter>>>,
    pub bookmark_filename: Option<PathBuf>,
    pub report_interval: Duration,

    /// In the absence of a valid bookmark, should the reader start at the end of the file, or
    /// the beginning.
    pub end: bool,

    /// If in oneshot mode, will exit on EOF.
    pub oneshot: bool,

    pub batch_size: usize,
}

impl Processor {
    pub fn new(reader: EveReader, importer: Importer) -> Self {
        Self {
            reader: reader,
            importer: importer,
            filters: Arc::new(Mutex::new(Vec::new())),
            bookmark_filename: None,
            report_interval: Duration::from_secs(0),
            end: false,
            oneshot: false,
            batch_size: DEFAULT_BATCH_SIZE,
        }
    }

    /// Initialize the reader from a bookmark. Returns false if unable to initalize
    /// from the bookmark (invalid bookmark, file does not exist...).
    fn init_from_bookmark(&mut self) -> bool {
        if let Some(bookmark_filename) = &self.bookmark_filename {
            match bookmark::Bookmark::from_file(&bookmark_filename) {
                Err(err) => {
                    log::warn!("Fail to load bookmark: {}", err);
                    return false;
                }
                Ok(bookmark) => {
                    if let Err(err) = bookmark.is_valid() {
                        log::info!("Invalid bookmark found: {}", err);
                        return false;
                    } else {
                        log::info!(
                            "Valid bookmark found, jumping to record: {}",
                            bookmark.offset
                        );
                        if let Err(err) = self.reader.goto_lineno(bookmark.offset) {
                            log::warn!("Failed to skip to line {}, error={}", bookmark.offset, err);
                            return false;
                        }
                        return true;
                    }
                }
            }
        } else {
            return false;
        }
    }

    pub async fn run(&mut self) {
        if !self.init_from_bookmark() {
            if self.end {
                match self.reader.goto_end() {
                    Ok(n) => {
                        log::info!("Skipped {} lines jumping to end of file", n);
                    }
                    Err(err) => {
                        log::error!("Failed to skip to end of file: {}", err);
                    }
                }
            }
        }
        let mut commits = 0;
        let mut count = 0;
        let mut eofs = 0;
        let mut last_report = std::time::Instant::now();
        loop {
            if self.report_interval > Duration::from_secs(0)
                && last_report.elapsed() > self.report_interval
            {
                log::debug!("count={}, commits={}, eofs={}", count, commits, eofs);
                count = 0;
                commits = 0;
                eofs = 0;
                last_report = std::time::Instant::now();
            }
            match self.reader.next_record() {
                Err(err) => {
                    log::error!("Failed to read event: {}", err);
                    self.sleep_for(1000).await;
                }
                Ok(None) => {
                    eofs += 1;
                    if self.importer.pending() > 0 {
                        self.commit().await;
                        commits += 1;
                    } else if !self.oneshot && self.reader.is_file_changed() {
                        log::info!(
                            "File may have been rotated, will reopen: filename={:?}",
                            self.reader.filename
                        );
                        if let Err(err) = self.reader.reopen() {
                            log::error!(
                                "Failed to reopen {:?}, error={}",
                                self.reader.filename,
                                err
                            );
                        }
                    }

                    if self.oneshot {
                        break;
                    }

                    // On EOF, always sleep for a second...
                    self.sleep_for(1000).await;
                }
                Ok(Some(mut event)) => {
                    if let Ok(filters) = self.filters.lock() {
                        for filter in &(*filters)[..] {
                            filter.run(&mut event);
                        }
                    }
                    count += 1;
                    self.importer.submit(event).await.unwrap();
                    if self.importer.pending() >= 100 {
                        self.commit().await;
                        commits += 1;
                    }
                }
            }
        }
        log::info!("count={}, commits={}, eofs={}", count, commits, eofs);
    }

    async fn sleep_for(&self, millis: u64) {
        let d = std::time::Duration::from_millis(millis);
        tokio::time::delay_for(d).await;
    }

    async fn commit(&mut self) {
        loop {
            match self.importer.commit().await {
                Ok(_n) => {
                    self.write_bookmark();
                    break;
                }
                Err(err) => {
                    log::error!("Failed to commit events (will try again): {}", err);
                    self.sleep_for(1000).await;
                }
            }
        }
    }

    fn write_bookmark(&mut self) {
        if let Some(bookmark_filename) = &self.bookmark_filename {
            if let Some(meta) = self.reader.metadata() {
                let bookmark = bookmark::Bookmark::from_metadata(&meta);
                if let Err(err) = bookmark.write(&bookmark_filename) {
                    log::error!(
                        "Failed to write bookmark: filename={}, err={}",
                        bookmark_filename.display(),
                        err
                    );
                }
            }
        }
    }
}
<commit_msg>per minute commit logging: include filename<commit_after>// Copyright (C) 2020 Jason Ish
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

//! Eve processor. The combination of:
//! - reader: to read those eve log files
//! - importer: to send those events somewhere
//! - bookmarker: to remember the last location reader

use crate::bookmark;
use crate::eve::filters::EveFilter;
use crate::eve::reader::EveReader;
use crate::importer::Importer;
use crate::logger::log;
use std::path::PathBuf;
use std::sync::{Arc, Mutex};
use std::time::Duration;

const DEFAULT_BATCH_SIZE: usize = 300;

pub struct Processor {
    pub reader: EveReader,
    pub importer: Importer,
    pub filters: Arc<Mutex<Vec<EveFilter>>>,
    pub bookmark_filename: Option<PathBuf>,
    pub report_interval: Duration,

    /// In the absence of a valid bookmark, should the reader start at the end of the file, or
    /// the beginning.
    pub end: bool,

    /// If in oneshot mode, will exit on EOF.
    pub oneshot: bool,

    pub batch_size: usize,
}

impl Processor {
    pub fn new(reader: EveReader, importer: Importer) -> Self {
        Self {
            reader: reader,
            importer: importer,
            filters: Arc::new(Mutex::new(Vec::new())),
            bookmark_filename: None,
            report_interval: Duration::from_secs(0),
            end: false,
            oneshot: false,
            batch_size: DEFAULT_BATCH_SIZE,
        }
    }

    /// Initialize the reader from a bookmark. Returns false if unable to initalize
    /// from the bookmark (invalid bookmark, file does not exist...).
    fn init_from_bookmark(&mut self) -> bool {
        if let Some(bookmark_filename) = &self.bookmark_filename {
            match bookmark::Bookmark::from_file(&bookmark_filename) {
                Err(err) => {
                    log::warn!("Fail to load bookmark: {}", err);
                    return false;
                }
                Ok(bookmark) => {
                    if let Err(err) = bookmark.is_valid() {
                        log::info!("Invalid bookmark found: {}", err);
                        return false;
                    } else {
                        log::info!(
                            "Valid bookmark found, jumping to record: {}",
                            bookmark.offset
                        );
                        if let Err(err) = self.reader.goto_lineno(bookmark.offset) {
                            log::warn!("Failed to skip to line {}, error={}", bookmark.offset, err);
                            return false;
                        }
                        return true;
                    }
                }
            }
        } else {
            return false;
        }
    }

    pub async fn run(&mut self) {
        if !self.init_from_bookmark() {
            if self.end {
                match self.reader.goto_end() {
                    Ok(n) => {
                        log::info!("Skipped {} lines jumping to end of file", n);
                    }
                    Err(err) => {
                        log::error!("Failed to skip to end of file: {}", err);
                    }
                }
            }
        }
        let mut commits = 0;
        let mut count = 0;
        let mut eofs = 0;
        let mut last_report = std::time::Instant::now();
        loop {
            if self.report_interval > Duration::from_secs(0)
                && last_report.elapsed() > self.report_interval
            {
                log::debug!(filename = ?self.reader.filename, "count={}, commits={}, eofs={}", count, commits, eofs);
                count = 0;
                commits = 0;
                eofs = 0;
                last_report = std::time::Instant::now();
            }
            match self.reader.next_record() {
                Err(err) => {
                    log::error!("Failed to read event: {}", err);
                    self.sleep_for(1000).await;
                }
                Ok(None) => {
                    eofs += 1;
                    if self.importer.pending() > 0 {
                        self.commit().await;
                        commits += 1;
                    } else if !self.oneshot && self.reader.is_file_changed() {
                        log::info!(
                            "File may have been rotated, will reopen: filename={:?}",
                            self.reader.filename
                        );
                        if let Err(err) = self.reader.reopen() {
                            log::error!(
                                "Failed to reopen {:?}, error={}",
                                self.reader.filename,
                                err
                            );
                        }
                    }

                    if self.oneshot {
                        break;
                    }

                    // On EOF, always sleep for a second...
                    self.sleep_for(1000).await;
                }
                Ok(Some(mut event)) => {
                    if let Ok(filters) = self.filters.lock() {
                        for filter in &(*filters)[..] {
                            filter.run(&mut event);
                        }
                    }
                    count += 1;
                    self.importer.submit(event).await.unwrap();
                    if self.importer.pending() >= 100 {
                        self.commit().await;
                        commits += 1;
                    }
                }
            }
        }
        log::info!(filename = ?self.reader.filename, "count={}, commits={}, eofs={}", count, commits, eofs);
    }

    async fn sleep_for(&self, millis: u64) {
        let d = std::time::Duration::from_millis(millis);
        tokio::time::delay_for(d).await;
    }

    async fn commit(&mut self) {
        loop {
            match self.importer.commit().await {
                Ok(_n) => {
                    self.write_bookmark();
                    break;
                }
                Err(err) => {
                    log::error!("Failed to commit events (will try again): {}", err);
                    self.sleep_for(1000).await;
                }
            }
        }
    }

    fn write_bookmark(&mut self) {
        if let Some(bookmark_filename) = &self.bookmark_filename {
            if let Some(meta) = self.reader.metadata() {
                let bookmark = bookmark::Bookmark::from_metadata(&meta);
                if let Err(err) = bookmark.write(&bookmark_filename) {
                    log::error!(
                        "Failed to write bookmark: filename={}, err={}",
                        bookmark_filename.display(),
                        err
                    );
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::collections::BTreeMap;
use std::io;

use time;

use clog::Clog;
use git::Commit;
use error::Error;
use fmt::{FormatWriter, WriterResult};
use sectionmap::SectionMap;

/// Wraps a `std::io::Write` object to write `clog` output in a Markdown format
///
/// # Example
///
/// ```no_run
/// # use std::fs::File;
/// # use clog::{SectionMap, Clog};
/// # use clog::fmt::MarkdownWriter;
/// let clog = Clog::new().unwrap_or_else(|e| {
///     e.exit();
/// });
///
/// // Get the commits we're interested in...
/// let sm = SectionMap::from_commits(clog.get_commits());
///
/// // Create a file to hold our results, which the MardownWriter will wrap (note, .unwrap() is only
/// // used to keep the example short and concise)
/// let mut file = File::create("my_changelog.md").ok().unwrap();
///
/// // Create the MarkdownWriter
/// let mut writer = MarkdownWriter::new(&mut file);
///
/// // Use the MarkdownWriter to write the changelog
/// clog.write_changelog_with(&mut writer).unwrap_or_else(|e| {
///     e.exit();
/// });
/// ```
pub struct MarkdownWriter<'a>(&'a mut io::Write);

impl<'a> MarkdownWriter<'a> {
    /// Creates a new instance of the `MarkdownWriter` struct using a `std::io::Write` object.
    ///
    /// # Example
    ///
    /// ```no_run
    /// # use std::io::{stdout, BufWriter};
    /// # use clog::Clog;
    /// # use clog::fmt::MarkdownWriter;
    /// let clog = Clog::new().unwrap_or_else(|e| {
    ///     e.exit();
    /// });
    ///
    /// // Create a MarkdownWriter to wrap stdout
    /// let out = stdout();
    /// let mut out_buf = BufWriter::new(out.lock());
    /// let mut writer = MarkdownWriter::new(&mut out_buf);
    /// ```
    pub fn new<T: io::Write + 'a>(writer: &'a mut T) -> MarkdownWriter<'a> {
        MarkdownWriter(writer)
    }

    fn write_header(&mut self, options: &Clog) -> io::Result<()> {
        let subtitle = match options.subtitle.len() {
            0 => options.subtitle.to_owned(),
            _ => format!(" {}", options.subtitle)
        };

        let version_text = if options.patch_ver {
            format!("### {}{}", options.version, subtitle)
        } else {
            format!("## {}{}", options.version, subtitle)
        };

        let date = time::now_utc();

        match date.strftime("%Y-%m-%d") {
            Ok(date) => {
                write!(
                    self.0,
                    "\n{} ({})\n\n",
                    options.version, version_text, date
                )
            },
            Err(_)   => {
                write!(
                    self.0,
                    "\n{} ({})\n\n",
                    options.version, version_text, "XXXX-XX-XX"
                )
            }
        }
    }

    /// Writes a particular section of a changelog
    fn write_section(&mut self, options: &Clog, title: &str, section: &BTreeMap<&String, &Vec<Commit>>)
                            -> WriterResult {
        if section.len() == 0 { return Ok(()) }

        if let Err(..) = self.0.write(&format!("\n#### {}\n\n", title)[..].as_bytes()) {
            return Err(Error::WriteErr);
        }

        for (component, entries) in section.iter() {
            let nested = (entries.len() > 1) && !component.is_empty();

            let prefix = if nested {
                if let Err(..) = write!(self.0 , "* **{}**\n", component) {
                    return Err(Error::WriteErr);
                }
                "  *".to_owned()
            } else if !component.is_empty() {
                format!("* **{}**", component)
            } else {
                format!("* ")
            };

            for entry in entries.iter() {
                if let Err(..) = write!(
                                    self.0 , "{} {} ([{}]({})",
                                    prefix,
                                    entry.subject,
                                    &entry.hash[0..8],
                                    options.link_style
                                           .commit_link(&*entry.hash, &options.repo[..])
                                ) {
                    return Err(Error::WriteErr);
                }

                if entry.closes.len() > 0 {
                    let closes_string = entry.closes.iter()
                                                    .map(|s| format!("[#{}]({})",
                                                        &*s,
                                                        options.link_style.issue_link(&*s, &options.repo)))
                                                    // FIXME: Connect should be
                                                    // used on the Iterator
                                                    .collect::<Vec<String>>()
                                                    .connect(", ");

                    if let Err(..) = write!(self.0 , ", closes {}", closes_string) {
                        return Err(Error::WriteErr);
                    }
                }

                if let Err(..) = write!(self.0 , ")\n") {
                    return Err(Error::WriteErr);
                }
            }
        }

        Ok(())
    }

    /// Writes some contents to the `Write` writer object
    #[allow(dead_code)]
    fn write(&mut self, content: &str) -> io::Result<()> {
        try!(write!(self.0 , "\n\n\n"));
        write!(self.0 , "{}", content)
    }
}

impl<'a> FormatWriter for MarkdownWriter<'a> {
    fn write_changelog(&mut self, options: &Clog, sm: &SectionMap) -> WriterResult {
        if let Some(..) = self.write_header(options).err() {
            return Err(Error::WriteErr);
        }
        for (sec, secmap) in sm.sections.iter() {
            try!(self.write_section(options, &sec[..], &secmap.iter().collect::<BTreeMap<_,_>>()));
        }

        self.0.flush().unwrap();

        Ok(())
    }
}
<commit_msg>fix(Markdown Writer): adds a colon ':' after the component in changelogs<commit_after>use std::collections::BTreeMap;
use std::io;

use time;

use clog::Clog;
use git::Commit;
use error::Error;
use fmt::{FormatWriter, WriterResult};
use sectionmap::SectionMap;

/// Wraps a `std::io::Write` object to write `clog` output in a Markdown format
///
/// # Example
///
/// ```no_run
/// # use std::fs::File;
/// # use clog::{SectionMap, Clog};
/// # use clog::fmt::MarkdownWriter;
/// let clog = Clog::new().unwrap_or_else(|e| {
///     e.exit();
/// });
///
/// // Get the commits we're interested in...
/// let sm = SectionMap::from_commits(clog.get_commits());
///
/// // Create a file to hold our results, which the MardownWriter will wrap (note, .unwrap() is only
/// // used to keep the example short and concise)
/// let mut file = File::create("my_changelog.md").ok().unwrap();
///
/// // Create the MarkdownWriter
/// let mut writer = MarkdownWriter::new(&mut file);
///
/// // Use the MarkdownWriter to write the changelog
/// clog.write_changelog_with(&mut writer).unwrap_or_else(|e| {
///     e.exit();
/// });
/// ```
pub struct MarkdownWriter<'a>(&'a mut io::Write);

impl<'a> MarkdownWriter<'a> {
    /// Creates a new instance of the `MarkdownWriter` struct using a `std::io::Write` object.
    ///
    /// # Example
    ///
    /// ```no_run
    /// # use std::io::{stdout, BufWriter};
    /// # use clog::Clog;
    /// # use clog::fmt::MarkdownWriter;
    /// let clog = Clog::new().unwrap_or_else(|e| {
    ///     e.exit();
    /// });
    ///
    /// // Create a MarkdownWriter to wrap stdout
    /// let out = stdout();
    /// let mut out_buf = BufWriter::new(out.lock());
    /// let mut writer = MarkdownWriter::new(&mut out_buf);
    /// ```
    pub fn new<T: io::Write + 'a>(writer: &'a mut T) -> MarkdownWriter<'a> {
        MarkdownWriter(writer)
    }

    fn write_header(&mut self, options: &Clog) -> io::Result<()> {
        let subtitle = match options.subtitle.len() {
            0 => options.subtitle.to_owned(),
            _ => format!(" {}", options.subtitle)
        };

        let version_text = if options.patch_ver {
            format!("### {}{}", options.version, subtitle)
        } else {
            format!("## {}{}", options.version, subtitle)
        };

        let date = time::now_utc();

        match date.strftime("%Y-%m-%d") {
            Ok(date) => {
                write!(
                    self.0,
                    "\n{} ({})\n\n",
                    options.version, version_text, date
                )
            },
            Err(_)   => {
                write!(
                    self.0,
                    "\n{} ({})\n\n",
                    options.version, version_text, "XXXX-XX-XX"
                )
            }
        }
    }

    /// Writes a particular section of a changelog
    fn write_section(&mut self, options: &Clog, title: &str, section: &BTreeMap<&String, &Vec<Commit>>)
                            -> WriterResult {
        if section.len() == 0 { return Ok(()) }

        if let Err(..) = self.0.write(&format!("\n#### {}\n\n", title)[..].as_bytes()) {
            return Err(Error::WriteErr);
        }

        for (component, entries) in section.iter() {
            let nested = (entries.len() > 1) && !component.is_empty();

            let prefix = if nested {
                if let Err(..) = write!(self.0 , "* **{}:**\n", component) {
                    return Err(Error::WriteErr);
                }
                "  *".to_owned()
            } else if !component.is_empty() {
                format!("* **{}:**", component)
            } else {
                format!("* ")
            };

            for entry in entries.iter() {
                if let Err(..) = write!(
                                    self.0 , "{} {} ([{}]({})",
                                    prefix,
                                    entry.subject,
                                    &entry.hash[0..8],
                                    options.link_style
                                           .commit_link(&*entry.hash, &options.repo[..])
                                ) {
                    return Err(Error::WriteErr);
                }

                if entry.closes.len() > 0 {
                    let closes_string = entry.closes.iter()
                                                    .map(|s| format!("[#{}]({})",
                                                        &*s,
                                                        options.link_style.issue_link(&*s, &options.repo)))
                                                    // FIXME: Connect should be
                                                    // used on the Iterator
                                                    .collect::<Vec<String>>()
                                                    .connect(", ");

                    if let Err(..) = write!(self.0 , ", closes {}", closes_string) {
                        return Err(Error::WriteErr);
                    }
                }

                if let Err(..) = write!(self.0 , ")\n") {
                    return Err(Error::WriteErr);
                }
            }
        }

        Ok(())
    }

    /// Writes some contents to the `Write` writer object
    #[allow(dead_code)]
    fn write(&mut self, content: &str) -> io::Result<()> {
        try!(write!(self.0 , "\n\n\n"));
        write!(self.0 , "{}", content)
    }
}

impl<'a> FormatWriter for MarkdownWriter<'a> {
    fn write_changelog(&mut self, options: &Clog, sm: &SectionMap) -> WriterResult {
        if let Some(..) = self.write_header(options).err() {
            return Err(Error::WriteErr);
        }
        for (sec, secmap) in sm.sections.iter() {
            try!(self.write_section(options, &sec[..], &secmap.iter().collect::<BTreeMap<_,_>>()));
        }

        self.0.flush().unwrap();

        Ok(())
    }
}
<|endoftext|>


<commit_before>#[derive(Debug, Eq, PartialEq)]
struct MediaType {
    weight_milliles: u16,
    is_sxg_b3: bool,
}

impl<'a> MediaType {
    fn new(input: &'a str) -> Self {
        let input = input.trim();
        let tokens: Vec<_> = input.split(';').map(|x| x.trim()).collect();
        let is_sxg = tokens[0].eq_ignore_ascii_case("application/signed-exchange");
        let mut weight_milliles = 1000;
        let mut version = None;
        for param in tokens.iter().skip(1) {
            let entry: Vec<_> = param.split('=').collect();
            if entry.len() == 2 {
                let name = entry[0].trim();
                let value = entry[1].trim();
                if name.eq_ignore_ascii_case("q") {
                    if let Ok(value) = value.parse::<f64>() {
                        weight_milliles = (value * 1000.0) as u16;
                        // https://tools.ietf.org/html/rfc7231#section-5.3.2
                        // The weight "q=..." separates media type parameters
                        // from accept extension paramters.
                        // The "v=b3" that occurs after "q=..." is not part
                        // of the media type.
                        break;
                    }
                } else if name.eq_ignore_ascii_case("v") {
                    version = Some(value);
                }
            }
        }
        MediaType {
            is_sxg_b3: is_sxg && (version == Some("b3") || version == Some(r#""b3""#)),
            weight_milliles,
        }
    }
}

pub fn validate_sxg_request_header(accept: &str) -> Result<(), String> {
    const SXG: &'static str = "application/signed-exchange;v=b3";
    let media_types = accept.split(',');
    let media_types: Vec<_> = media_types.map(|s| MediaType::new(s)).collect();
    let q_sxg = media_types.iter().map(|t| t.weight_milliles * t.is_sxg_b3 as u16).max().unwrap();
    let q_max = media_types.iter().map(|t| t.weight_milliles).max().unwrap();
    if q_sxg == 0 {
        Err(format!("The request accept header does not contain {}.", SXG))
    } else if q_sxg < q_max {
        Err(format!("The q value of {} is not the max in request accept header", SXG))
    } else {
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn it_works() {
        assert!(validate_sxg_request_header("application/signed-exchange;v=b3").is_ok());
        assert!(validate_sxg_request_header("application/signed-exchange;v=b3;q=0.9,*/*;q=0.8").is_ok());
        assert!(validate_sxg_request_header("").is_err());
        assert!(validate_sxg_request_header("text/html,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9").is_err());
    }
    #[test]
    fn opional_whitespaces() {
        assert!(validate_sxg_request_header("  application/signed-exchange  ;  v=b3  ;  q=0.9  ,  */*  ;  q=0.8  ").is_ok());
    }
    #[test]
    fn upper_case_param_name() {
        assert_eq!(
            MediaType::new("application/signed-exchange;V=b3;Q=0.9"),
            MediaType {
                is_sxg_b3: true,
                weight_milliles: 900,
            },
        );
    }
    #[test]
    fn v_not_b3() {
        assert_eq!(
            MediaType::new("application/signed-exchange"),
            MediaType {
                is_sxg_b3: false,
                weight_milliles: 1000,
            },
        );
        assert_eq!(
            MediaType::new("application/signed-exchange;v=b2"),
            MediaType {
                is_sxg_b3: false,
                weight_milliles: 1000,
            },
        );
    }
    #[test]
    fn quoted_string() {
        assert_eq!(
            MediaType::new(r#"application/signed-exchange;v="b3""#),
            MediaType {
                is_sxg_b3: true,
                weight_milliles: 1000,
            },
        );
    }
    #[test]
    fn does_not_count_v_after_q() {
        assert_eq!(
            MediaType::new("application/signed-exchange;v=b3;q=0.5"),
            MediaType {
                is_sxg_b3: true,
                weight_milliles: 500,
            },
        );
        assert_eq!(
            MediaType::new("application/signed-exchange;q=0.5;v=b3"),
            MediaType {
                is_sxg_b3: false,
                weight_milliles: 500,
            },
        );
    }
}

<commit_msg>Fix a typo (#28)<commit_after>#[derive(Debug, Eq, PartialEq)]
struct MediaType {
    weight_milliles: u16,
    is_sxg_b3: bool,
}

impl<'a> MediaType {
    fn new(input: &'a str) -> Self {
        let input = input.trim();
        let tokens: Vec<_> = input.split(';').map(|x| x.trim()).collect();
        let is_sxg = tokens[0].eq_ignore_ascii_case("application/signed-exchange");
        let mut weight_milliles = 1000;
        let mut version = None;
        for param in tokens.iter().skip(1) {
            let entry: Vec<_> = param.split('=').collect();
            if entry.len() == 2 {
                let name = entry[0].trim();
                let value = entry[1].trim();
                if name.eq_ignore_ascii_case("q") {
                    if let Ok(value) = value.parse::<f64>() {
                        weight_milliles = (value * 1000.0) as u16;
                        // https://tools.ietf.org/html/rfc7231#section-5.3.2
                        // The weight "q=..." separates media type parameters
                        // from accept extension paramters.
                        // The "v=b3" that occurs after "q=..." is not part
                        // of the media type.
                        break;
                    }
                } else if name.eq_ignore_ascii_case("v") {
                    version = Some(value);
                }
            }
        }
        MediaType {
            is_sxg_b3: is_sxg && (version == Some("b3") || version == Some(r#""b3""#)),
            weight_milliles,
        }
    }
}

pub fn validate_sxg_request_header(accept: &str) -> Result<(), String> {
    const SXG: &'static str = "application/signed-exchange;v=b3";
    let media_types = accept.split(',');
    let media_types: Vec<_> = media_types.map(|s| MediaType::new(s)).collect();
    let q_sxg = media_types.iter().map(|t| t.weight_milliles * t.is_sxg_b3 as u16).max().unwrap();
    let q_max = media_types.iter().map(|t| t.weight_milliles).max().unwrap();
    if q_sxg == 0 {
        Err(format!("The request accept header does not contain {}.", SXG))
    } else if q_sxg < q_max {
        Err(format!("The q value of {} is not the max in request accept header", SXG))
    } else {
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn it_works() {
        assert!(validate_sxg_request_header("application/signed-exchange;v=b3").is_ok());
        assert!(validate_sxg_request_header("application/signed-exchange;v=b3;q=0.9,*/*;q=0.8").is_ok());
        assert!(validate_sxg_request_header("").is_err());
        assert!(validate_sxg_request_header("text/html,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9").is_err());
    }
    #[test]
    fn optional_whitespaces() {
        assert!(validate_sxg_request_header("  application/signed-exchange  ;  v=b3  ;  q=0.9  ,  */*  ;  q=0.8  ").is_ok());
    }
    #[test]
    fn upper_case_param_name() {
        assert_eq!(
            MediaType::new("application/signed-exchange;V=b3;Q=0.9"),
            MediaType {
                is_sxg_b3: true,
                weight_milliles: 900,
            },
        );
    }
    #[test]
    fn v_not_b3() {
        assert_eq!(
            MediaType::new("application/signed-exchange"),
            MediaType {
                is_sxg_b3: false,
                weight_milliles: 1000,
            },
        );
        assert_eq!(
            MediaType::new("application/signed-exchange;v=b2"),
            MediaType {
                is_sxg_b3: false,
                weight_milliles: 1000,
            },
        );
    }
    #[test]
    fn quoted_string() {
        assert_eq!(
            MediaType::new(r#"application/signed-exchange;v="b3""#),
            MediaType {
                is_sxg_b3: true,
                weight_milliles: 1000,
            },
        );
    }
    #[test]
    fn does_not_count_v_after_q() {
        assert_eq!(
            MediaType::new("application/signed-exchange;v=b3;q=0.5"),
            MediaType {
                is_sxg_b3: true,
                weight_milliles: 500,
            },
        );
        assert_eq!(
            MediaType::new("application/signed-exchange;q=0.5;v=b3"),
            MediaType {
                is_sxg_b3: false,
                weight_milliles: 500,
            },
        );
    }
}

<|endoftext|>


<commit_before>use std::process::{Command, exit};

fn main() {
  println!("cargo:rerun-if-changed=../../../../../3rdparty/protobuf");
  println!("cargo:rerun-if-changed=generate-grpc.sh");
  let output = Command::new("./generate-grpc.sh").output().unwrap();

  print!("{}", String::from_utf8(output.stdout).unwrap());
  eprint!("{}", String::from_utf8(output.stderr).unwrap());
  exit(output.status.code().unwrap());
}
<commit_msg>Re-generate protos if the proto compiler changes (#5138)<commit_after>use std::env::home_dir;
use std::process::{Command, exit};

fn main() {
  println!("cargo:rerun-if-changed=../../../../../3rdparty/protobuf");
  println!("cargo:rerun-if-changed=generate-grpc.sh");

  let cachedir = home_dir().unwrap().join(".cache").join("pants").join("rust").join("cargo").join("bin");
  println!("cargo:rerun-if-changed={}", cachedir.join("grpc_rust_plugin").to_str().unwrap());
  println!("cargo:rerun-if-changed={}", cachedir.join("protoc-gen-rust").to_str().unwrap());

  let output = Command::new("./generate-grpc.sh").output().unwrap();

  print!("{}", String::from_utf8(output.stdout).unwrap());
  eprint!("{}", String::from_utf8(output.stderr).unwrap());
  exit(output.status.code().unwrap());
}
<|endoftext|>


<commit_before>//! Functions for computing integral images and running sums
//! of rows and columns.
//! I is the integral image of an image F if I(x, y) is the
//! sum of F(x', y') for x' <= x, y' <= y. i.e. each pixel
//! in the integral image contains the sum of the pixel intensities
//! of all input pixels that are above it and to its left.
//! The integral image has the helpful property that it lets us
//! compute the sum of pixel intensities from any rectangular region
//! in the input image in constant time.
//! Specifically, given a rectangle in F with clockwise corners
//! A, B, C, D, with A at the upper left, the total pixel intensity
//! of this rectangle is I(C) - I(B) - I(D) + I(A).

extern crate image;

use image::{
    Luma,
    GenericImage,
    ImageBuffer
};

use definitions::{
    VecBuffer
};

/// Computes the integral image of an 8bpp grayscale image.
// TODO: Support more formats.
// TODO: This is extremely slow. Fix that!
pub fn integral_image<I>(image: &I) -> VecBuffer<Luma<u32>>
    where I: GenericImage<Pixel=Luma<u8>> {
    padded_integral_image(image, 0, 0)
}

/// Computes the integral image of the result of padding image
/// with its boundary pixels for x_padding columns on either
/// side and y_padding rows at its top and bottom.
/// Returned image has width image.width() + 2 * x_padding
/// and height image.height() + 2 * y_padding.
pub fn padded_integral_image<I>(image: &I, x_padding: u32, y_padding: u32)
        -> VecBuffer<Luma<u32>>
    where I: GenericImage<Pixel=Luma<u8>> {

    let (in_width, in_height) = image.dimensions();
    let out_width = in_width + 2 * x_padding;
    let out_height = in_height + 2 * y_padding;

    let mut out: ImageBuffer<Luma<u32>, Vec<u32>>
        = ImageBuffer::new(out_width, out_height);

    for y in 0..out_height {
        for x in 0..out_width {

            let y_in: u32;
            if y < y_padding {
                y_in = 0;
            }
            else if y >= in_height + y_padding {
                y_in = in_height - 1;
            }
            else {
                y_in = y - y_padding;
            }
            let x_in: u32;
            if x < x_padding {
                x_in = 0;
            }
            else if x >= in_width + x_padding {
                x_in = in_width - 1;
            }
            else {
                x_in = x - x_padding;
            }

            let p = image.get_pixel(x_in, y_in);
            out.put_pixel(x, y, Luma([p[0] as u32]));
        }
    }

    for x in 1..out_width {
        (*out.get_pixel_mut(x, 0))[0] += out.get_pixel(x - 1, 0)[0];
    }

    for y in 1..out_height {
        (*out.get_pixel_mut(0, y))[0] += out.get_pixel(0, y - 1)[0];

        for x in 1..out_width {
            (*out.get_pixel_mut(x, y))[0] += out.get_pixel(x, y - 1)[0];
            (*out.get_pixel_mut(x, y))[0] += out.get_pixel(x - 1, y)[0];
            (*out.get_pixel_mut(x, y))[0] -= out.get_pixel(x - 1, y - 1)[0];
        }
    }

    out
}

/// Computes the running sum of one row of image, padded
/// at the beginning and end. The padding is by continuity.
/// Takes a reference to buffer so that this can be reused
/// for all rows in an image.
// TODO: faster, more formats
pub fn row_running_sum<I>(image: &I, row: u32, buffer: &mut [u32], padding: u32)
    where I: GenericImage<Pixel=Luma<u8>> {

    let width = image.width();
    assert!(buffer.len() >= (width + 2 * padding) as usize,
        format!("Buffer length {} is less than 2 * {} + {}",
            buffer.len(), width, padding));

    for x in 0..padding {
        buffer[x as usize] = image.get_pixel(0, row)[0] as u32;
    }

    for x in 0..width {
        let idx = (x + padding) as usize;
        buffer[idx] = image.get_pixel(x, row)[0] as u32;
    }

    for x in 0..padding {
        let idx = (x + width + padding) as usize;
        buffer[idx] = image.get_pixel(width - 1, row)[0] as u32;
    }

    for x in 1..width + 2 * padding {
        buffer[x as usize] += buffer[(x - 1) as usize] as u32;
    }
}

/// Computes the running sum of one column of image, padded
/// at the top and bottom. The padding is by continuity.
/// Takes a reference to buffer so that this can be reused
/// for all columns in an image.
// TODO: faster, more formats
pub fn column_running_sum<I>(image: &I, column: u32, buffer: &mut [u32], padding: u32)
    where I: GenericImage<Pixel=Luma<u8>> {

    let height = image.height();
    assert!(buffer.len() >= (height + 2 * padding) as usize,
        format!("Buffer length {} is less than 2 * {} + {}",
            buffer.len(), height, padding));

    for y in 0..padding {
        buffer[y as usize] = image.get_pixel(column, 0)[0] as u32;
    }

    for y in 0..height {
        let idx = (y + padding) as usize;
        buffer[idx] = image.get_pixel(column, y)[0] as u32;
    }

    for y in 0..padding {
        let idx = (y + height + padding) as usize;
        buffer[idx] = image.get_pixel(column, height - 1)[0] as u32;
    }

    for y in 1..height + 2 * padding {
        buffer[y as usize] += buffer[(y - 1) as usize] as u32;
    }
}

#[cfg(test)]
mod test {

    use super::{
        column_running_sum,
        integral_image,
        padded_integral_image,
        row_running_sum
    };
    use utils::{
        gray_bench_image
    };
    use image::{
        GrayImage,
        ImageBuffer,
        Luma
    };
    use test;

    #[test]
    fn test_integral_image() {
        let image: GrayImage = ImageBuffer::from_raw(3, 2, vec![
            1, 2, 3,
            4, 5, 6]).unwrap();

        let expected: ImageBuffer<Luma<u32>, Vec<u32>>
            = ImageBuffer::from_raw(3, 2, vec![
            1,  3,  6,
            5, 12, 21]).unwrap();

        assert_pixels_eq!(integral_image(&image), expected);
    }

    #[bench]
    fn bench_integral_image(b: &mut test::Bencher) {
        let image = gray_bench_image(500, 500);
        b.iter(|| {
            let integral = integral_image(&image);
            test::black_box(integral);
            });
    }

    #[test]
    fn test_padded_integral_image() {
        let image: GrayImage = ImageBuffer::from_raw(3, 2, vec![
            1, 2, 3,
            4, 5, 6]).unwrap();

        let expected: ImageBuffer<Luma<u32>, Vec<u32>>
            = ImageBuffer::from_raw(5, 4, vec![
              1,  2,   4,   7,  10,
              2,  4,   8,  14,  20,
              6, 12,  21,  33,  45,
             10, 20,  34,  52,  70]).unwrap();

        assert_pixels_eq!(padded_integral_image(&image, 1, 1), expected);
    }

    #[test]
    fn test_row_running_sum() {
        let image: GrayImage = ImageBuffer::from_raw(3, 2, vec![
            1, 2, 3,
            4, 5, 6]).unwrap();

        let expected = [1, 2, 4, 7, 10];

        let mut buffer = [0; 5];
        row_running_sum(&image, 0, &mut buffer, 1);

        assert_eq!(buffer, expected);
    }

    #[bench]
    fn bench_row_running_sum(b: &mut test::Bencher) {
        let image = gray_bench_image(1000, 1);
        let mut buffer = [0; 1010];
        b.iter(|| {
            row_running_sum(&image, 0, &mut buffer, 5);
            });
    }

    #[test]
    fn test_column_running_sum() {
        let image: GrayImage = ImageBuffer::from_raw(2, 3, vec![
            1, 4,
            2, 5,
            3, 6]).unwrap();

        let expected = [1, 2, 4, 7, 10];

        let mut buffer = [0; 5];
        column_running_sum(&image, 0, &mut buffer, 1);

        assert_eq!(buffer, expected);
    }

    #[bench]
    fn bench_column_running_sum(b: &mut test::Bencher) {
        let image = gray_bench_image(100, 1000);
        let mut buffer = [0; 1010];
        b.iter(|| {
            column_running_sum(&image, 0, &mut buffer, 5);
            });
    }
}
<commit_msg>Small docs improvement<commit_after>//! Functions for computing [integral images](https://en.wikipedia.org/wiki/Summed_area_table)
//! and running sums of rows and columns.

extern crate image;

use image::{
    Luma,
    GenericImage,
    ImageBuffer
};

use definitions::{
    VecBuffer
};

/// Computes the integral image of an 8bpp grayscale image.
/// I is the integral image of an image F if I(x, y) is the
/// sum of F(x', y') for x' <= x, y' <= y. i.e. each pixel
/// in the integral image contains the sum of the pixel intensities
/// of all input pixels that are above it and to its left.
/// The integral image has the helpful property that it lets us
/// compute the sum of pixel intensities from any rectangular region
/// in the input image in constant time.
/// Specifically, given a rectangle in F with clockwise corners
/// A, B, C, D, with A at the upper left, the total pixel intensity
/// of this rectangle is I(C) - I(B) - I(D) + I(A).
// TODO: Support more formats.
// TODO: This is extremely slow. Fix that!
pub fn integral_image<I>(image: &I) -> VecBuffer<Luma<u32>>
    where I: GenericImage<Pixel=Luma<u8>> {
    padded_integral_image(image, 0, 0)
}

/// Computes the integral image of the result of padding image
/// with its boundary pixels for x_padding columns on either
/// side and y_padding rows at its top and bottom.
/// Returned image has width image.width() + 2 * x_padding
/// and height image.height() + 2 * y_padding.
pub fn padded_integral_image<I>(image: &I, x_padding: u32, y_padding: u32)
        -> VecBuffer<Luma<u32>>
    where I: GenericImage<Pixel=Luma<u8>> {

    let (in_width, in_height) = image.dimensions();
    let out_width = in_width + 2 * x_padding;
    let out_height = in_height + 2 * y_padding;

    let mut out: ImageBuffer<Luma<u32>, Vec<u32>>
        = ImageBuffer::new(out_width, out_height);

    for y in 0..out_height {
        for x in 0..out_width {

            let y_in: u32;
            if y < y_padding {
                y_in = 0;
            }
            else if y >= in_height + y_padding {
                y_in = in_height - 1;
            }
            else {
                y_in = y - y_padding;
            }
            let x_in: u32;
            if x < x_padding {
                x_in = 0;
            }
            else if x >= in_width + x_padding {
                x_in = in_width - 1;
            }
            else {
                x_in = x - x_padding;
            }

            let p = image.get_pixel(x_in, y_in);
            out.put_pixel(x, y, Luma([p[0] as u32]));
        }
    }

    for x in 1..out_width {
        (*out.get_pixel_mut(x, 0))[0] += out.get_pixel(x - 1, 0)[0];
    }

    for y in 1..out_height {
        (*out.get_pixel_mut(0, y))[0] += out.get_pixel(0, y - 1)[0];

        for x in 1..out_width {
            (*out.get_pixel_mut(x, y))[0] += out.get_pixel(x, y - 1)[0];
            (*out.get_pixel_mut(x, y))[0] += out.get_pixel(x - 1, y)[0];
            (*out.get_pixel_mut(x, y))[0] -= out.get_pixel(x - 1, y - 1)[0];
        }
    }

    out
}

/// Computes the running sum of one row of image, padded
/// at the beginning and end. The padding is by continuity.
/// Takes a reference to buffer so that this can be reused
/// for all rows in an image.
// TODO: faster, more formats
pub fn row_running_sum<I>(image: &I, row: u32, buffer: &mut [u32], padding: u32)
    where I: GenericImage<Pixel=Luma<u8>> {

    let width = image.width();
    assert!(buffer.len() >= (width + 2 * padding) as usize,
        format!("Buffer length {} is less than 2 * {} + {}",
            buffer.len(), width, padding));

    for x in 0..padding {
        buffer[x as usize] = image.get_pixel(0, row)[0] as u32;
    }

    for x in 0..width {
        let idx = (x + padding) as usize;
        buffer[idx] = image.get_pixel(x, row)[0] as u32;
    }

    for x in 0..padding {
        let idx = (x + width + padding) as usize;
        buffer[idx] = image.get_pixel(width - 1, row)[0] as u32;
    }

    for x in 1..width + 2 * padding {
        buffer[x as usize] += buffer[(x - 1) as usize] as u32;
    }
}

/// Computes the running sum of one column of image, padded
/// at the top and bottom. The padding is by continuity.
/// Takes a reference to buffer so that this can be reused
/// for all columns in an image.
// TODO: faster, more formats
pub fn column_running_sum<I>(image: &I, column: u32, buffer: &mut [u32], padding: u32)
    where I: GenericImage<Pixel=Luma<u8>> {

    let height = image.height();
    assert!(buffer.len() >= (height + 2 * padding) as usize,
        format!("Buffer length {} is less than 2 * {} + {}",
            buffer.len(), height, padding));

    for y in 0..padding {
        buffer[y as usize] = image.get_pixel(column, 0)[0] as u32;
    }

    for y in 0..height {
        let idx = (y + padding) as usize;
        buffer[idx] = image.get_pixel(column, y)[0] as u32;
    }

    for y in 0..padding {
        let idx = (y + height + padding) as usize;
        buffer[idx] = image.get_pixel(column, height - 1)[0] as u32;
    }

    for y in 1..height + 2 * padding {
        buffer[y as usize] += buffer[(y - 1) as usize] as u32;
    }
}

#[cfg(test)]
mod test {

    use super::{
        column_running_sum,
        integral_image,
        padded_integral_image,
        row_running_sum
    };
    use utils::{
        gray_bench_image
    };
    use image::{
        GrayImage,
        ImageBuffer,
        Luma
    };
    use test;

    #[test]
    fn test_integral_image() {
        let image: GrayImage = ImageBuffer::from_raw(3, 2, vec![
            1, 2, 3,
            4, 5, 6]).unwrap();

        let expected: ImageBuffer<Luma<u32>, Vec<u32>>
            = ImageBuffer::from_raw(3, 2, vec![
            1,  3,  6,
            5, 12, 21]).unwrap();

        assert_pixels_eq!(integral_image(&image), expected);
    }

    #[bench]
    fn bench_integral_image(b: &mut test::Bencher) {
        let image = gray_bench_image(500, 500);
        b.iter(|| {
            let integral = integral_image(&image);
            test::black_box(integral);
            });
    }

    #[test]
    fn test_padded_integral_image() {
        let image: GrayImage = ImageBuffer::from_raw(3, 2, vec![
            1, 2, 3,
            4, 5, 6]).unwrap();

        let expected: ImageBuffer<Luma<u32>, Vec<u32>>
            = ImageBuffer::from_raw(5, 4, vec![
              1,  2,   4,   7,  10,
              2,  4,   8,  14,  20,
              6, 12,  21,  33,  45,
             10, 20,  34,  52,  70]).unwrap();

        assert_pixels_eq!(padded_integral_image(&image, 1, 1), expected);
    }

    #[test]
    fn test_row_running_sum() {
        let image: GrayImage = ImageBuffer::from_raw(3, 2, vec![
            1, 2, 3,
            4, 5, 6]).unwrap();

        let expected = [1, 2, 4, 7, 10];

        let mut buffer = [0; 5];
        row_running_sum(&image, 0, &mut buffer, 1);

        assert_eq!(buffer, expected);
    }

    #[bench]
    fn bench_row_running_sum(b: &mut test::Bencher) {
        let image = gray_bench_image(1000, 1);
        let mut buffer = [0; 1010];
        b.iter(|| {
            row_running_sum(&image, 0, &mut buffer, 5);
            });
    }

    #[test]
    fn test_column_running_sum() {
        let image: GrayImage = ImageBuffer::from_raw(2, 3, vec![
            1, 4,
            2, 5,
            3, 6]).unwrap();

        let expected = [1, 2, 4, 7, 10];

        let mut buffer = [0; 5];
        column_running_sum(&image, 0, &mut buffer, 1);

        assert_eq!(buffer, expected);
    }

    #[bench]
    fn bench_column_running_sum(b: &mut test::Bencher) {
        let image = gray_bench_image(100, 1000);
        let mut buffer = [0; 1010];
        b.iter(|| {
            column_running_sum(&image, 0, &mut buffer, 5);
            });
    }
}
<|endoftext|>


<commit_before>//! The memory module.
//! This module declares a single struct, the Memory Management Unit (MMU).
//! The MMU acts as the system bus, allowing components to communicate with each other, reaches
//! RAM, ROM, I/O registers and more.
use std::fmt;
use std::cell::Cell;

use jeebie::gpu::GPU;
use jeebie::cart::Cartridge;
use jeebie::bootrom::DMG_BOOTROM;

/// The Memory Management Unit.
/// Provides access to all mapped memory in the system, including I/O and graphics.
pub struct MMU {
    pub loading_bios: Cell<bool>,
    data: [u8; 65536],
    pub gpu: GPU,
}

impl fmt::Debug for MMU {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "MMU 64k")
    }
}

impl MMU {
    pub fn new() -> MMU {
        MMU {
            loading_bios: Cell::new(true),
            data: [0; 65536],
            gpu: GPU::new(),
        }
    }

    pub fn load_rom(&mut self, cart: &Cartridge) {
        for i in 0..cart.size {
            self.data[i] = cart.data[i];
        }
    }

    /// reads a byte at the memory address specified
    pub fn read_b(&self, addr: u16) -> u8 {
        // when PC first reaches 0x100, the BIOS data is not addressable anymore.
        // using Cell sucks, but this is one of the few cases mentioned by official docs where
        // it is an option. Read is logically an immutable operation, but we need to make the
        // bios unaddressable after its first read.
        if self.loading_bios.get() && addr == 0x0100 {
            self.loading_bios.set(false);
        }

        match addr {
            // bios area, 256B long for regular gameboy.
            0...0x00FF if self.loading_bios.get() => DMG_BOOTROM[(addr & 0xFF) as usize],
            // ROM0 area, this is banked memory, it will swap according to selected bank
            0x0000...0x3FFF if !self.loading_bios.get() => self.data[addr as usize],
            // ROM1 area, 16kB unbanked data
            0x4000...0x7FFF => self.data[addr as usize],
            // Graphics, 8kB VRAM
            0x8000...0x9FFF => self.gpu.read_vram((addr & 0x1FFF) as usize),
            // Switchable RAM bank, 8kB
            0xA000...0xBFFF => {
                // TODO: handle RAM banks
                self.data[addr as usize]
            }
            // Internal RAM, 8kB
            0xC000...0xDFFF => self.data[addr as usize],
            // Echo of internal RAM, this is less than 8k, up to 0xFDFF,
            0xE000...0xFDFF => self.data[(addr - 0x2000) as usize],
            // Sprite attribute memory, 160B
            0xFE00...0xFE9F => self.gpu.read_oam((addr - 0xFE00) as usize),
            // empty
            0xFEA0...0xFEFF | 0xFF4C...0xFF7F => 0,
            // I/O ports
            0xFF00...0xFF4B => {
                match addr & 0xFF {
                    0x40 | 0x42 | 0x43 | 0x44 | 0x47 => self.gpu.read_register(addr as usize),
                    _ => 0,
                }
            },
            // High RAM (zero page), used with LDH instructions
            0xFF80...0xFFFE => 0,
            // Interrupt Enable register
            0xFFFF => 0,

            _ => panic!("tried to read at unknown address: {:4x}", addr),
        }
    }

    pub fn write_b(&mut self, addr: u16, data: u8) {
        match addr {
            // bios area, 256B long for regular gameboy.
            0...0x00FF if self.loading_bios.get() => unimplemented!(),
            // ROM0 area, this is banked memory, it will swap according to selected bank
            0x0000...0x3FFF if !self.loading_bios.get() => unimplemented!(),
            // ROM1 area, 16kB unbanked data
            0x4000...0x7FFF => unimplemented!(),
            // Graphics, 8kB VRAM
            0x8000...0x9FFF => self.gpu.write_vram((addr & 0x1FFF) as usize, data),
            // Switchable RAM bank, 8kB
            0xA000...0xBFFF => {
                // TODO: handle RAM banks
                self.data[addr as usize] = data;
            }
            // Internal RAM, 8kB
            0xC000...0xDFFF => {
                self.data[addr as usize] = data;
            },
            // Echo of internal RAM, this is less than 8k, up to 0xFDFF,
            0xE000...0xFDFF => {
                self.data[(addr - 0x2000) as usize] = data;
            },
            // Sprite attribute memory, 160B
            0xFE00...0xFE9F => {
                self.gpu.write_oam((addr - 0xFE00) as usize, data);
            }
            // empty
            0xFEA0...0xFEFF | 0xFF4C...0xFF7F => {},
            // I/O ports
            0xFF00...0xFF4B => {
                match addr & 0xFF {
                    0x40 | 0x42 | 0x43 | 0x44 | 0x47 => self.gpu.write_register(addr as usize, data),
                    _ => {},
                }
            },
            // High RAM (zero page), used with LDH instructions
            0xFF80...0xFFFE => unimplemented!(),
            // Interrupt Enable register
            0xFFFF => unimplemented!(),

            _ => panic!("tried to write at unknown address: {:4x}", addr),
        }
    }
}
<commit_msg>Improved memory error messages<commit_after>//! The memory module.
//! This module declares a single struct, the Memory Management Unit (MMU).
//! The MMU acts as the system bus, allowing components to communicate with each other, reaches
//! RAM, ROM, I/O registers and more.
use std::fmt;
use std::cell::Cell;

use jeebie::gpu::GPU;
use jeebie::cart::Cartridge;
use jeebie::bootrom::DMG_BOOTROM;

/// The Memory Management Unit.
/// Provides access to all mapped memory in the system, including I/O and graphics.
pub struct MMU {
    pub loading_bios: Cell<bool>,
    data: [u8; 65536],
    pub gpu: GPU,
}

impl fmt::Debug for MMU {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "MMU 64k")
    }
}

impl MMU {
    pub fn new() -> MMU {
        MMU {
            loading_bios: Cell::new(true),
            data: [0; 65536],
            gpu: GPU::new(),
        }
    }

    pub fn load_rom(&mut self, cart: &Cartridge) {
        for i in 0..cart.size {
            self.data[i] = cart.data[i];
        }
    }

    /// reads a byte at the memory address specified
    pub fn read_b(&self, addr: u16) -> u8 {
        // when PC first reaches 0x100, the BIOS data is not addressable anymore.
        // using Cell sucks, but this is one of the few cases mentioned by official docs where
        // it is an option. Read is logically an immutable operation, but we need to make the
        // bios unaddressable after its first read.
        if self.loading_bios.get() && addr == 0x0100 {
            self.loading_bios.set(false);
        }

        match addr {
            // bios area, 256B long for regular gameboy.
            0...0x00FF if self.loading_bios.get() => DMG_BOOTROM[(addr & 0xFF) as usize],
            // ROM0 area, this is banked memory, it will swap according to selected bank
            0x0000...0x3FFF if !self.loading_bios.get() => self.data[addr as usize],
            // ROM1 area, 16kB unbanked data
            0x4000...0x7FFF => self.data[addr as usize],
            // Graphics, 8kB VRAM
            0x8000...0x9FFF => self.gpu.read_vram((addr & 0x1FFF) as usize),
            // Switchable RAM bank, 8kB
            0xA000...0xBFFF => {
                // TODO: handle RAM banks
                self.data[addr as usize]
            }
            // Internal RAM, 8kB
            0xC000...0xDFFF => self.data[addr as usize],
            // Echo of internal RAM, this is less than 8k, up to 0xFDFF,
            0xE000...0xFDFF => self.data[(addr - 0x2000) as usize],
            // Sprite attribute memory, 160B
            0xFE00...0xFE9F => self.gpu.read_oam((addr - 0xFE00) as usize),
            // empty
            0xFEA0...0xFEFF | 0xFF4C...0xFF7F => 0,
            // I/O ports
            0xFF00...0xFF4B => {
                match addr & 0xFF {
                    0x40 | 0x42 | 0x43 | 0x44 | 0x47 => self.gpu.read_register(addr as usize),
                    _ => 0,
                }
            },
            // High RAM (zero page), used with LDH instructions
            0xFF80...0xFFFE => 0,
            // Interrupt Enable register
            0xFFFF => 0,

            _ => panic!("tried to read at unknown address: {:4x}", addr),
        }
    }

    pub fn write_b(&mut self, addr: u16, data: u8) {
        match addr {
            // bios area, 256B long for regular gameboy.
            0...0x00FF if self.loading_bios.get() => panic!("Writing to bootrom ${:04x} <- {:02x}", addr, data),
            // ROM0 area, this is banked memory, it will swap according to selected bank
            0x0000...0x3FFF if !self.loading_bios.get() => panic!("Writing to ROM0 ${:04x} <- {:02x}", addr, data),
            // ROM1 area, 16kB unbanked data
            0x4000...0x7FFF => panic!("Writing to ROM1 ${:04x} <- {:02x}", addr, data),
            // Graphics, 8kB VRAM
            0x8000...0x9FFF => self.gpu.write_vram((addr & 0x1FFF) as usize, data),
            // Switchable RAM bank, 8kB
            0xA000...0xBFFF => {
                // TODO: handle RAM banks
                self.data[addr as usize] = data;
            }
            // Internal RAM, 8kB
            0xC000...0xDFFF => {
                self.data[addr as usize] = data;
            },
            // Echo of internal RAM, this is less than 8k, up to 0xFDFF,
            0xE000...0xFDFF => {
                self.data[(addr - 0x2000) as usize] = data;
            },
            // Sprite attribute memory, 160B
            0xFE00...0xFE9F => {
                self.gpu.write_oam((addr - 0xFE00) as usize, data);
            }
            // empty
            0xFEA0...0xFEFF | 0xFF4C...0xFF7F => {},
            // I/O ports
            0xFF00...0xFF4B => {
                match addr & 0xFF {
                    0x40 | 0x42 | 0x43 | 0x44 | 0x47 => self.gpu.write_register(addr as usize, data),
                    _ => {},
                }
            },
            // High RAM (zero page), used with LDH instructions
            0xFF80...0xFFFE => unimplemented!(),
            // Interrupt Enable register
            0xFFFF => unimplemented!(),

            _ => panic!("tried to write at unknown address: {:4x}", addr),
        }
    }
}
<|endoftext|>