<commit_before>use crate::{
    analysis::{imports::Imports, namespaces},
    codegen::general::{
        self, cfg_deprecated, derives, version_condition, version_condition_string,
    },
    config::gobjects::GObject,
    env::Env,
    file_saver,
    library::*,
    nameutil::enum_member_name,
    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>) {
    let configs: Vec<&GObject> = env
        .config
        .objects
        .values()
        .filter(|c| {
            c.status.need_generate() && c.type_id.map_or(false, |tid| tid.ns_id == namespaces::MAIN)
        })
        .collect();
    let mut has_get_quark = false;
    let mut has_any = false;
    let mut has_get_type = false;
    let mut generate_display_trait = false;
    for config in &configs {
        if let Type::Enumeration(ref enum_) = *env.library.type_(config.type_id.unwrap()) {
            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;
    }

    let mut imports = Imports::new(&env.library);
    imports.add(env.main_sys_crate_name());
    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::Value");
        imports.add("glib::value::SetValue");
        imports.add("glib::value::FromValue");
        imports.add("glib::value::FromValueOptional");
        imports.add("gobject_sys");
    }
    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 config in &configs {
            if let Type::Enumeration(ref enum_) = *env.library.type_(config.type_id.unwrap()) {
                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)?;
            }
        }

        Ok(())
    });
}

#[allow(clippy::write_literal)]
fn generate_enum(
    env: &Env,
    w: &mut dyn Write,
    enum_: &Enumeration,
    config: &GObject,
) -> 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),
}
"
    )?;

    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(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(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(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<glib_sys::GQuark> = once_cell::sync::Lazy::new(|| unsafe {{
            glib_sys::g_quark_from_static_string(b\"{}\\0\".as_ptr() as *const _)
        }});
        from_glib(*QUARK)",
                    quark = 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(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: &Value) -> Option<Self> {{
        Some(FromValue::from_value(value))
    }}
}}",
            name = enum_.name,
        )?;
        writeln!(w)?;

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

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

    Ok(())
}
<commit_msg>Replace "{}", "..." formatting with direct string literal<commit_after>use crate::{
    analysis::{imports::Imports, namespaces},
    codegen::general::{
        self, cfg_deprecated, derives, version_condition, version_condition_string,
    },
    config::gobjects::GObject,
    env::Env,
    file_saver,
    library::*,
    nameutil::enum_member_name,
    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>) {
    let configs: Vec<&GObject> = env
        .config
        .objects
        .values()
        .filter(|c| {
            c.status.need_generate() && c.type_id.map_or(false, |tid| tid.ns_id == namespaces::MAIN)
        })
        .collect();
    let mut has_get_quark = false;
    let mut has_any = false;
    let mut has_get_type = false;
    let mut generate_display_trait = false;
    for config in &configs {
        if let Type::Enumeration(ref enum_) = *env.library.type_(config.type_id.unwrap()) {
            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;
    }

    let mut imports = Imports::new(&env.library);
    imports.add(env.main_sys_crate_name());
    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::Value");
        imports.add("glib::value::SetValue");
        imports.add("glib::value::FromValue");
        imports.add("glib::value::FromValueOptional");
        imports.add("gobject_sys");
    }
    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 config in &configs {
            if let Type::Enumeration(ref enum_) = *env.library.type_(config.type_id.unwrap()) {
                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)?;
            }
        }

        Ok(())
    });
}

#[allow(clippy::write_literal)]
fn generate_enum(
    env: &Env,
    w: &mut dyn Write,
    enum_: &Enumeration,
    config: &GObject,
) -> 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),
}}
"
    )?;

    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(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(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(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<glib_sys::GQuark> = once_cell::sync::Lazy::new(|| unsafe {{
            glib_sys::g_quark_from_static_string(b\"{}\\0\".as_ptr() as *const _)
        }});
        from_glib(*QUARK)",
                    quark = 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(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: &Value) -> Option<Self> {{
        Some(FromValue::from_value(value))
    }}
}}",
            name = enum_.name,
        )?;
        writeln!(w)?;

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

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

    Ok(())
}
<|endoftext|>


<commit_before>use super::{Model, RenderContext};
use super::render::*;
use super::component::*;
use super::geom::*;

use gfx_device_gl as gl;
use glutin;

const BORDER_SIZE: f32 = 1.0;

pub struct Button {
    label: String,
    rect: Rect2,

    target: TextureTarget,
    hover: bool,
}

impl Button {
    pub fn new(ctx: RenderContext, label: String, rect: Rect2) -> Button {
        Button {
            label,
            rect,
            target: TextureTarget::new(ctx, rect.size),
            hover: false,
        }
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum ButtonUpdate {
    Unchanged,
    NeedRender,
    Clicked,
}

impl GuiComponent<ButtonUpdate> for Button {
    fn rect(&self) -> Rect2 {
        self.rect
    }
    fn render(&mut self, device: &mut gl::Device, ctx: &mut RenderContext, parent: Rect3) {
        // border
        ctx.draw_rect(self.rect.upgrade_with(&parent), [1.0; 3]);
        // background
        ctx.draw_rect(
            Rect2 {
                pos: self.rect.pos + Pt2::fill(BORDER_SIZE),
                size: self.rect.size - Pt2::fill(BORDER_SIZE * 2.0),
            }.upgrade_with(&parent),
            if self.hover { [0.0; 3] } else { [0.1; 3] },
        );
        ctx.draw_text(&self.label, self.rect.pos + Pt2::fill(4.0), [1.0; 3]);

        ctx.draw_textured_rect(
            self.rect.upgrade_with(&parent),
            self.target.shader_resource().clone(),
        );
    }
    fn update(&mut self, model: &Model, parent: Rect3) -> ButtonUpdate {
        let hover = self.rect.offset(parent.project()).intersect(model.mouse_pos);
        if self.hover != hover {
            self.hover = hover;
            ButtonUpdate::NeedRender
        } else {
            ButtonUpdate::Unchanged
        }
    }
    fn handle(&mut self, model: &Model, parent: Rect3, event: &glutin::Event) -> ButtonUpdate {
        ButtonUpdate::Unchanged
    }
}
<commit_msg>delete old code from button render<commit_after>use super::{Model, RenderContext};
use super::component::*;
use super::geom::*;

use gfx_device_gl as gl;
use glutin;

const BORDER_SIZE: f32 = 1.0;

pub struct Button {
    label: String,
    rect: Rect2,

    hover: bool,
}

impl Button {
    pub fn new(ctx: RenderContext, label: String, rect: Rect2) -> Button {
        Button {
            label,
            rect,
            hover: false,
        }
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum ButtonUpdate {
    Unchanged,
    NeedRender,
    Clicked,
}

impl GuiComponent<ButtonUpdate> for Button {
    fn rect(&self) -> Rect2 {
        self.rect
    }
    fn render(&mut self, device: &mut gl::Device, ctx: &mut RenderContext, parent: Rect3) {
        // border
        ctx.draw_rect(self.rect.upgrade_with(&parent), [1.0; 3]);
        // background
        ctx.draw_rect(
            Rect2 {
                pos: self.rect.pos + Pt2::fill(BORDER_SIZE),
                size: self.rect.size - Pt2::fill(BORDER_SIZE * 2.0),
            }.upgrade_with(&parent),
            if self.hover { [0.0; 3] } else { [0.1; 3] },
        );
        ctx.draw_text(&self.label, self.rect.pos + Pt2::fill(4.0), [1.0; 3]);
    }
    fn update(&mut self, model: &Model, parent: Rect3) -> ButtonUpdate {
        let hover = self.rect.offset(parent.project()).intersect(model.mouse_pos);
        if self.hover != hover {
            self.hover = hover;
            ButtonUpdate::NeedRender
        } else {
            ButtonUpdate::Unchanged
        }
    }
    fn handle(&mut self, model: &Model, parent: Rect3, event: &glutin::Event) -> ButtonUpdate {
        ButtonUpdate::Unchanged
    }
}
<|endoftext|>


<commit_before>extern crate rustbox;

use std::rc::Rc;
use std::cell::RefCell;
use std::collections::VecDeque;
use std::usize;
use rustbox::Key;
use buffer::Buffer;
use mode::{Mode,ModeType,NormalMode,InsertMode,ReplaceMode};
use point::{Direction,Point};
use point::Direction::*;

#[derive(Eq,PartialEq,Debug,Clone)]
pub enum Action {
    BackwardDelete,
    Cancel,
    ChangeMode(ModeType),
    Delete,
    DeleteLine,
    Insert(char),
    NewLine,
    NewLineAtPoint,
    MoveCursor(Direction),
    NewBuffer,
    NextBuffer,
    /// Used when in the middle of key sequence such as `yy`.
    /// See `keystrokes`.
    PartialKey,
    Paste,
    RepeatPrevious,
    Replace(char),
    Save,
    Quit,
    YankLine,
    /// Multiple actions in a row
    Multi(Vec<Action>),
    /// The same action `n` times
    Repeat(Box<Action>, usize)
}

#[derive(Eq,PartialEq,Debug,Clone)]
pub enum MicroState {
    /// The default microstate. Most events are delegated to the current mode.
    Mode,
    /// When entering data in the minibuffer.
    /// Keystrokes are stored in `minibuffer` until enter or esc is pressed.
    MiniBuffer
}

pub struct State<'a> {
    /// Position of the cursor in the buffer.
    /// This is *not* the cursor position on the screen.
    /// See `View` for more details on this.
    pub cursor: Point,
    /// Width of the whole window in the terminal.
    pub width: usize,
    /// Height of the whole window in the terminal.
    pub height: usize,
    /// Status message that will be displayed in the bottom bar.
    pub status: Option<String>,
    /// Recorded keystrokes. Used for compound actions like `dd`.
    pub keystrokes: Vec<Key>,
    /// Current mode type.
    pub mode_type: ModeType,
    /// Current mode.
    pub mode: Box<Mode + 'a>,
    /// The content of the minibuffer. Empty string if none.
    pub minibuffer: String,
    /// Used for instance when entering data in the minibuffer.
    pub microstate: MicroState,

    pub buffers: Vec<Rc<RefCell<Buffer>>>,
    pub buffer: Rc<RefCell<Buffer>>,
    buffer_idx: usize,

    yanked: VecDeque<String>,
    previous_action: Option<Action>,
}

impl<'a> State<'a> {
    pub fn new(width: usize, height: usize) -> State<'a> {
        let buffer = Buffer::new();
        let mut buffers = Vec::new();
        buffers.push(Rc::new(RefCell::new(buffer)));

        State {
            cursor: Point::new(0, 0),
            buffer: buffers[0].clone(),
            buffers: buffers,
            buffer_idx: 0,
            width: width,
            height: height,
            status: None,
            mode_type: ModeType::Normal,
            mode: Box::new(NormalMode::new()),
            keystrokes: Vec::new(),
            minibuffer: String::new(),
            microstate: MicroState::Mode,
            yanked: VecDeque::new(),
            previous_action: None,
        }
    }

    pub fn handle_key(&mut self, key: rustbox::Key) -> bool {
        self.status = None;

        if self.microstate == MicroState::MiniBuffer {
            match key {
                Key::Char(c) => {
                    self.minibuffer.push(c);
                    return false
                }
                Key::Enter => {
                    return self.handle_minibuffer_command()
                }
                Key::Esc => {
                    self.microstate = MicroState::Mode;
                    self.minibuffer = String::new();
                    return false
                }
                _ => { return false }
            }
        }

        match key {
            Key::Char(':') if self.microstate == MicroState::Mode => {
                self.microstate = MicroState::MiniBuffer;
                false
            }
            _ => {
                self.keystrokes.push(key);
                match self.mode.keys_pressed(self.keystrokes.as_slice()) {
                    Some(Action::PartialKey) => false,
                    Some(action) => self.execute_action(action),
                    None => { self.keystrokes = Vec::new(); false }
                }
            }
        }

    }

    fn execute_action(&mut self, action: Action) -> bool {
        match action {
            Action::RepeatPrevious => {
                if let Some(action) = self.previous_action.clone() {
                    self.execute_action(action);
                }
            }
            Action::NewLineAtPoint => {
                self.buffer.borrow_mut().split_line(self.cursor);
                self.move_cursor(Down);
                self.move_cursor(BeginningOfLine);
            }
            Action::NewLine => {
                self.buffer.borrow_mut().new_line(self.cursor);
                self.move_cursor(Down);
                self.move_cursor(BeginningOfLine);
                self.switch_mode(ModeType::Insert);
            }
            Action::Insert(c) => {
                self.buffer.borrow_mut().insert(self.cursor, c);
                self.move_cursor(Right);
            }
            Action::Replace(c) => {
                self.buffer.borrow_mut().upsert(self.cursor, c);
                self.move_cursor(Right);
            }
            Action::Delete => {
                let character = self.buffer.borrow_mut().delete(self.cursor);
                self.yanked.push_front(character.to_string());
            }
            Action::DeleteLine => {
                let line = self.buffer.borrow_mut().delete_line(self.cursor);
                self.yanked.push_front(line);
                self.move_cursor(BeginningOfLine);
            }
            Action::BackwardDelete => {
                self.move_cursor(Left);
                self.buffer.borrow_mut().delete(self.cursor);
            }
            Action::Paste => {
                if self.yanked.is_empty() {
                    self.status = Some("Nothing to paste!".to_string());
                } else {
                    self.paste();
                }
            }
            Action::MoveCursor(direction) => {
                self.move_cursor(direction);
            }
            Action::ChangeMode(mode_type) => {
                self.switch_mode(mode_type);
            }
            Action::Cancel => {
                self.keystrokes = Vec::new();
                self.execute_action(Action::ChangeMode(ModeType::Normal));
            }
            Action::Save => {
                let bytes = self.buffer.borrow_mut().save_file();
                if bytes > 0 {
                    let path = self.buffer.borrow_mut().filepath.clone().unwrap(); // We know the filepath is set
                    let status = format!("Saved \"{}\" ({} bytes)", path, bytes);
                    self.status = Some(status);
                }
            }
            Action::YankLine => {
                if let Some(line) = self.buffer.borrow_mut().line_at(self.cursor.y) {
                    self.yanked.push_front(line.clone());
                }
            }
            Action::NewBuffer => {
                let buffer = Buffer::new();
                self.buffer = Rc::new(RefCell::new(buffer));
                self.buffers.push(self.buffer.clone());
                self.buffer_idx += 1;
            }
            Action::NextBuffer => {
                self.buffer_idx = (self.buffer_idx + 1) % self.buffers.len();
                self.buffer = self.buffers[self.buffer_idx].clone();
            }
            Action::Multi(ref actions) => {
                for action in actions { self.execute_action(action.clone()); }
            }
            Action::Repeat(ref action, times) => {
                for _ in 0..times { self.execute_action(*action.clone()); }
            }
            Action::Quit => { return true },
            _ => {},
        }

        if action != Action::RepeatPrevious && action != Action::Cancel {
            self.previous_action = Some(action);
        }

        self.keystrokes = Vec::new();
        false
    }

    fn switch_mode(&mut self, mode_type: ModeType) {
        if let Some(action) = self.mode.on_exit() {
            self.execute_action(action);
        }

        self.mode_type = mode_type;
        self.mode = match mode_type {
            ModeType::Insert =>  Box::new(InsertMode::new()) as Box<Mode>,
            ModeType::Normal =>  Box::new(NormalMode::new()) as Box<Mode>,
            ModeType::Replace => Box::new(ReplaceMode::new()) as Box<Mode>,
        };
    }

    fn move_cursor(&mut self, direction: Direction) {
        let mut cur = self.cursor.with_direction(direction);

        match direction {
            EndOfLine => { cur.x = usize::max_value() } // This is so ugly...
            EndOfFile => { cur.y = self.buffer.borrow_mut().line_len() - 1 }
            _ => {}
        }

        let max_x = self.buffer.borrow_mut().last_non_empty_col(cur);
        let max_y = self.buffer.borrow_mut().line_len() - 1;
        cur.clamp_by(max_x, max_y);

        self.cursor = cur;
    }

    fn paste(&mut self) {
        let mut yanked = self.yanked.front().unwrap().clone();

        // Pasting a new line
        if let Some(_) = yanked.rfind('\n') {
            self.buffer.borrow_mut().new_line(self.cursor);
            self.move_cursor(Down);
            self.move_cursor(BeginningOfLine);
            yanked.pop(); // remove the last \n
        } else {
            self.move_cursor(Right);
        }

        self.buffer.borrow_mut().insert_text(self.cursor, yanked);
    }

    fn handle_minibuffer_command(&mut self) -> bool {
        let result = match self.minibuffer.as_ref() {
            "w" => self.execute_action(Action::Save),
            "q" => self.execute_action(Action::Quit),
            "new" => self.execute_action(Action::NewBuffer),
            "bn" => self.execute_action(Action::NextBuffer),
            _ => {
                self.status = Some(format!("Not a valid command: {}", self.minibuffer));
                false
            }
        };

        self.microstate = MicroState::Mode;
        self.minibuffer = String::new();
        result
    }
}
<commit_msg>Allow backspace in minibuffer<commit_after>extern crate rustbox;

use std::rc::Rc;
use std::cell::RefCell;
use std::collections::VecDeque;
use std::usize;
use rustbox::Key;
use buffer::Buffer;
use mode::{Mode,ModeType,NormalMode,InsertMode,ReplaceMode};
use point::{Direction,Point};
use point::Direction::*;

#[derive(Eq,PartialEq,Debug,Clone)]
pub enum Action {
    BackwardDelete,
    Cancel,
    ChangeMode(ModeType),
    Delete,
    DeleteLine,
    Insert(char),
    NewLine,
    NewLineAtPoint,
    MoveCursor(Direction),
    NewBuffer,
    NextBuffer,
    /// Used when in the middle of key sequence such as `yy`.
    /// See `keystrokes`.
    PartialKey,
    Paste,
    RepeatPrevious,
    Replace(char),
    Save,
    Quit,
    YankLine,
    /// Multiple actions in a row
    Multi(Vec<Action>),
    /// The same action `n` times
    Repeat(Box<Action>, usize)
}

#[derive(Eq,PartialEq,Debug,Clone)]
pub enum MicroState {
    /// The default microstate. Most events are delegated to the current mode.
    Mode,
    /// When entering data in the minibuffer.
    /// Keystrokes are stored in `minibuffer` until enter or esc is pressed.
    MiniBuffer
}

pub struct State<'a> {
    /// Position of the cursor in the buffer.
    /// This is *not* the cursor position on the screen.
    /// See `View` for more details on this.
    pub cursor: Point,
    /// Width of the whole window in the terminal.
    pub width: usize,
    /// Height of the whole window in the terminal.
    pub height: usize,
    /// Status message that will be displayed in the bottom bar.
    pub status: Option<String>,
    /// Recorded keystrokes. Used for compound actions like `dd`.
    pub keystrokes: Vec<Key>,
    /// Current mode type.
    pub mode_type: ModeType,
    /// Current mode.
    pub mode: Box<Mode + 'a>,
    /// The content of the minibuffer. Empty string if none.
    pub minibuffer: String,
    /// Used for instance when entering data in the minibuffer.
    pub microstate: MicroState,

    pub buffers: Vec<Rc<RefCell<Buffer>>>,
    pub buffer: Rc<RefCell<Buffer>>,
    buffer_idx: usize,

    yanked: VecDeque<String>,
    previous_action: Option<Action>,
}

impl<'a> State<'a> {
    pub fn new(width: usize, height: usize) -> State<'a> {
        let buffer = Buffer::new();
        let mut buffers = Vec::new();
        buffers.push(Rc::new(RefCell::new(buffer)));

        State {
            cursor: Point::new(0, 0),
            buffer: buffers[0].clone(),
            buffers: buffers,
            buffer_idx: 0,
            width: width,
            height: height,
            status: None,
            mode_type: ModeType::Normal,
            mode: Box::new(NormalMode::new()),
            keystrokes: Vec::new(),
            minibuffer: String::new(),
            microstate: MicroState::Mode,
            yanked: VecDeque::new(),
            previous_action: None,
        }
    }

    pub fn handle_key(&mut self, key: rustbox::Key) -> bool {
        self.status = None;

        if self.microstate == MicroState::MiniBuffer {
            match key {
                Key::Char(c) => {
                    self.minibuffer.push(c);
                    return false
                }
                Key::Backspace => {
                    self.minibuffer.pop();
                    return false
                }
                Key::Enter => {
                    return self.handle_minibuffer_command()
                }
                Key::Esc => {
                    self.microstate = MicroState::Mode;
                    self.minibuffer = String::new();
                    return false
                }
                _ => { return false }
            }
        }

        match key {
            Key::Char(':') if self.microstate == MicroState::Mode => {
                self.microstate = MicroState::MiniBuffer;
                false
            }
            _ => {
                self.keystrokes.push(key);
                match self.mode.keys_pressed(self.keystrokes.as_slice()) {
                    Some(Action::PartialKey) => false,
                    Some(action) => self.execute_action(action),
                    None => { self.keystrokes = Vec::new(); false }
                }
            }
        }

    }

    fn execute_action(&mut self, action: Action) -> bool {
        match action {
            Action::RepeatPrevious => {
                if let Some(action) = self.previous_action.clone() {
                    self.execute_action(action);
                }
            }
            Action::NewLineAtPoint => {
                self.buffer.borrow_mut().split_line(self.cursor);
                self.move_cursor(Down);
                self.move_cursor(BeginningOfLine);
            }
            Action::NewLine => {
                self.buffer.borrow_mut().new_line(self.cursor);
                self.move_cursor(Down);
                self.move_cursor(BeginningOfLine);
                self.switch_mode(ModeType::Insert);
            }
            Action::Insert(c) => {
                self.buffer.borrow_mut().insert(self.cursor, c);
                self.move_cursor(Right);
            }
            Action::Replace(c) => {
                self.buffer.borrow_mut().upsert(self.cursor, c);
                self.move_cursor(Right);
            }
            Action::Delete => {
                let character = self.buffer.borrow_mut().delete(self.cursor);
                self.yanked.push_front(character.to_string());
            }
            Action::DeleteLine => {
                let line = self.buffer.borrow_mut().delete_line(self.cursor);
                self.yanked.push_front(line);
                self.move_cursor(BeginningOfLine);
            }
            Action::BackwardDelete => {
                self.move_cursor(Left);
                self.buffer.borrow_mut().delete(self.cursor);
            }
            Action::Paste => {
                if self.yanked.is_empty() {
                    self.status = Some("Nothing to paste!".to_string());
                } else {
                    self.paste();
                }
            }
            Action::MoveCursor(direction) => {
                self.move_cursor(direction);
            }
            Action::ChangeMode(mode_type) => {
                self.switch_mode(mode_type);
            }
            Action::Cancel => {
                self.keystrokes = Vec::new();
                self.execute_action(Action::ChangeMode(ModeType::Normal));
            }
            Action::Save => {
                let bytes = self.buffer.borrow_mut().save_file();
                if bytes > 0 {
                    let path = self.buffer.borrow_mut().filepath.clone().unwrap(); // We know the filepath is set
                    let status = format!("Saved \"{}\" ({} bytes)", path, bytes);
                    self.status = Some(status);
                }
            }
            Action::YankLine => {
                if let Some(line) = self.buffer.borrow_mut().line_at(self.cursor.y) {
                    self.yanked.push_front(line.clone());
                }
            }
            Action::NewBuffer => {
                let buffer = Buffer::new();
                self.buffer = Rc::new(RefCell::new(buffer));
                self.buffers.push(self.buffer.clone());
                self.buffer_idx += 1;
            }
            Action::NextBuffer => {
                self.buffer_idx = (self.buffer_idx + 1) % self.buffers.len();
                self.buffer = self.buffers[self.buffer_idx].clone();
            }
            Action::Multi(ref actions) => {
                for action in actions { self.execute_action(action.clone()); }
            }
            Action::Repeat(ref action, times) => {
                for _ in 0..times { self.execute_action(*action.clone()); }
            }
            Action::Quit => { return true },
            _ => {},
        }

        if action != Action::RepeatPrevious && action != Action::Cancel {
            self.previous_action = Some(action);
        }

        self.keystrokes = Vec::new();
        false
    }

    fn switch_mode(&mut self, mode_type: ModeType) {
        if let Some(action) = self.mode.on_exit() {
            self.execute_action(action);
        }

        self.mode_type = mode_type;
        self.mode = match mode_type {
            ModeType::Insert =>  Box::new(InsertMode::new()) as Box<Mode>,
            ModeType::Normal =>  Box::new(NormalMode::new()) as Box<Mode>,
            ModeType::Replace => Box::new(ReplaceMode::new()) as Box<Mode>,
        };
    }

    fn move_cursor(&mut self, direction: Direction) {
        let mut cur = self.cursor.with_direction(direction);

        match direction {
            EndOfLine => { cur.x = usize::max_value() } // This is so ugly...
            EndOfFile => { cur.y = self.buffer.borrow_mut().line_len() - 1 }
            _ => {}
        }

        let max_x = self.buffer.borrow_mut().last_non_empty_col(cur);
        let max_y = self.buffer.borrow_mut().line_len() - 1;
        cur.clamp_by(max_x, max_y);

        self.cursor = cur;
    }

    fn paste(&mut self) {
        let mut yanked = self.yanked.front().unwrap().clone();

        // Pasting a new line
        if let Some(_) = yanked.rfind('\n') {
            self.buffer.borrow_mut().new_line(self.cursor);
            self.move_cursor(Down);
            self.move_cursor(BeginningOfLine);
            yanked.pop(); // remove the last \n
        } else {
            self.move_cursor(Right);
        }

        self.buffer.borrow_mut().insert_text(self.cursor, yanked);
    }

    fn handle_minibuffer_command(&mut self) -> bool {
        let result = match self.minibuffer.as_ref() {
            "w" => self.execute_action(Action::Save),
            "q" => self.execute_action(Action::Quit),
            "new" => self.execute_action(Action::NewBuffer),
            "bn" => self.execute_action(Action::NextBuffer),
            _ => {
                self.status = Some(format!("Not a valid command: {}", self.minibuffer));
                false
            }
        };

        self.microstate = MicroState::Mode;
        self.minibuffer = String::new();
        result
    }
}
<|endoftext|>


<commit_before>use term_painter::ToStyle;
use term_painter::Color::*;
use std::io;

mod types;

// Game logic (board, initialization, valid move, set, play or finished)

/// Visualization of the boards.
fn print_boards(board1: &Vec<types::SubField>, board2: &Vec<types::SubField>) {
    println!("{}{}", "------------------ O W N   B O A R D ------------------",
        "------------------------- O P P O N E N T ----------------");

    let mut cnt = 9;

    for i in 0..10 {
        println!("");
        print!("{}  ", cnt);
        for j in 0..10 {
            if board1[j + i * 10] == types::SubField::Hit {
                print!(" {} ", Red.paint(board1[j + i * 10]));
            } else {
                print!(" {} ", board1[j + i * 10]);
            }
        }
        print!("        ");
        print!("{}  ", cnt);
        for k in 0..10 {
            if board2[k + i * 10] == types::SubField::Hit {
                print!(" {} ", Green.paint(board2[k + i * 10]));
            } else {
                print!(" {} ", board2[k + i * 10]);
            }
        }
        println!("");
        cnt -= 1;
    }
    println!("");
    println!("{}{}", "     A    B    C    D    E    F    G    H    I    J",
        "               A    B    C    D    E    F    G    H    I    J");
    println!("");
    println!("{}{}", "----------------------------------------------------",
        "--------------------------------------------------------------");
}

/// Reads a string from the terminal / user.
fn read_string() -> String {
    let mut buffer = String::new();
    io::stdin()
        .read_line(&mut buffer)
        .expect("something went horribly wrong...");

    /* Discard trailing newline */
    let new_len = buffer.trim_right().len();
    buffer.truncate(new_len);

    buffer
}

/// The actual placement of the ships.
fn place(player: &mut types::Player, ship: types::Ship) -> u8 {

    /* Vector to collect the indices for the possible ship position. */
    let mut indices = Vec::new();

    /* 100 --> invalid value */
    let mut input = 100;
    while input == 100 {
        input = get_input();
        match player.own_board[input] {
            types::SubField::Water => { indices.push(input); },
            _ => {
                println!("Invalid input, again please.");
                input = 100;
            },
        }
    }

    println!("{}{}", "Enter 'h' for a horizontal (upwards) orientation of the ship,",
        "'v' for a vertical (rightwards) one.");
    /* x --> invalid value */
    let mut orientation = "x".to_string();
    while orientation == "x" {
        orientation = read_string();
        match orientation.as_ref() {
            "h" => { orientation = "h".to_string(); },
            "v" => { orientation = "v".to_string(); },
            _ => {
                println!("Invalid input, again please.");
                orientation = "x".to_string();
            }
        }
    }

    /* Determines the length of the current ship. */
    let len = match ship {
        types::Ship::Carrier => 5,
        types::Ship::Battleship => 4,
        types::Ship::Cruiser => 3,
        types::Ship::Submarine => 3,
        types::Ship::Destroyer => 2,
    };

    if orientation == "v" {
        for _ in 0..len - 1 {
            /* Check to prevent out of bounds errors. */
            if input >= 10 {
                input -= 10;
            }
            if player.own_board[input] != types::SubField::Water
                /* input < 10 --> no field above */
                || input < 10  {
                    println!("Invalid position for this ship.");
                    println!("Please choose another coordinate.");
                    return 1
            }
            indices.push(input);
        }
    } else {
        for _ in 0..len - 1 {
            /* Check to prevent out of bounds errors. */
            if input < 99 {
                input += 1;
            }
            if player.own_board[input] != types::SubField::Water
                /* unit rank == 9 --> no field to the right */
                || (input % 10) == 9 {
                    println!("Invalid position for this ship.");
                    println!("Please choose another coordinate.");
                    return 1
            }
            indices.push(input);
        }
    }

    /* Places the ships on the board. */
    for i in indices.iter() {
        player.own_board[*i] = types::SubField::Ship;
    }

    0
}

/// Handles the initial ship placement for each player.
fn place_ships(mut player1: &mut types::Player, mut player2: &mut types::Player) {

    /* A vector of all the ships each player needs to place. */
    let mut ships = Vec::new();
    ships.push(types::Ship::Carrier);
    ships.push(types::Ship::Battleship);
    ships.push(types::Ship::Battleship);
    ships.push(types::Ship::Cruiser);
    ships.push(types::Ship::Cruiser);
    ships.push(types::Ship::Cruiser);
    ships.push(types::Ship::Submarine);
    ships.push(types::Ship::Submarine);
    ships.push(types::Ship::Submarine);
    ships.push(types::Ship::Submarine);
    ships.push(types::Ship::Destroyer);
    ships.push(types::Ship::Destroyer);
    ships.push(types::Ship::Destroyer);
    ships.push(types::Ship::Destroyer);
    ships.push(types::Ship::Destroyer);

    print_boards(&player1.own_board, &player1.op_board);

    /* Error code, should be replaced by proper error handling later. */
    let mut err;

    /* Asks player1 to place the ships. */
    for i in ships.iter() {
        err = 1;
        while err == 1 {
            println!("Player1, please enter the first coordinate for your {:?}.", i);
            err = place(&mut player1, *i);
            print_boards(&player1.own_board, &player1.op_board);
        }
    }

    /* Asks player2 to place the ships. */
    for i in ships.iter() {
        err = 1;
        while err == 1 {
            println!("Player2, please enter the first coordinate for your {:?}.", i);
            err = place(&mut player2, *i);
            print_boards(&player2.own_board, &player2.op_board);
        }
    }
}

/// Reads the coordinates of a field from the user
/// and returns the corresponding index.
fn get_input() -> usize {

    /* 100 --> invalid */
    let mut input = 100;

    while input == 100 {
        input = match read_string().as_ref() {
            "A0" => 90, "A1" => 80,"A2" => 70, "A3" => 60, "A4" => 50,
            "A5" => 40, "A6" => 30, "A7" => 20, "A8" => 10, "A9" => 0,
            "B0" => 91, "B1" => 81, "B2" => 71, "B3" => 61, "B4" => 51,
            "B5" => 41, "B6" => 31, "B7" => 21, "B8" => 11, "B9" => 1,
            "C0" => 92, "C1" => 82, "C2" => 72, "C3" => 62, "C4" => 52,
            "C5" => 42, "C6" => 32, "C7" => 22, "C8" => 12, "C9" => 2,
            "D0" => 93, "D1" => 83, "D2" => 73, "D3" => 63, "D4" => 53,
            "D5" => 43, "D6" => 33, "D7" => 23, "D8" => 13, "D9" => 3,
            "E0" => 94, "E1" => 84, "E2" => 74, "E3" => 64, "E4" => 54,
            "E5" => 44, "E6" => 34, "E7" => 24, "E8" => 14, "E9" => 4,
            "F0" => 95, "F1" => 85, "F2" => 75, "F3" => 65, "F4" => 55,
            "F5" => 45, "F6" => 35, "F7" => 25, "F8" => 15, "F9" => 5,
            "G0" => 96, "G1" => 86, "G2" => 76, "G3" => 66, "G4" => 56,
            "G5" => 46, "G6" => 36, "G7" => 26, "G8" => 16, "G9" => 6,
            "H0" => 97, "H1" => 87, "H2" => 77, "H3" => 67, "H4" => 57,
            "H5" => 47, "H6" => 37, "H7" => 27, "H8" => 17, "H9" => 7,
            "I0" => 98, "I1" => 88, "I2" => 78, "I3" => 68, "I4" => 58,
            "I5" => 48, "I6" => 38, "I7" => 28, "I8" => 18, "I9" => 8,
            "J0" => 99, "J1" => 89, "J2" => 79, "J3" => 69, "J4" => 59,
            "J5" => 49, "J6" => 39, "J7" => 29, "J8" => 19, "J9" => 9,
            _ => 100,
        };

        if input == 100 {
            println!("Invalid field, try again.")
        }
    }
    input
}

fn match_move(first: &mut types::Player, second: &mut types::Player, idx: usize) {

    match second.own_board[idx] {
        types::SubField::Water => {
            println!("Miss - try again.");
            first.op_board[idx] = types::SubField::WaterHit;
        }
        types::SubField::Ship => {
            println!("Hit!");
            first.op_board[idx] = types::SubField::Hit;
            second.own_board[idx] = types::SubField::Hit;
            second.capacity -= 1;
        },
        types::SubField::Hit => { println!("Already hit."); }
        types::SubField::WaterHit => { println!("Miss - try again."); }
    }
}

/// Lets the players perform their moves.
fn make_move(mut first: &mut types::Player, mut second: &mut types::Player) {
    println!("Enter coordinates, {}:", first.name);
    let input = get_input();
    match_move(&mut first, &mut second, input);
}

/// Returns whether all the player's ships got destroyed.
pub fn game_over(player: &types::Player) -> bool {
    player.capacity <= 0
}

pub fn start_match() {

    /* Creates the initial (empty) boards (10 x 10) for player1 and player2. */
    let mut player1 = types::Player {
        own_board: vec![types::SubField::Water; 100],
        op_board: vec![types::SubField::Water; 100],
        capacity: 44,
        name: "Player1".to_string(),
    };

    let mut player2 = types::Player {
        own_board: vec![types::SubField::Water; 100],
        op_board: vec![types::SubField::Water; 100],
        capacity: 44,
        name: "Player2".to_string(),
    };

    place_ships(&mut player1, &mut player2);

    loop {
        print_boards(&player1.own_board, &player1.op_board);
        make_move(&mut player1, &mut player2);
        if game_over(&player2) {
            println!("G A M E   O V E R");
            println!("Congratulations, Player1");
            break;
        }

        print_boards(&player2.own_board, &player2.op_board);
        make_move(&mut player2, &mut player1);
        if game_over(&player1) {
            println!("G A M E   O V E R");
            println!("Congratulations, Player2");
            break;
        }
    }
}
<commit_msg>minor changes<commit_after>use term_painter::ToStyle;
use term_painter::Color::*;
use std::io;

mod types;

// Game logic (board, initialization, valid move, set, play or finished)

/// Visualization of the boards.
fn print_boards(board1: &Vec<types::SubField>, board2: &Vec<types::SubField>) {
    println!("{}{}", "------------------ O W N   B O A R D ------------------",
        "------------------------- O P P O N E N T ----------------");

    let mut cnt = 9;

    for i in 0..10 {
        println!("");
        print!("{}  ", cnt);
        for j in 0..10 {
            if board1[j + i * 10] == types::SubField::Hit {
                print!(" {} ", Red.paint(board1[j + i * 10]));
            } else {
                print!(" {} ", board1[j + i * 10]);
            }
        }
        print!("        ");
        print!("{}  ", cnt);
        for j in 0..10 {
            if board2[j + i * 10] == types::SubField::Hit {
                print!(" {} ", Green.paint(board2[j + i * 10]));
            } else {
                print!(" {} ", board2[j + i * 10]);
            }
        }
        println!("");
        cnt -= 1;
    }
    println!("");
    println!("{}{}", "     A    B    C    D    E    F    G    H    I    J",
        "               A    B    C    D    E    F    G    H    I    J");
    println!("");
    println!("{}{}", "----------------------------------------------------",
        "--------------------------------------------------------------");
}

/// Reads a string from the terminal / user.
fn read_string() -> String {
    let mut buffer = String::new();
    io::stdin()
        .read_line(&mut buffer)
        .expect("something went horribly wrong...");

    /* Discard trailing newline */
    let new_len = buffer.trim_right().len();
    buffer.truncate(new_len);

    buffer
}

/// The actual placement of the ships.
fn place(player: &mut types::Player, ship: types::Ship) -> u8 {

    /* Vector to collect the indices for the possible ship position. */
    let mut indices = Vec::new();

    /* 100 --> invalid value */
    let mut input = 100;
    while input == 100 {
        input = get_input();
        match player.own_board[input] {
            types::SubField::Water => { indices.push(input); },
            _ => {
                println!("Invalid input, again please.");
                input = 100;
            },
        }
    }

    println!("{}{}", "Enter 'h' for a horizontal (upwards) orientation of the ship,",
        "'v' for a vertical (rightwards) one.");
    /* x --> invalid value */
    let mut orientation = "x".to_string();
    while orientation == "x" {
        orientation = read_string();
        match orientation.as_ref() {
            "h" => {},
            "v" => {},
            _ => {
                println!("Invalid input, again please.");
                orientation = "x".to_string();
            }
        }
    }

    /* Determines the length of the current ship. */
    let len = match ship {
        types::Ship::Carrier => 5,
        types::Ship::Battleship => 4,
        types::Ship::Cruiser => 3,
        types::Ship::Submarine => 3,
        types::Ship::Destroyer => 2,
    };

    if orientation == "v" {
        for _ in 0..len - 1 {
            /* Check to prevent out of bounds errors. */
            if input >= 10 {
                input -= 10;
            }
            if player.own_board[input] != types::SubField::Water
                /* input < 10 --> no field above */
                || input < 10 {
                    println!("Invalid position for this ship.");
                    println!("Please choose another coordinate.");
                    return 1
            }
            indices.push(input);
        }
    } else {
        for _ in 0..len - 1 {
            /* Check to prevent out of bounds errors. */
            if input < 99 {
                input += 1;
            }
            if player.own_board[input] != types::SubField::Water
                /* unit position == 9 --> no field to the right */
                || (input % 10) == 9 {
                    println!("Invalid position for this ship.");
                    println!("Please choose another coordinate.");
                    return 1
            }
            indices.push(input);
        }
    }

    /* Places the ships on the board. */
    for i in indices.iter() {
        player.own_board[*i] = types::SubField::Ship;
    }

    0
}

/// Handles the initial ship placement for each player.
fn place_ships(mut player1: &mut types::Player, mut player2: &mut types::Player) {

    /* A vector of all the ships each player needs to place.
       The default version: #  Class of ship Size
                            1   Carrier       5
                            2   Battleship    4
                            3   Cruiser       3
                            4   Submarine     3
                            5   Destroyer     2
    */
    let ships = vec![types::Ship::Carrier, types::Ship::Battleship,
        types::Ship::Battleship, types::Ship::Cruiser, types::Ship::Cruiser,
        types::Ship::Cruiser, types::Ship::Submarine, types::Ship::Submarine,
        types::Ship::Submarine, types::Ship::Submarine, types::Ship::Destroyer,
        types::Ship::Destroyer, types::Ship::Destroyer, types::Ship::Destroyer,
        types::Ship::Destroyer];

    print_boards(&player1.own_board, &player1.op_board);

    /* Error code, should be replaced by proper error handling later. */
    let mut err;

    /* Asks player1 to place the ships. */
    for i in ships.iter() {
        err = 1;
        while err == 1 {
            println!("Player1, please enter the first coordinate for your {:?}.", i);
            err = place(&mut player1, *i);
            print_boards(&player1.own_board, &player1.op_board);
        }
    }

    /* Asks player2 to place the ships. */
    for i in ships.iter() {
        err = 1;
        while err == 1 {
            println!("Player2, please enter the first coordinate for your {:?}.", i);
            err = place(&mut player2, *i);
            print_boards(&player2.own_board, &player2.op_board);
        }
    }
}

/// Reads the coordinates of a field from the user
/// and returns the corresponding index.
fn get_input() -> usize {

    /* 100 --> invalid */
    let mut input = 100;

    while input == 100 {
        input = match read_string().as_ref() {
            "A0" => 90, "A1" => 80,"A2" => 70, "A3" => 60, "A4" => 50,
            "A5" => 40, "A6" => 30, "A7" => 20, "A8" => 10, "A9" => 0,
            "B0" => 91, "B1" => 81, "B2" => 71, "B3" => 61, "B4" => 51,
            "B5" => 41, "B6" => 31, "B7" => 21, "B8" => 11, "B9" => 1,
            "C0" => 92, "C1" => 82, "C2" => 72, "C3" => 62, "C4" => 52,
            "C5" => 42, "C6" => 32, "C7" => 22, "C8" => 12, "C9" => 2,
            "D0" => 93, "D1" => 83, "D2" => 73, "D3" => 63, "D4" => 53,
            "D5" => 43, "D6" => 33, "D7" => 23, "D8" => 13, "D9" => 3,
            "E0" => 94, "E1" => 84, "E2" => 74, "E3" => 64, "E4" => 54,
            "E5" => 44, "E6" => 34, "E7" => 24, "E8" => 14, "E9" => 4,
            "F0" => 95, "F1" => 85, "F2" => 75, "F3" => 65, "F4" => 55,
            "F5" => 45, "F6" => 35, "F7" => 25, "F8" => 15, "F9" => 5,
            "G0" => 96, "G1" => 86, "G2" => 76, "G3" => 66, "G4" => 56,
            "G5" => 46, "G6" => 36, "G7" => 26, "G8" => 16, "G9" => 6,
            "H0" => 97, "H1" => 87, "H2" => 77, "H3" => 67, "H4" => 57,
            "H5" => 47, "H6" => 37, "H7" => 27, "H8" => 17, "H9" => 7,
            "I0" => 98, "I1" => 88, "I2" => 78, "I3" => 68, "I4" => 58,
            "I5" => 48, "I6" => 38, "I7" => 28, "I8" => 18, "I9" => 8,
            "J0" => 99, "J1" => 89, "J2" => 79, "J3" => 69, "J4" => 59,
            "J5" => 49, "J6" => 39, "J7" => 29, "J8" => 19, "J9" => 9,
            _ => 100,
        };

        if input == 100 {
            println!("Invalid field, try again.")
        }
    }
    input
}

fn match_move(first: &mut types::Player, second: &mut types::Player, idx: usize) {

    match second.own_board[idx] {
        types::SubField::Water => {
            println!("Miss - try again.");
            first.op_board[idx] = types::SubField::WaterHit;
        }
        types::SubField::Ship => {
            println!("Hit!");
            first.op_board[idx] = types::SubField::Hit;
            second.own_board[idx] = types::SubField::Hit;
            second.capacity -= 1;
        },
        types::SubField::Hit => { println!("Already hit."); }
        types::SubField::WaterHit => { println!("Miss - try again."); }
    }
}

/// Lets the players perform their moves.
fn make_move(mut first: &mut types::Player, mut second: &mut types::Player) {
    println!("Enter coordinates, {}:", first.name);
    let input = get_input();
    match_move(&mut first, &mut second, input);
}

/// Returns whether all the player's ships got destroyed.
pub fn game_over(player: &types::Player) -> bool {
    player.capacity <= 0
}

pub fn start_match() {

    /* Creates the initial (empty) boards (10 x 10) for player1 and player2. */
    let mut player1 = types::Player {
        own_board: vec![types::SubField::Water; 100],
        op_board: vec![types::SubField::Water; 100],
        capacity: 44,
        name: "Player1".to_string(),
    };

    let mut player2 = types::Player {
        own_board: vec![types::SubField::Water; 100],
        op_board: vec![types::SubField::Water; 100],
        capacity: 44,
        name: "Player2".to_string(),
    };

    place_ships(&mut player1, &mut player2);

    loop {
        print_boards(&player1.own_board, &player1.op_board);
        make_move(&mut player1, &mut player2);
        if game_over(&player2) {
            println!("G A M E   O V E R");
            println!("Congratulations, Player1");
            break;
        }

        print_boards(&player2.own_board, &player2.op_board);
        make_move(&mut player2, &mut player1);
        if game_over(&player1) {
            println!("G A M E   O V E R");
            println!("Congratulations, Player2");
            break;
        }
    }
}
<|endoftext|>


<commit_before>use bios_boot_device::{BIOSBootDeviceTag, BootDevice};
use boot_command_line::BootCommandLineTag;
use boot_loader_name::BootLoaderNameTag;
use memory_map::{MemoryMapTag, MemoryMapIter};
use tag::{TagType, Tag, TagIter};
use basic_memory_information::BasicMemoryInformationTag;

pub unsafe fn load(multiboot_addr: u32) -> &'static MultiBootInfo  {
    let multiboot_info = MultiBootInfo::from_raw_parts(multiboot_addr);
    assert!(multiboot_info.has_valid_end_tag());
    multiboot_info
}

#[derive(Debug)]
#[repr(C)]
pub struct MultiBootInfo {
    total_size: u32,
    reserved: u32,      // Should always be 0.
    first_tag: *const Tag
}

impl MultiBootInfo {
    unsafe fn from_raw_parts(multiboot_addr: u32) -> &'static MultiBootInfo {
        &*(multiboot_addr as *const MultiBootInfo)
    }

    pub fn start_address(&self) -> usize {
        self as *const _ as usize
    }

    pub fn end_address(&self) -> usize {
        self.start_address() + self.total_size()
    }

    pub fn total_size(&self) -> usize {
        self.total_size as usize
    }

    pub fn mem_lower(&self) -> Option<usize> {
        self.find_tag(TagType::MemoryInformation)
            .map(|tag_ptr| {
                unsafe {
                    tag_ptr.cast::<BasicMemoryInformationTag>()
                }
            })
            .map(|tag| { tag.mem_lower() })
    }

    pub fn mem_upper(&self) -> Option<usize> {
        self.find_tag(TagType::MemoryInformation)
            .map(|tag_ptr| {
                unsafe {
                    tag_ptr.cast::<BasicMemoryInformationTag>()
                }
            })
            .map(|tag| { tag.mem_upper() })
    }

    pub fn bios_boot_device(&self) -> Option<BootDevice> {
        self.find_tag(TagType::BIOSBootDevice)
            .map(|tag_ptr| {
                unsafe {
                    tag_ptr.cast::<BIOSBootDeviceTag>()
                }
            })
            .map(|tag| {
                BootDevice::new(tag.biosdev, tag.partition, tag.sub_partition)
            })
    }

    pub fn boot_cmd_line(&self) -> Option<&str> {
        self.find_tag(TagType::BootCommandLine)
            .map(|tag_ptr| {
                unsafe {
                    tag_ptr.cast::<BootCommandLineTag>()
                }
            })
            .map(|tag| { tag.string() })
    }

    pub fn boot_loader_name(&self) -> Option<&str> {
        self.find_tag(TagType::BootCommandLine)
            .map(|tag_ptr| {
                unsafe {
                    tag_ptr.cast::<BootLoaderNameTag>()
                }
            })
            .map(|tag| { tag.string() })
    }

    pub fn memory_map(&self) -> Option<MemoryMapIter> {
        self.find_tag(TagType::MemoryMap)
            .map(|tag_ptr| {
                unsafe {
                    tag_ptr.cast::<MemoryMapTag>()
                }
            })
            .map(|tag| {
                tag.memory_map()
            })
    }

    fn has_valid_end_tag(&self) -> bool {
        true
    }

    fn tags(&self) -> TagIter {
        TagIter::new(self.first_tag)
    }

    fn find_tag(&self, tag_type: TagType) -> Option<&'static Tag> {
        self.tags().find(|tag| { tag.tag_type() == tag_type as usize })
    }

}
<commit_msg>Refactor code to fix error with multiboot header.<commit_after>use bios_boot_device::{BIOSBootDeviceTag, BootDevice};
use boot_command_line::BootCommandLineTag;
use boot_loader_name::BootLoaderNameTag;
use memory_map::{MemoryMapTag, MemoryMapIter};
use tag::{TagType, Tag, TagIter};
use basic_memory_information::BasicMemoryInformationTag;

pub unsafe fn load(multiboot_addr: u32) -> &'static MultiBootInfo  {
    let multiboot_info = MultiBootInfo::from_raw_parts(multiboot_addr);
    assert!(multiboot_info.has_valid_end_tag());
    multiboot_info
}

#[derive(Debug)]
#[repr(C)]
pub struct MultiBootInfo {
    total_size: u32,
    reserved: u32,      // Should always be 0.
    first_tag: Tag
}

impl MultiBootInfo {
    unsafe fn from_raw_parts(multiboot_addr: u32) -> &'static MultiBootInfo {
        &*(multiboot_addr as *const MultiBootInfo)
    }

    pub fn start_address(&self) -> usize {
        self as *const _ as usize
    }

    pub fn end_address(&self) -> usize {
        self.start_address() + self.total_size()
    }

    pub fn total_size(&self) -> usize {
        self.total_size as usize
    }

    pub fn mem_lower(&self) -> Option<usize> {
        self.find_tag(TagType::MemoryInformation)
            .map(|tag_ptr| {
                unsafe {
                    tag_ptr.cast::<BasicMemoryInformationTag>()
                }
            })
            .map(|tag| { tag.mem_lower() })
    }

    pub fn mem_upper(&self) -> Option<usize> {
        self.find_tag(TagType::MemoryInformation)
            .map(|tag_ptr| {
                unsafe {
                    tag_ptr.cast::<BasicMemoryInformationTag>()
                }
            })
            .map(|tag| { tag.mem_upper() })
    }

    pub fn bios_boot_device(&self) -> Option<BootDevice> {
        self.find_tag(TagType::BIOSBootDevice)
            .map(|tag_ptr| {
                unsafe {
                    tag_ptr.cast::<BIOSBootDeviceTag>()
                }
            })
            .map(|tag| {
                BootDevice::new(tag.biosdev, tag.partition, tag.sub_partition)
            })
    }

    pub fn boot_cmd_line(&self) -> Option<&str> {
        self.find_tag(TagType::BootCommandLine)
            .map(|tag_ptr| {
                unsafe {
                    tag_ptr.cast::<BootCommandLineTag>()
                }
            })
            .map(|tag| { tag.string() })
    }

    pub fn boot_loader_name(&self) -> Option<&str> {
        self.find_tag(TagType::BootCommandLine)
            .map(|tag_ptr| {
                unsafe {
                    tag_ptr.cast::<BootLoaderNameTag>()
                }
            })
            .map(|tag| { tag.string() })
    }

    pub fn memory_map(&self) -> Option<MemoryMapIter> {
        self.find_tag(TagType::MemoryMap)
            .map(|tag_ptr| {
                unsafe {
                    tag_ptr.cast::<MemoryMapTag>()
                }
            })
            .map(|tag| {
                tag.memory_map()
            })
    }

    fn has_valid_end_tag(&self) -> bool {
        true
    }

    fn tags(&self) -> TagIter {
        TagIter::new(&self.first_tag)
    }

    fn find_tag(&self, tag_type: TagType) -> Option<&'static Tag> {
        self.tags().find(|tag| { tag.tag_type() == tag_type as usize })
    }

}
<|endoftext|>


<commit_before>//! Safe _handles_ to managed JavaScript memory.

pub(crate) mod internal;

use std::marker::PhantomData;
use std::ops::{Deref, DerefMut};
use std::error::Error;
use std::fmt::{self, Debug, Display};
use neon_runtime;
use neon_runtime::raw;
use types::Value;
use context::Context;
use context::internal::Env;
use result::{JsResult, JsResultExt};
use self::internal::SuperType;

/// The trait of data that is managed by the JS garbage collector and can only be accessed via handles.
pub trait Managed: Copy {
    fn to_raw(self) -> raw::Local;

    fn from_raw(h: raw::Local) -> Self;
}

/// A safely rooted _handle_ to a JS value in memory that is managed by the garbage collector.
#[repr(C)]
#[derive(Clone, Copy)]
pub struct Handle<'a, T: Managed + 'a> {
    value: T,
    phantom: PhantomData<&'a T>
}

impl<'a, T: Managed + 'a> PartialEq for Handle<'a, T> {
    fn eq(&self, other: &Self) -> bool {
        unsafe { neon_runtime::mem::same_handle(self.to_raw(), other.to_raw()) }
    }
}

impl<'a, T: Managed + 'a> Eq for Handle<'a, T> { }

impl<'a, T: Managed + 'a> Handle<'a, T> {
    pub(crate) fn new_internal(value: T) -> Handle<'a, T> {
        Handle {
            value: value,
            phantom: PhantomData
        }
    }
}

/// An error representing a failed downcast.
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone)]
pub struct DowncastError<F: Value, T: Value> {
    phantom_from: PhantomData<F>,
    phantom_to: PhantomData<T>,
    description: String
}

impl<F: Value, T: Value> Debug for DowncastError<F, T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f, "DowncastError")
    }
}

impl<F: Value, T: Value> DowncastError<F, T> {
    fn new() -> Self {
        DowncastError {
            phantom_from: PhantomData,
            phantom_to: PhantomData,
            description: format!("failed downcast to {}", T::name())
        }
    }
}

impl<F: Value, T: Value> Display for DowncastError<F, T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f, "{}", self.description())
    }
}

impl<F: Value, T: Value> Error for DowncastError<F, T> {
    fn description(&self) -> &str {
        &self.description
    }
}

/// The result of a call to `Handle::downcast()`.
pub type DowncastResult<'a, F, T> = Result<Handle<'a, T>, DowncastError<F, T>>;

impl<'a, F: Value, T: Value> JsResultExt<'a, T> for DowncastResult<'a, F, T> {
    fn or_throw<'b, C: Context<'b>>(self, cx: &mut C) -> JsResult<'a, T> {
        match self {
            Ok(v) => Ok(v),
            Err(e) => cx.throw_type_error(&e.description)
        }
    }
}

impl<'a, T: Value> Handle<'a, T> {

    /// Safely upcast a handle to a supertype.
    ///
    /// This method does not require an execution context because it only copies a handle.
    pub fn upcast<U: Value + SuperType<T>>(&self) -> Handle<'a, U> {
        Handle::new_internal(SuperType::upcast_internal(self.value))
    }

    #[cfg(feature = "legacy-runtime")]
    /// Tests whether this value is an instance of the given type.
    ///
    /// # Example:
    ///
    /// ```no_run
    /// # use neon::prelude::*;
    /// # fn my_neon_function(mut cx: FunctionContext) -> JsResult<JsUndefined> {
    /// let v: Handle<JsValue> = cx.number(17).upcast();
    /// v.is_a::<JsString>(); // false
    /// v.is_a::<JsNumber>(); // true
    /// v.is_a::<JsValue>();  // true
    /// # Ok(cx.undefined())
    /// # }
    /// ```
    pub fn is_a<U: Value>(&self) -> bool {
        U::is_typeof(Env::current(), self.value)
    }

    #[cfg(feature = "napi-runtime")]
    /// Tests whether this value is an instance of the given type.
    ///
    /// # Example:
    ///
    /// ```no_run
    /// # use neon::prelude::*;
    /// # fn my_neon_function(mut cx: FunctionContext) -> JsResult<JsUndefined> {
    /// let v: Handle<JsValue> = cx.number(17).upcast(&mut cx);
    /// v.is_a::<JsString>(&mut cx); // false
    /// v.is_a::<JsNumber>(&mut cx); // true
    /// v.is_a::<JsValue>(&mut cx);  // true
    /// # Ok(cx.undefined())
    /// # }
    /// ```
    pub fn is_a<'b, U: Value, C: Context<'b>>(&self, cx: &mut C) -> bool {
        U::is_typeof(cx.env(), self.value)
    }

    #[cfg(feature = "legacy-runtime")]
    /// Attempts to downcast a handle to another type, which may fail. A failure
    /// to downcast **does not** throw a JavaScript exception, so it's OK to
    /// continue interacting with the JS engine if this method produces an `Err`
    /// result.
    pub fn downcast<U: Value>(&self) -> DowncastResult<'a, T, U> {
        match U::downcast(Env::current(), self.value) {
            Some(v) => Ok(Handle::new_internal(v)),
            None => Err(DowncastError::new())
        }
    }

    #[cfg(feature = "napi-runtime")]
    /// Attempts to downcast a handle to another type, which may fail. A failure
    /// to downcast **does not** throw a JavaScript exception, so it's OK to
    /// continue interacting with the JS engine if this method produces an `Err`
    /// result.
    pub fn downcast<'b, U: Value, C: Context<'b>>(&self, cx: &mut C) -> DowncastResult<'a, T, U> {
        match U::downcast(cx.env(), self.value) {
            Some(v) => Ok(Handle::new_internal(v)),
            None => Err(DowncastError::new())
        }
    }

    #[cfg(feature = "legacy-runtime")]
    /// Attempts to downcast a handle to another type, raising a JavaScript `TypeError`
    /// exception on failure. This method is a convenient shorthand, equivalent to
    /// `self.downcast::<U>().or_throw::<C>(cx)`.
    pub fn downcast_or_throw<'b, U: Value, C: Context<'b>>(&self, cx: &mut C) -> JsResult<'a, U> {
        self.downcast().or_throw(cx)
    }

    #[cfg(feature = "napi-runtime")]
    /// Attempts to downcast a handle to another type, raising a JavaScript `TypeError`
    /// exception on failure. This method is a convenient shorthand, equivalent to
    /// `self.downcast::<U>().or_throw::<C>(cx)`.
    pub fn downcast_or_throw<'b, U: Value, C: Context<'b>>(&self, cx: &mut C) -> JsResult<'a, U> {
        self.downcast(cx).or_throw(cx)
    }

}

impl<'a, T: Managed> Deref for Handle<'a, T> {
    type Target = T;
    fn deref<'b>(&'b self) -> &'b T {
        &self.value
    }
}

impl<'a, T: Managed> DerefMut for Handle<'a, T> {
    fn deref_mut<'b>(&'b mut self) -> &'b mut T {
        &mut self.value
    }
}
<commit_msg>Only import Env if it is used<commit_after>//! Safe _handles_ to managed JavaScript memory.

pub(crate) mod internal;

use std::marker::PhantomData;
use std::ops::{Deref, DerefMut};
use std::error::Error;
use std::fmt::{self, Debug, Display};
use neon_runtime;
use neon_runtime::raw;
use types::Value;
use context::Context;
#[cfg(feature = "legacy-runtime")]
use context::internal::Env;
use result::{JsResult, JsResultExt};
use self::internal::SuperType;

/// The trait of data that is managed by the JS garbage collector and can only be accessed via handles.
pub trait Managed: Copy {
    fn to_raw(self) -> raw::Local;

    fn from_raw(h: raw::Local) -> Self;
}

/// A safely rooted _handle_ to a JS value in memory that is managed by the garbage collector.
#[repr(C)]
#[derive(Clone, Copy)]
pub struct Handle<'a, T: Managed + 'a> {
    value: T,
    phantom: PhantomData<&'a T>
}

impl<'a, T: Managed + 'a> PartialEq for Handle<'a, T> {
    fn eq(&self, other: &Self) -> bool {
        unsafe { neon_runtime::mem::same_handle(self.to_raw(), other.to_raw()) }
    }
}

impl<'a, T: Managed + 'a> Eq for Handle<'a, T> { }

impl<'a, T: Managed + 'a> Handle<'a, T> {
    pub(crate) fn new_internal(value: T) -> Handle<'a, T> {
        Handle {
            value: value,
            phantom: PhantomData
        }
    }
}

/// An error representing a failed downcast.
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone)]
pub struct DowncastError<F: Value, T: Value> {
    phantom_from: PhantomData<F>,
    phantom_to: PhantomData<T>,
    description: String
}

impl<F: Value, T: Value> Debug for DowncastError<F, T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f, "DowncastError")
    }
}

impl<F: Value, T: Value> DowncastError<F, T> {
    fn new() -> Self {
        DowncastError {
            phantom_from: PhantomData,
            phantom_to: PhantomData,
            description: format!("failed downcast to {}", T::name())
        }
    }
}

impl<F: Value, T: Value> Display for DowncastError<F, T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f, "{}", self.description())
    }
}

impl<F: Value, T: Value> Error for DowncastError<F, T> {
    fn description(&self) -> &str {
        &self.description
    }
}

/// The result of a call to `Handle::downcast()`.
pub type DowncastResult<'a, F, T> = Result<Handle<'a, T>, DowncastError<F, T>>;

impl<'a, F: Value, T: Value> JsResultExt<'a, T> for DowncastResult<'a, F, T> {
    fn or_throw<'b, C: Context<'b>>(self, cx: &mut C) -> JsResult<'a, T> {
        match self {
            Ok(v) => Ok(v),
            Err(e) => cx.throw_type_error(&e.description)
        }
    }
}

impl<'a, T: Value> Handle<'a, T> {

    /// Safely upcast a handle to a supertype.
    ///
    /// This method does not require an execution context because it only copies a handle.
    pub fn upcast<U: Value + SuperType<T>>(&self) -> Handle<'a, U> {
        Handle::new_internal(SuperType::upcast_internal(self.value))
    }

    #[cfg(feature = "legacy-runtime")]
    /// Tests whether this value is an instance of the given type.
    ///
    /// # Example:
    ///
    /// ```no_run
    /// # use neon::prelude::*;
    /// # fn my_neon_function(mut cx: FunctionContext) -> JsResult<JsUndefined> {
    /// let v: Handle<JsValue> = cx.number(17).upcast();
    /// v.is_a::<JsString>(); // false
    /// v.is_a::<JsNumber>(); // true
    /// v.is_a::<JsValue>();  // true
    /// # Ok(cx.undefined())
    /// # }
    /// ```
    pub fn is_a<U: Value>(&self) -> bool {
        U::is_typeof(Env::current(), self.value)
    }

    #[cfg(feature = "napi-runtime")]
    /// Tests whether this value is an instance of the given type.
    ///
    /// # Example:
    ///
    /// ```no_run
    /// # use neon::prelude::*;
    /// # fn my_neon_function(mut cx: FunctionContext) -> JsResult<JsUndefined> {
    /// let v: Handle<JsValue> = cx.number(17).upcast(&mut cx);
    /// v.is_a::<JsString>(&mut cx); // false
    /// v.is_a::<JsNumber>(&mut cx); // true
    /// v.is_a::<JsValue>(&mut cx);  // true
    /// # Ok(cx.undefined())
    /// # }
    /// ```
    pub fn is_a<'b, U: Value, C: Context<'b>>(&self, cx: &mut C) -> bool {
        U::is_typeof(cx.env(), self.value)
    }

    #[cfg(feature = "legacy-runtime")]
    /// Attempts to downcast a handle to another type, which may fail. A failure
    /// to downcast **does not** throw a JavaScript exception, so it's OK to
    /// continue interacting with the JS engine if this method produces an `Err`
    /// result.
    pub fn downcast<U: Value>(&self) -> DowncastResult<'a, T, U> {
        match U::downcast(Env::current(), self.value) {
            Some(v) => Ok(Handle::new_internal(v)),
            None => Err(DowncastError::new())
        }
    }

    #[cfg(feature = "napi-runtime")]
    /// Attempts to downcast a handle to another type, which may fail. A failure
    /// to downcast **does not** throw a JavaScript exception, so it's OK to
    /// continue interacting with the JS engine if this method produces an `Err`
    /// result.
    pub fn downcast<'b, U: Value, C: Context<'b>>(&self, cx: &mut C) -> DowncastResult<'a, T, U> {
        match U::downcast(cx.env(), self.value) {
            Some(v) => Ok(Handle::new_internal(v)),
            None => Err(DowncastError::new())
        }
    }

    #[cfg(feature = "legacy-runtime")]
    /// Attempts to downcast a handle to another type, raising a JavaScript `TypeError`
    /// exception on failure. This method is a convenient shorthand, equivalent to
    /// `self.downcast::<U>().or_throw::<C>(cx)`.
    pub fn downcast_or_throw<'b, U: Value, C: Context<'b>>(&self, cx: &mut C) -> JsResult<'a, U> {
        self.downcast().or_throw(cx)
    }

    #[cfg(feature = "napi-runtime")]
    /// Attempts to downcast a handle to another type, raising a JavaScript `TypeError`
    /// exception on failure. This method is a convenient shorthand, equivalent to
    /// `self.downcast::<U>().or_throw::<C>(cx)`.
    pub fn downcast_or_throw<'b, U: Value, C: Context<'b>>(&self, cx: &mut C) -> JsResult<'a, U> {
        self.downcast(cx).or_throw(cx)
    }

}

impl<'a, T: Managed> Deref for Handle<'a, T> {
    type Target = T;
    fn deref<'b>(&'b self) -> &'b T {
        &self.value
    }
}

impl<'a, T: Managed> DerefMut for Handle<'a, T> {
    fn deref_mut<'b>(&'b mut self) -> &'b mut T {
        &mut self.value
    }
}
<|endoftext|>