<commit_before>//! `ra_assists` crate provides a bunch of code assists, also known as code
//! actions (in LSP) or intentions (in IntelliJ).
//!
//! An assist is a micro-refactoring, which is automatically activated in
//! certain context. For example, if the cursor is over `,`, a "swap `,`" assist
//! becomes available.

mod assist_ctx;
mod marks;

use hir::db::HirDatabase;
use itertools::Itertools;
use ra_db::FileRange;
use ra_syntax::{TextRange, TextUnit};
use ra_text_edit::TextEdit;

pub(crate) use crate::assist_ctx::{Assist, AssistCtx};
pub use crate::assists::auto_import::auto_import_text_edit;

/// Unique identifier of the assist, should not be shown to the user
/// directly.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AssistId(pub &'static str);

#[derive(Debug, Clone)]
pub struct AssistLabel {
    /// Short description of the assist, as shown in the UI.
    pub label: String,
    pub id: AssistId,
}

#[derive(Debug, Clone)]
pub struct AssistAction {
    pub edit: TextEdit,
    pub cursor_position: Option<TextUnit>,
    pub target: Option<TextRange>,
}

/// Return all the assists eapplicable at the given position.
///
/// Assists are returned in the "unresolved" state, that is only labels are
/// returned, without actual edits.
pub fn applicable_assists<H>(db: &H, range: FileRange) -> Vec<AssistLabel>
where
    H: HirDatabase + 'static,
{
    AssistCtx::with_ctx(db, range, false, |ctx| {
        assists::all()
            .iter()
            .filter_map(|f| f(ctx.clone()))
            .map(|a| match a {
                Assist::Unresolved(labels) => labels,
                Assist::Resolved(..) => unreachable!(),
            })
            .concat()
    })
}

/// Return all the assists applicable at the given position.
///
/// Assists are returned in the "resolved" state, that is with edit fully
/// computed.
pub fn assists<H>(db: &H, range: FileRange) -> Vec<(AssistLabel, AssistAction)>
where
    H: HirDatabase + 'static,
{
    use std::cmp::Ordering;

    AssistCtx::with_ctx(db, range, true, |ctx| {
        let mut a = assists::all()
            .iter()
            .filter_map(|f| f(ctx.clone()))
            .map(|a| match a {
                Assist::Resolved(labels_actions) => labels_actions,
                Assist::Unresolved(..) => unreachable!(),
            })
            .concat();
        a.sort_by(|a, b| match (a.1.target, b.1.target) {
            (Some(a), Some(b)) => a.len().cmp(&b.len()),
            (Some(_), None) => Ordering::Less,
            (None, Some(_)) => Ordering::Greater,
            (None, None) => Ordering::Equal,
        });
        a
    })
}

mod assists {
    use crate::{Assist, AssistCtx};
    use hir::db::HirDatabase;

    mod add_derive;
    mod add_explicit_type;
    mod add_impl;
    mod flip_comma;
    mod flip_binexpr;
    mod change_visibility;
    mod fill_match_arms;
    mod merge_match_arms;
    mod introduce_variable;
    mod inline_local_variable;
    mod raw_string;
    mod replace_if_let_with_match;
    mod split_import;
    mod remove_dbg;
    pub(crate) mod auto_import;
    mod add_missing_impl_members;
    mod move_guard;
    mod move_bounds;

    pub(crate) fn all<DB: HirDatabase>() -> &'static [fn(AssistCtx<DB>) -> Option<Assist>] {
        &[
            add_derive::add_derive,
            add_explicit_type::add_explicit_type,
            add_impl::add_impl,
            change_visibility::change_visibility,
            fill_match_arms::fill_match_arms,
            merge_match_arms::merge_match_arms,
            flip_comma::flip_comma,
            flip_binexpr::flip_binexpr,
            introduce_variable::introduce_variable,
            replace_if_let_with_match::replace_if_let_with_match,
            split_import::split_import,
            remove_dbg::remove_dbg,
            auto_import::auto_import,
            add_missing_impl_members::add_missing_impl_members,
            add_missing_impl_members::add_missing_default_members,
            inline_local_variable::inline_local_varialbe,
            move_guard::move_guard_to_arm_body,
            move_guard::move_arm_cond_to_match_guard,
            move_bounds::move_bounds_to_where_clause,
            raw_string::add_hash,
            raw_string::make_raw_string,
            raw_string::make_usual_string,
            raw_string::remove_hash,
        ]
    }
}

#[cfg(test)]
mod helpers {
    use hir::mock::MockDatabase;
    use ra_db::FileRange;
    use ra_syntax::TextRange;
    use test_utils::{add_cursor, assert_eq_text, extract_offset, extract_range};

    use crate::{Assist, AssistCtx};

    pub(crate) fn check_assist(
        assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>,
        before: &str,
        after: &str,
    ) {
        check_assist_nth_action(assist, before, after, 0)
    }

    pub(crate) fn check_assist_range(
        assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>,
        before: &str,
        after: &str,
    ) {
        check_assist_range_nth_action(assist, before, after, 0)
    }

    pub(crate) fn check_assist_target(
        assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>,
        before: &str,
        target: &str,
    ) {
        check_assist_target_nth_action(assist, before, target, 0)
    }

    pub(crate) fn check_assist_range_target(
        assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>,
        before: &str,
        target: &str,
    ) {
        check_assist_range_target_nth_action(assist, before, target, 0)
    }

    pub(crate) fn check_assist_nth_action(
        assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>,
        before: &str,
        after: &str,
        index: usize,
    ) {
        let (before_cursor_pos, before) = extract_offset(before);
        let (db, _source_root, file_id) = MockDatabase::with_single_file(&before);
        let frange =
            FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) };
        let assist =
            AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable");
        let labels_actions = match assist {
            Assist::Unresolved(_) => unreachable!(),
            Assist::Resolved(labels_actions) => labels_actions,
        };

        let (_, action) = labels_actions.get(index).expect("expect assist action at index");
        let actual = action.edit.apply(&before);
        let actual_cursor_pos = match action.cursor_position {
            None => action
                .edit
                .apply_to_offset(before_cursor_pos)
                .expect("cursor position is affected by the edit"),
            Some(off) => off,
        };
        let actual = add_cursor(&actual, actual_cursor_pos);
        assert_eq_text!(after, &actual);
    }

    pub(crate) fn check_assist_range_nth_action(
        assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>,
        before: &str,
        after: &str,
        index: usize,
    ) {
        let (range, before) = extract_range(before);
        let (db, _source_root, file_id) = MockDatabase::with_single_file(&before);
        let frange = FileRange { file_id, range };
        let assist =
            AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable");
        let labels_actions = match assist {
            Assist::Unresolved(_) => unreachable!(),
            Assist::Resolved(labels_actions) => labels_actions,
        };

        let (_, action) = labels_actions.get(index).expect("expect assist action at index");
        let mut actual = action.edit.apply(&before);
        if let Some(pos) = action.cursor_position {
            actual = add_cursor(&actual, pos);
        }
        assert_eq_text!(after, &actual);
    }

    pub(crate) fn check_assist_target_nth_action(
        assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>,
        before: &str,
        target: &str,
        index: usize,
    ) {
        let (before_cursor_pos, before) = extract_offset(before);
        let (db, _source_root, file_id) = MockDatabase::with_single_file(&before);
        let frange =
            FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) };
        let assist =
            AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable");
        let labels_actions = match assist {
            Assist::Unresolved(_) => unreachable!(),
            Assist::Resolved(labels_actions) => labels_actions,
        };

        let (_, action) = labels_actions.get(index).expect("expect assist action at index");
        let range = action.target.expect("expected target on action");
        assert_eq_text!(&before[range.start().to_usize()..range.end().to_usize()], target);
    }

    pub(crate) fn check_assist_range_target_nth_action(
        assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>,
        before: &str,
        target: &str,
        index: usize,
    ) {
        let (range, before) = extract_range(before);
        let (db, _source_root, file_id) = MockDatabase::with_single_file(&before);
        let frange = FileRange { file_id, range };
        let assist =
            AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable");
        let labels_actions = match assist {
            Assist::Unresolved(_) => unreachable!(),
            Assist::Resolved(labels_actions) => labels_actions,
        };

        let (_, action) = labels_actions.get(index).expect("expect assist action at index");
        let range = action.target.expect("expected target on action");
        assert_eq_text!(&before[range.start().to_usize()..range.end().to_usize()], target);
    }

    pub(crate) fn check_assist_not_applicable(
        assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>,
        before: &str,
    ) {
        let (before_cursor_pos, before) = extract_offset(before);
        let (db, _source_root, file_id) = MockDatabase::with_single_file(&before);
        let frange =
            FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) };
        let assist = AssistCtx::with_ctx(&db, frange, true, assist);
        assert!(assist.is_none());
    }

    pub(crate) fn check_assist_range_not_applicable(
        assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>,
        before: &str,
    ) {
        let (range, before) = extract_range(before);
        let (db, _source_root, file_id) = MockDatabase::with_single_file(&before);
        let frange = FileRange { file_id, range };
        let assist = AssistCtx::with_ctx(&db, frange, true, assist);
        assert!(assist.is_none());
    }
}

#[cfg(test)]
mod tests {
    use hir::mock::MockDatabase;
    use ra_db::FileRange;
    use ra_syntax::TextRange;
    use test_utils::{extract_offset, extract_range};

    #[test]
    fn assist_order_field_struct() {
        let before = "struct Foo { <|>bar: u32 }";
        let (before_cursor_pos, before) = extract_offset(before);
        let (db, _source_root, file_id) = MockDatabase::with_single_file(&before);
        let frange =
            FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) };
        let assists = super::assists(&db, frange);
        let mut assists = assists.iter();

        assert_eq!(assists.next().expect("expected assist").0.label, "make pub(crate)");
        assert_eq!(assists.next().expect("expected assist").0.label, "add `#[derive]`");
    }

    #[test]
    fn assist_order_if_expr() {
        let before = "
        pub fn test_some_range(a: int) -> bool {
            if let 2..6 = <|>5<|> {
                true
            } else {
                false
            }
        }";
        let (range, before) = extract_range(before);
        let (db, _source_root, file_id) = MockDatabase::with_single_file(&before);
        let frange = FileRange { file_id, range };
        let assists = super::assists(&db, frange);
        let mut assists = assists.iter();

        assert_eq!(assists.next().expect("expected assist").0.label, "introduce variable");
        assert_eq!(assists.next().expect("expected assist").0.label, "replace with match");
    }
}
<commit_msg>WIP: Add demorgan application with naive negation<commit_after>//! `ra_assists` crate provides a bunch of code assists, also known as code
//! actions (in LSP) or intentions (in IntelliJ).
//!
//! An assist is a micro-refactoring, which is automatically activated in
//! certain context. For example, if the cursor is over `,`, a "swap `,`" assist
//! becomes available.

mod assist_ctx;
mod marks;

use hir::db::HirDatabase;
use itertools::Itertools;
use ra_db::FileRange;
use ra_syntax::{TextRange, TextUnit};
use ra_text_edit::TextEdit;

pub(crate) use crate::assist_ctx::{Assist, AssistCtx};
pub use crate::assists::auto_import::auto_import_text_edit;

/// Unique identifier of the assist, should not be shown to the user
/// directly.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AssistId(pub &'static str);

#[derive(Debug, Clone)]
pub struct AssistLabel {
    /// Short description of the assist, as shown in the UI.
    pub label: String,
    pub id: AssistId,
}

#[derive(Debug, Clone)]
pub struct AssistAction {
    pub edit: TextEdit,
    pub cursor_position: Option<TextUnit>,
    pub target: Option<TextRange>,
}

/// Return all the assists eapplicable at the given position.
///
/// Assists are returned in the "unresolved" state, that is only labels are
/// returned, without actual edits.
pub fn applicable_assists<H>(db: &H, range: FileRange) -> Vec<AssistLabel>
where
    H: HirDatabase + 'static,
{
    AssistCtx::with_ctx(db, range, false, |ctx| {
        assists::all()
            .iter()
            .filter_map(|f| f(ctx.clone()))
            .map(|a| match a {
                Assist::Unresolved(labels) => labels,
                Assist::Resolved(..) => unreachable!(),
            })
            .concat()
    })
}

/// Return all the assists applicable at the given position.
///
/// Assists are returned in the "resolved" state, that is with edit fully
/// computed.
pub fn assists<H>(db: &H, range: FileRange) -> Vec<(AssistLabel, AssistAction)>
where
    H: HirDatabase + 'static,
{
    use std::cmp::Ordering;

    AssistCtx::with_ctx(db, range, true, |ctx| {
        let mut a = assists::all()
            .iter()
            .filter_map(|f| f(ctx.clone()))
            .map(|a| match a {
                Assist::Resolved(labels_actions) => labels_actions,
                Assist::Unresolved(..) => unreachable!(),
            })
            .concat();
        a.sort_by(|a, b| match (a.1.target, b.1.target) {
            (Some(a), Some(b)) => a.len().cmp(&b.len()),
            (Some(_), None) => Ordering::Less,
            (None, Some(_)) => Ordering::Greater,
            (None, None) => Ordering::Equal,
        });
        a
    })
}

mod assists {
    use crate::{Assist, AssistCtx};
    use hir::db::HirDatabase;

    mod add_derive;
    mod add_explicit_type;
    mod add_impl;
    mod apply_demorgan;
    mod flip_comma;
    mod flip_binexpr;
    mod change_visibility;
    mod fill_match_arms;
    mod merge_match_arms;
    mod introduce_variable;
    mod inline_local_variable;
    mod raw_string;
    mod replace_if_let_with_match;
    mod split_import;
    mod remove_dbg;
    pub(crate) mod auto_import;
    mod add_missing_impl_members;
    mod move_guard;
    mod move_bounds;

    pub(crate) fn all<DB: HirDatabase>() -> &'static [fn(AssistCtx<DB>) -> Option<Assist>] {
        &[
            add_derive::add_derive,
            add_explicit_type::add_explicit_type,
            add_impl::add_impl,
            apply_demorgan::apply_demorgan,
            change_visibility::change_visibility,
            fill_match_arms::fill_match_arms,
            merge_match_arms::merge_match_arms,
            flip_comma::flip_comma,
            flip_binexpr::flip_binexpr,
            introduce_variable::introduce_variable,
            replace_if_let_with_match::replace_if_let_with_match,
            split_import::split_import,
            remove_dbg::remove_dbg,
            auto_import::auto_import,
            add_missing_impl_members::add_missing_impl_members,
            add_missing_impl_members::add_missing_default_members,
            inline_local_variable::inline_local_varialbe,
            move_guard::move_guard_to_arm_body,
            move_guard::move_arm_cond_to_match_guard,
            move_bounds::move_bounds_to_where_clause,
            raw_string::add_hash,
            raw_string::make_raw_string,
            raw_string::make_usual_string,
            raw_string::remove_hash,
        ]
    }
}

#[cfg(test)]
mod helpers {
    use hir::mock::MockDatabase;
    use ra_db::FileRange;
    use ra_syntax::TextRange;
    use test_utils::{add_cursor, assert_eq_text, extract_offset, extract_range};

    use crate::{Assist, AssistCtx};

    pub(crate) fn check_assist(
        assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>,
        before: &str,
        after: &str,
    ) {
        check_assist_nth_action(assist, before, after, 0)
    }

    pub(crate) fn check_assist_range(
        assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>,
        before: &str,
        after: &str,
    ) {
        check_assist_range_nth_action(assist, before, after, 0)
    }

    pub(crate) fn check_assist_target(
        assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>,
        before: &str,
        target: &str,
    ) {
        check_assist_target_nth_action(assist, before, target, 0)
    }

    pub(crate) fn check_assist_range_target(
        assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>,
        before: &str,
        target: &str,
    ) {
        check_assist_range_target_nth_action(assist, before, target, 0)
    }

    pub(crate) fn check_assist_nth_action(
        assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>,
        before: &str,
        after: &str,
        index: usize,
    ) {
        let (before_cursor_pos, before) = extract_offset(before);
        let (db, _source_root, file_id) = MockDatabase::with_single_file(&before);
        let frange =
            FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) };
        let assist =
            AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable");
        let labels_actions = match assist {
            Assist::Unresolved(_) => unreachable!(),
            Assist::Resolved(labels_actions) => labels_actions,
        };

        let (_, action) = labels_actions.get(index).expect("expect assist action at index");
        let actual = action.edit.apply(&before);
        let actual_cursor_pos = match action.cursor_position {
            None => action
                .edit
                .apply_to_offset(before_cursor_pos)
                .expect("cursor position is affected by the edit"),
            Some(off) => off,
        };
        let actual = add_cursor(&actual, actual_cursor_pos);
        assert_eq_text!(after, &actual);
    }

    pub(crate) fn check_assist_range_nth_action(
        assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>,
        before: &str,
        after: &str,
        index: usize,
    ) {
        let (range, before) = extract_range(before);
        let (db, _source_root, file_id) = MockDatabase::with_single_file(&before);
        let frange = FileRange { file_id, range };
        let assist =
            AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable");
        let labels_actions = match assist {
            Assist::Unresolved(_) => unreachable!(),
            Assist::Resolved(labels_actions) => labels_actions,
        };

        let (_, action) = labels_actions.get(index).expect("expect assist action at index");
        let mut actual = action.edit.apply(&before);
        if let Some(pos) = action.cursor_position {
            actual = add_cursor(&actual, pos);
        }
        assert_eq_text!(after, &actual);
    }

    pub(crate) fn check_assist_target_nth_action(
        assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>,
        before: &str,
        target: &str,
        index: usize,
    ) {
        let (before_cursor_pos, before) = extract_offset(before);
        let (db, _source_root, file_id) = MockDatabase::with_single_file(&before);
        let frange =
            FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) };
        let assist =
            AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable");
        let labels_actions = match assist {
            Assist::Unresolved(_) => unreachable!(),
            Assist::Resolved(labels_actions) => labels_actions,
        };

        let (_, action) = labels_actions.get(index).expect("expect assist action at index");
        let range = action.target.expect("expected target on action");
        assert_eq_text!(&before[range.start().to_usize()..range.end().to_usize()], target);
    }

    pub(crate) fn check_assist_range_target_nth_action(
        assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>,
        before: &str,
        target: &str,
        index: usize,
    ) {
        let (range, before) = extract_range(before);
        let (db, _source_root, file_id) = MockDatabase::with_single_file(&before);
        let frange = FileRange { file_id, range };
        let assist =
            AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable");
        let labels_actions = match assist {
            Assist::Unresolved(_) => unreachable!(),
            Assist::Resolved(labels_actions) => labels_actions,
        };

        let (_, action) = labels_actions.get(index).expect("expect assist action at index");
        let range = action.target.expect("expected target on action");
        assert_eq_text!(&before[range.start().to_usize()..range.end().to_usize()], target);
    }

    pub(crate) fn check_assist_not_applicable(
        assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>,
        before: &str,
    ) {
        let (before_cursor_pos, before) = extract_offset(before);
        let (db, _source_root, file_id) = MockDatabase::with_single_file(&before);
        let frange =
            FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) };
        let assist = AssistCtx::with_ctx(&db, frange, true, assist);
        assert!(assist.is_none());
    }

    pub(crate) fn check_assist_range_not_applicable(
        assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>,
        before: &str,
    ) {
        let (range, before) = extract_range(before);
        let (db, _source_root, file_id) = MockDatabase::with_single_file(&before);
        let frange = FileRange { file_id, range };
        let assist = AssistCtx::with_ctx(&db, frange, true, assist);
        assert!(assist.is_none());
    }
}

#[cfg(test)]
mod tests {
    use hir::mock::MockDatabase;
    use ra_db::FileRange;
    use ra_syntax::TextRange;
    use test_utils::{extract_offset, extract_range};

    #[test]
    fn assist_order_field_struct() {
        let before = "struct Foo { <|>bar: u32 }";
        let (before_cursor_pos, before) = extract_offset(before);
        let (db, _source_root, file_id) = MockDatabase::with_single_file(&before);
        let frange =
            FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) };
        let assists = super::assists(&db, frange);
        let mut assists = assists.iter();

        assert_eq!(assists.next().expect("expected assist").0.label, "make pub(crate)");
        assert_eq!(assists.next().expect("expected assist").0.label, "add `#[derive]`");
    }

    #[test]
    fn assist_order_if_expr() {
        let before = "
        pub fn test_some_range(a: int) -> bool {
            if let 2..6 = <|>5<|> {
                true
            } else {
                false
            }
        }";
        let (range, before) = extract_range(before);
        let (db, _source_root, file_id) = MockDatabase::with_single_file(&before);
        let frange = FileRange { file_id, range };
        let assists = super::assists(&db, frange);
        let mut assists = assists.iter();

        assert_eq!(assists.next().expect("expected assist").0.label, "introduce variable");
        assert_eq!(assists.next().expect("expected assist").0.label, "replace with match");
    }
}
<|endoftext|>


<commit_before>//! Name resolution.
use std::sync::Arc;

use rustc_hash::FxHashMap;

use ra_syntax::SmolStr;

use crate::{
    ModuleDef,
    code_model_api::Crate,
    db::HirDatabase,
    name::{Name, KnownName},
    nameres::{PerNs, CrateDefMap, CrateModuleId},
    generics::GenericParams,
    expr::{scope::{ExprScopes, ScopeId}, PatId},
    impl_block::ImplBlock,
    path::Path,
    Trait
};

#[derive(Debug, Clone, Default)]
pub(crate) struct Resolver {
    scopes: Vec<Scope>,
}

// FIXME how to store these best
#[derive(Debug, Clone)]
pub(crate) struct ModuleItemMap {
    crate_def_map: Arc<CrateDefMap>,
    module_id: CrateModuleId,
}

#[derive(Debug, Clone)]
pub(crate) struct ExprScope {
    expr_scopes: Arc<ExprScopes>,
    scope_id: ScopeId,
}

#[derive(Debug, Clone)]
pub(crate) struct PathResult {
    /// The actual path resolution
    resolution: PerNs<Resolution>,
    /// The first index in the path that we
    /// were unable to resolve.
    /// When path is fully resolved, this is 0.
    remaining_index: usize,
}

impl PathResult {
    /// Returns the remaining index in the result
    /// returns None if the path was fully resolved
    pub(crate) fn remaining_index(&self) -> Option<usize> {
        if self.remaining_index > 0 {
            Some(self.remaining_index)
        } else {
            None
        }
    }

    /// Consumes `PathResult` and returns the contained `PerNs<Resolution>`
    /// if the path was fully resolved, meaning we have no remaining items
    pub(crate) fn into_fully_resolved(self) -> PerNs<Resolution> {
        if self.is_fully_resolved() {
            self.resolution
        } else {
            PerNs::none()
        }
    }

    /// Consumes `PathResult` and returns the resolution and the
    /// remaining_index as a tuple.
    pub(crate) fn into_inner(self) -> (PerNs<Resolution>, Option<usize>) {
        let index = self.remaining_index();
        (self.resolution, index)
    }

    /// Path is fully resolved when `remaining_index` is none
    /// and the resolution contains anything
    pub(crate) fn is_fully_resolved(&self) -> bool {
        !self.resolution.is_none() && self.remaining_index().is_none()
    }

    fn empty() -> PathResult {
        PathResult { resolution: PerNs::none(), remaining_index: 0 }
    }

    fn from_resolution(res: PerNs<Resolution>) -> PathResult {
        PathResult::from_resolution_with_index(res, 0)
    }

    fn from_resolution_with_index(res: PerNs<Resolution>, remaining_index: usize) -> PathResult {
        if res.is_none() {
            PathResult::empty()
        } else {
            PathResult { resolution: res, remaining_index }
        }
    }
}

#[derive(Debug, Clone)]
pub(crate) enum Scope {
    /// All the items and imported names of a module
    ModuleScope(ModuleItemMap),
    /// Brings the generic parameters of an item into scope
    GenericParams(Arc<GenericParams>),
    /// Brings `Self` into scope
    ImplBlockScope(ImplBlock),
    /// Local bindings
    ExprScope(ExprScope),
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Resolution {
    /// An item
    Def(ModuleDef),
    /// A local binding (only value namespace)
    LocalBinding(PatId),
    /// A generic parameter
    GenericParam(u32),
    SelfType(ImplBlock),
}

impl Resolver {
    pub(crate) fn resolve_name(&self, db: &impl HirDatabase, name: &Name) -> PerNs<Resolution> {
        let mut resolution = PerNs::none();
        for scope in self.scopes.iter().rev() {
            resolution = resolution.or(scope.resolve_name(db, name));
            if resolution.is_both() {
                return resolution;
            }
        }
        resolution
    }

    /// Returns the resolved path segments
    /// Which may be fully resolved, empty or partially resolved.
    pub(crate) fn resolve_path_segments(&self, db: &impl HirDatabase, path: &Path) -> PathResult {
        if let Some(name) = path.as_ident() {
            PathResult::from_resolution(self.resolve_name(db, name))
        } else if path.is_self() {
            PathResult::from_resolution(self.resolve_name(db, &Name::self_param()))
        } else {
            let (item_map, module) = match self.module() {
                Some(m) => m,
                _ => return PathResult::empty(),
            };
            let (module_res, segment_index) = item_map.resolve_path(db, module, path);

            let def = module_res.map(Resolution::Def);

            if let Some(index) = segment_index {
                PathResult::from_resolution_with_index(def, index)
            } else {
                PathResult::from_resolution(def)
            }
        }
    }

    /// Returns the fully resolved path if we were able to resolve it.
    /// otherwise returns `PerNs::none`
    pub(crate) fn resolve_path(&self, db: &impl HirDatabase, path: &Path) -> PerNs<Resolution> {
        // into_fully_resolved() returns the fully resolved path or PerNs::none() otherwise
        self.resolve_path_segments(db, path).into_fully_resolved()
    }

    pub(crate) fn all_names(&self, db: &impl HirDatabase) -> FxHashMap<Name, PerNs<Resolution>> {
        let mut names = FxHashMap::default();
        for scope in self.scopes.iter().rev() {
            scope.collect_names(db, &mut |name, res| {
                let current: &mut PerNs<Resolution> = names.entry(name).or_default();
                if current.types.is_none() {
                    current.types = res.types;
                }
                if current.values.is_none() {
                    current.values = res.values;
                }
            });
        }
        names
    }

    pub(crate) fn traits_in_scope<'a>(&'a self) -> impl Iterator<Item = Trait> + 'a {
        // FIXME prelude
        self.scopes
            .iter()
            .rev()
            .flat_map(|scope| {
                match scope {
                    Scope::ModuleScope(m) => Some(m.crate_def_map[m.module_id].scope.traits()),
                    _ => None,
                }
                .into_iter()
            })
            .flatten()
    }

    fn module(&self) -> Option<(&CrateDefMap, CrateModuleId)> {
        self.scopes.iter().rev().find_map(|scope| match scope {
            Scope::ModuleScope(m) => Some((&*m.crate_def_map, m.module_id)),

            _ => None,
        })
    }

    pub(crate) fn krate(&self) -> Option<Crate> {
        self.module().map(|t| t.0.krate())
    }
}

impl Resolver {
    pub(crate) fn push_scope(mut self, scope: Scope) -> Resolver {
        self.scopes.push(scope);
        self
    }

    pub(crate) fn push_generic_params_scope(self, params: Arc<GenericParams>) -> Resolver {
        self.push_scope(Scope::GenericParams(params))
    }

    pub(crate) fn push_impl_block_scope(self, impl_block: ImplBlock) -> Resolver {
        self.push_scope(Scope::ImplBlockScope(impl_block))
    }

    pub(crate) fn push_module_scope(
        self,
        crate_def_map: Arc<CrateDefMap>,
        module_id: CrateModuleId,
    ) -> Resolver {
        self.push_scope(Scope::ModuleScope(ModuleItemMap { crate_def_map, module_id }))
    }

    pub(crate) fn push_expr_scope(
        self,
        expr_scopes: Arc<ExprScopes>,
        scope_id: ScopeId,
    ) -> Resolver {
        self.push_scope(Scope::ExprScope(ExprScope { expr_scopes, scope_id }))
    }
}

impl Scope {
    fn resolve_name(&self, db: &impl HirDatabase, name: &Name) -> PerNs<Resolution> {
        match self {
            Scope::ModuleScope(m) => {
                if let Some(KnownName::SelfParam) = name.as_known_name() {
                    PerNs::types(Resolution::Def(m.crate_def_map.mk_module(m.module_id).into()))
                } else {
                    m.crate_def_map
                        .resolve_name_in_module(db, m.module_id, name)
                        .map(Resolution::Def)
                }
            }
            Scope::GenericParams(gp) => match gp.find_by_name(name) {
                Some(gp) => PerNs::types(Resolution::GenericParam(gp.idx)),
                None => PerNs::none(),
            },
            Scope::ImplBlockScope(i) => {
                if name.as_known_name() == Some(KnownName::SelfType) {
                    PerNs::types(Resolution::SelfType(i.clone()))
                } else {
                    PerNs::none()
                }
            }
            Scope::ExprScope(e) => {
                let entry =
                    e.expr_scopes.entries(e.scope_id).iter().find(|entry| entry.name() == name);
                match entry {
                    Some(e) => PerNs::values(Resolution::LocalBinding(e.pat())),
                    None => PerNs::none(),
                }
            }
        }
    }

    fn collect_names(&self, db: &impl HirDatabase, f: &mut dyn FnMut(Name, PerNs<Resolution>)) {
        match self {
            Scope::ModuleScope(m) => {
                // FIXME: should we provide `self` here?
                // f(
                //     Name::self_param(),
                //     PerNs::types(Resolution::Def {
                //         def: m.module.into(),
                //     }),
                // );
                m.crate_def_map[m.module_id].scope.entries().for_each(|(name, res)| {
                    f(name.clone(), res.def.map(Resolution::Def));
                });
                m.crate_def_map.extern_prelude().iter().for_each(|(name, def)| {
                    f(name.clone(), PerNs::types(Resolution::Def(*def)));
                });
                if let Some(prelude) = m.crate_def_map.prelude() {
                    let prelude_def_map = db.crate_def_map(prelude.krate);
                    prelude_def_map[prelude.module_id].scope.entries().for_each(|(name, res)| {
                        f(name.clone(), res.def.map(Resolution::Def));
                    });
                }
            }
            Scope::GenericParams(gp) => {
                for param in &gp.params {
                    f(param.name.clone(), PerNs::types(Resolution::GenericParam(param.idx)))
                }
            }
            Scope::ImplBlockScope(i) => {
                f(Name::self_type(), PerNs::types(Resolution::SelfType(i.clone())));
            }
            Scope::ExprScope(e) => {
                e.expr_scopes.entries(e.scope_id).iter().for_each(|e| {
                    f(e.name().clone(), PerNs::values(Resolution::LocalBinding(e.pat())));
                });
            }
        }
    }
}
<commit_msg>unused import<commit_after>//! Name resolution.
use std::sync::Arc;

use rustc_hash::FxHashMap;

use crate::{
    ModuleDef,
    code_model_api::Crate,
    db::HirDatabase,
    name::{Name, KnownName},
    nameres::{PerNs, CrateDefMap, CrateModuleId},
    generics::GenericParams,
    expr::{scope::{ExprScopes, ScopeId}, PatId},
    impl_block::ImplBlock,
    path::Path,
    Trait
};

#[derive(Debug, Clone, Default)]
pub(crate) struct Resolver {
    scopes: Vec<Scope>,
}

// FIXME how to store these best
#[derive(Debug, Clone)]
pub(crate) struct ModuleItemMap {
    crate_def_map: Arc<CrateDefMap>,
    module_id: CrateModuleId,
}

#[derive(Debug, Clone)]
pub(crate) struct ExprScope {
    expr_scopes: Arc<ExprScopes>,
    scope_id: ScopeId,
}

#[derive(Debug, Clone)]
pub(crate) struct PathResult {
    /// The actual path resolution
    resolution: PerNs<Resolution>,
    /// The first index in the path that we
    /// were unable to resolve.
    /// When path is fully resolved, this is 0.
    remaining_index: usize,
}

impl PathResult {
    /// Returns the remaining index in the result
    /// returns None if the path was fully resolved
    pub(crate) fn remaining_index(&self) -> Option<usize> {
        if self.remaining_index > 0 {
            Some(self.remaining_index)
        } else {
            None
        }
    }

    /// Consumes `PathResult` and returns the contained `PerNs<Resolution>`
    /// if the path was fully resolved, meaning we have no remaining items
    pub(crate) fn into_fully_resolved(self) -> PerNs<Resolution> {
        if self.is_fully_resolved() {
            self.resolution
        } else {
            PerNs::none()
        }
    }

    /// Consumes `PathResult` and returns the resolution and the
    /// remaining_index as a tuple.
    pub(crate) fn into_inner(self) -> (PerNs<Resolution>, Option<usize>) {
        let index = self.remaining_index();
        (self.resolution, index)
    }

    /// Path is fully resolved when `remaining_index` is none
    /// and the resolution contains anything
    pub(crate) fn is_fully_resolved(&self) -> bool {
        !self.resolution.is_none() && self.remaining_index().is_none()
    }

    fn empty() -> PathResult {
        PathResult { resolution: PerNs::none(), remaining_index: 0 }
    }

    fn from_resolution(res: PerNs<Resolution>) -> PathResult {
        PathResult::from_resolution_with_index(res, 0)
    }

    fn from_resolution_with_index(res: PerNs<Resolution>, remaining_index: usize) -> PathResult {
        if res.is_none() {
            PathResult::empty()
        } else {
            PathResult { resolution: res, remaining_index }
        }
    }
}

#[derive(Debug, Clone)]
pub(crate) enum Scope {
    /// All the items and imported names of a module
    ModuleScope(ModuleItemMap),
    /// Brings the generic parameters of an item into scope
    GenericParams(Arc<GenericParams>),
    /// Brings `Self` into scope
    ImplBlockScope(ImplBlock),
    /// Local bindings
    ExprScope(ExprScope),
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Resolution {
    /// An item
    Def(ModuleDef),
    /// A local binding (only value namespace)
    LocalBinding(PatId),
    /// A generic parameter
    GenericParam(u32),
    SelfType(ImplBlock),
}

impl Resolver {
    pub(crate) fn resolve_name(&self, db: &impl HirDatabase, name: &Name) -> PerNs<Resolution> {
        let mut resolution = PerNs::none();
        for scope in self.scopes.iter().rev() {
            resolution = resolution.or(scope.resolve_name(db, name));
            if resolution.is_both() {
                return resolution;
            }
        }
        resolution
    }

    /// Returns the resolved path segments
    /// Which may be fully resolved, empty or partially resolved.
    pub(crate) fn resolve_path_segments(&self, db: &impl HirDatabase, path: &Path) -> PathResult {
        if let Some(name) = path.as_ident() {
            PathResult::from_resolution(self.resolve_name(db, name))
        } else if path.is_self() {
            PathResult::from_resolution(self.resolve_name(db, &Name::self_param()))
        } else {
            let (item_map, module) = match self.module() {
                Some(m) => m,
                _ => return PathResult::empty(),
            };
            let (module_res, segment_index) = item_map.resolve_path(db, module, path);

            let def = module_res.map(Resolution::Def);

            if let Some(index) = segment_index {
                PathResult::from_resolution_with_index(def, index)
            } else {
                PathResult::from_resolution(def)
            }
        }
    }

    /// Returns the fully resolved path if we were able to resolve it.
    /// otherwise returns `PerNs::none`
    pub(crate) fn resolve_path(&self, db: &impl HirDatabase, path: &Path) -> PerNs<Resolution> {
        // into_fully_resolved() returns the fully resolved path or PerNs::none() otherwise
        self.resolve_path_segments(db, path).into_fully_resolved()
    }

    pub(crate) fn all_names(&self, db: &impl HirDatabase) -> FxHashMap<Name, PerNs<Resolution>> {
        let mut names = FxHashMap::default();
        for scope in self.scopes.iter().rev() {
            scope.collect_names(db, &mut |name, res| {
                let current: &mut PerNs<Resolution> = names.entry(name).or_default();
                if current.types.is_none() {
                    current.types = res.types;
                }
                if current.values.is_none() {
                    current.values = res.values;
                }
            });
        }
        names
    }

    pub(crate) fn traits_in_scope<'a>(&'a self) -> impl Iterator<Item = Trait> + 'a {
        // FIXME prelude
        self.scopes
            .iter()
            .rev()
            .flat_map(|scope| {
                match scope {
                    Scope::ModuleScope(m) => Some(m.crate_def_map[m.module_id].scope.traits()),
                    _ => None,
                }
                .into_iter()
            })
            .flatten()
    }

    fn module(&self) -> Option<(&CrateDefMap, CrateModuleId)> {
        self.scopes.iter().rev().find_map(|scope| match scope {
            Scope::ModuleScope(m) => Some((&*m.crate_def_map, m.module_id)),

            _ => None,
        })
    }

    pub(crate) fn krate(&self) -> Option<Crate> {
        self.module().map(|t| t.0.krate())
    }
}

impl Resolver {
    pub(crate) fn push_scope(mut self, scope: Scope) -> Resolver {
        self.scopes.push(scope);
        self
    }

    pub(crate) fn push_generic_params_scope(self, params: Arc<GenericParams>) -> Resolver {
        self.push_scope(Scope::GenericParams(params))
    }

    pub(crate) fn push_impl_block_scope(self, impl_block: ImplBlock) -> Resolver {
        self.push_scope(Scope::ImplBlockScope(impl_block))
    }

    pub(crate) fn push_module_scope(
        self,
        crate_def_map: Arc<CrateDefMap>,
        module_id: CrateModuleId,
    ) -> Resolver {
        self.push_scope(Scope::ModuleScope(ModuleItemMap { crate_def_map, module_id }))
    }

    pub(crate) fn push_expr_scope(
        self,
        expr_scopes: Arc<ExprScopes>,
        scope_id: ScopeId,
    ) -> Resolver {
        self.push_scope(Scope::ExprScope(ExprScope { expr_scopes, scope_id }))
    }
}

impl Scope {
    fn resolve_name(&self, db: &impl HirDatabase, name: &Name) -> PerNs<Resolution> {
        match self {
            Scope::ModuleScope(m) => {
                if let Some(KnownName::SelfParam) = name.as_known_name() {
                    PerNs::types(Resolution::Def(m.crate_def_map.mk_module(m.module_id).into()))
                } else {
                    m.crate_def_map
                        .resolve_name_in_module(db, m.module_id, name)
                        .map(Resolution::Def)
                }
            }
            Scope::GenericParams(gp) => match gp.find_by_name(name) {
                Some(gp) => PerNs::types(Resolution::GenericParam(gp.idx)),
                None => PerNs::none(),
            },
            Scope::ImplBlockScope(i) => {
                if name.as_known_name() == Some(KnownName::SelfType) {
                    PerNs::types(Resolution::SelfType(i.clone()))
                } else {
                    PerNs::none()
                }
            }
            Scope::ExprScope(e) => {
                let entry =
                    e.expr_scopes.entries(e.scope_id).iter().find(|entry| entry.name() == name);
                match entry {
                    Some(e) => PerNs::values(Resolution::LocalBinding(e.pat())),
                    None => PerNs::none(),
                }
            }
        }
    }

    fn collect_names(&self, db: &impl HirDatabase, f: &mut dyn FnMut(Name, PerNs<Resolution>)) {
        match self {
            Scope::ModuleScope(m) => {
                // FIXME: should we provide `self` here?
                // f(
                //     Name::self_param(),
                //     PerNs::types(Resolution::Def {
                //         def: m.module.into(),
                //     }),
                // );
                m.crate_def_map[m.module_id].scope.entries().for_each(|(name, res)| {
                    f(name.clone(), res.def.map(Resolution::Def));
                });
                m.crate_def_map.extern_prelude().iter().for_each(|(name, def)| {
                    f(name.clone(), PerNs::types(Resolution::Def(*def)));
                });
                if let Some(prelude) = m.crate_def_map.prelude() {
                    let prelude_def_map = db.crate_def_map(prelude.krate);
                    prelude_def_map[prelude.module_id].scope.entries().for_each(|(name, res)| {
                        f(name.clone(), res.def.map(Resolution::Def));
                    });
                }
            }
            Scope::GenericParams(gp) => {
                for param in &gp.params {
                    f(param.name.clone(), PerNs::types(Resolution::GenericParam(param.idx)))
                }
            }
            Scope::ImplBlockScope(i) => {
                f(Name::self_type(), PerNs::types(Resolution::SelfType(i.clone())));
            }
            Scope::ExprScope(e) => {
                e.expr_scopes.entries(e.scope_id).iter().for_each(|e| {
                    f(e.name().clone(), PerNs::values(Resolution::LocalBinding(e.pat())));
                });
            }
        }
    }
}
<|endoftext|>


<commit_before>use crate::{SourceFile, validation, TextUnit, TextRange, AstNode};
use ra_text_edit::AtomTextEdit;
use std::str::{self, FromStr};

fn check_file_invariants(file: &SourceFile) {
    let root = file.syntax();
    validation::validate_block_structure(root);
    let _ = file.errors();
}

pub fn check_parser(text: &str) {
    let file = SourceFile::parse(text);
    check_file_invariants(&file);
}

#[derive(Debug, Clone)]
pub struct CheckReparse {
    text: String,
    edit: AtomTextEdit,
    edited_text: String,
}

impl CheckReparse {
    pub fn from_data(data: &[u8]) -> Option<Self> {
        let data = str::from_utf8(data).ok()?;
        let mut lines = data.lines();
        let delete_start = usize::from_str(lines.next()?).ok()?;
        let delete_len = usize::from_str(lines.next()?).ok()?;
        let insert = lines.next()?.to_string();
        let text = lines.collect::<Vec<_>>().join("\n");
        text.get(delete_start..delete_start.checked_add(delete_len)?)?; // make sure delete is a valid range
        let delete = TextRange::offset_len(
            TextUnit::from_usize(delete_start),
            TextUnit::from_usize(delete_len),
        );
        let edited_text =
            format!("{}{}{}", &text[..delete_start], &insert, &text[delete_start + delete_len..]);
        let edit = AtomTextEdit { delete, insert };
        Some(CheckReparse { text, edit, edited_text })
    }

    pub fn run(&self) {
        let file = SourceFile::parse(&self.text);
        let new_file = file.reparse(&self.edit);
        check_file_invariants(&new_file);
        assert_eq!(&new_file.syntax().text().to_string(), &self.edited_text);
        let full_reparse = SourceFile::parse(&self.edited_text);
        for (a, b) in new_file.syntax().descendants().zip(full_reparse.syntax().descendants()) {
            if (a.kind(), a.range()) != (b.kind(), b.range()) {
                eprint!("original:\n{}", file.syntax().debug_dump());
                eprint!("reparsed:\n{}", new_file.syntax().debug_dump());
                eprint!("full reparse:\n{}", full_reparse.syntax().debug_dump());
                assert_eq!(
                    format!("{:?}", a),
                    format!("{:?}", b),
                    "different syntax tree produced by the full reparse"
                );
            }
        }
        // FIXME
        // assert_eq!(new_file.errors(), full_reparse.errors());
        assert_eq!(new_file.errors().is_empty(), full_reparse.errors().is_empty());
    }
}
<commit_msg>Use template text to improve reparse fuzzing<commit_after>use crate::{SourceFile, validation, TextUnit, TextRange, AstNode};
use ra_text_edit::AtomTextEdit;
use std::str::{self, FromStr};

fn check_file_invariants(file: &SourceFile) {
    let root = file.syntax();
    validation::validate_block_structure(root);
    let _ = file.errors();
}

pub fn check_parser(text: &str) {
    let file = SourceFile::parse(text);
    check_file_invariants(&file);
}

#[derive(Debug, Clone)]
pub struct CheckReparse {
    text: String,
    edit: AtomTextEdit,
    edited_text: String,
}

impl CheckReparse {
    pub fn from_data(data: &[u8]) -> Option<Self> {
        const PREFIX: &'static str = "fn main(){\n\t";
        const SUFFIX: &'static str = "\n}";

        let data = str::from_utf8(data).ok()?;
        let mut lines = data.lines();
        let delete_start = usize::from_str(lines.next()?).ok()? + PREFIX.len();
        let delete_len = usize::from_str(lines.next()?).ok()?;
        let insert = lines.next()?.to_string();
        let text = lines.collect::<Vec<_>>().join("\n");
        let text = format!("{}{}{}", PREFIX, text, SUFFIX);
        text.get(delete_start..delete_start.checked_add(delete_len)?)?; // make sure delete is a valid range
        let delete = TextRange::offset_len(
            TextUnit::from_usize(delete_start),
            TextUnit::from_usize(delete_len),
        );
        let edited_text =
            format!("{}{}{}", &text[..delete_start], &insert, &text[delete_start + delete_len..]);
        let edit = AtomTextEdit { delete, insert };
        Some(CheckReparse { text, edit, edited_text })
    }

    pub fn run(&self) {
        let file = SourceFile::parse(&self.text);
        let new_file = file.reparse(&self.edit);
        check_file_invariants(&new_file);
        assert_eq!(&new_file.syntax().text().to_string(), &self.edited_text);
        let full_reparse = SourceFile::parse(&self.edited_text);
        for (a, b) in new_file.syntax().descendants().zip(full_reparse.syntax().descendants()) {
            if (a.kind(), a.range()) != (b.kind(), b.range()) {
                eprint!("original:\n{}", file.syntax().debug_dump());
                eprint!("reparsed:\n{}", new_file.syntax().debug_dump());
                eprint!("full reparse:\n{}", full_reparse.syntax().debug_dump());
                assert_eq!(
                    format!("{:?}", a),
                    format!("{:?}", b),
                    "different syntax tree produced by the full reparse"
                );
            }
        }
        // FIXME
        // assert_eq!(new_file.errors(), full_reparse.errors());
        assert_eq!(new_file.errors().is_empty(), full_reparse.errors().is_empty());
    }
}
<|endoftext|>


<commit_before>
use self::mocks::new_fixture;
use memory::Memory;

#[test]
fn ram_memory_mapped_read() {
    let mut fixture = new_fixture();

    for addr in 0..0x800 {
        fixture.ram[addr] = (addr & 0xfe) as u8;
    }

    for addr in 0..0x2000_u16 {
        let expect = (addr & 0xfe) as u8;
        let val = fixture.read(addr).unwrap();
        assert_eq!(expect, val);
    }
}

#[test]
fn ram_memory_mapped_write() {
    let mut fixture = new_fixture();
    for addr in 0..0x2000_u16 {
        let ram_index = (addr & 0x7ff) as usize;
        fixture.write(addr, 0xff).unwrap();
        assert_eq!(0xff, fixture.ram[ram_index]);
        fixture.ram[ram_index] = 0; // reset it after asserting it was written correctly
    }
}

#[test]
fn ppu_memory_mapped_read() {
    let mut fixture = new_fixture();
    fixture.ppu.set_value(0xff);
    for addr in 0x2000..0x2008_u16 {
        let val = fixture.read(addr).unwrap();
        assert_eq!(0xff, val);
    }
}

#[test]
fn ppu_memory_mapped_write() {
    let mut fixture = new_fixture();
    for addr in 0x2000..0x2008_u16 {
        fixture.write(addr, 0xff).unwrap();
        assert_eq!(addr, fixture.ppu.addr());
        assert_eq!(0xff, fixture.ppu.value());
    }
}

#[test]
fn apu_memory_mapped_read() {
    let mut fixture = new_fixture();
    fixture.apu.set_control(0xff);
    // Only a single APU address is readable
    let val = fixture.read(0x4015).unwrap();
    assert_eq!(0xff, val);
}

#[test]
fn apu_memory_mapped_write() {
    let mut fixture = new_fixture();

    for addr in 0x4000..0x4016_u16 {
        fixture.write(addr, 0xff).unwrap();
        assert_eq!(addr, fixture.apu.write_addr());
        assert_eq!(0xff, fixture.apu.write_value());
    }

    for addr in 0x4017..0x4018_u16 {
        fixture.write(addr, 0xff).unwrap();
        assert_eq!(addr, fixture.apu.write_addr());
        assert_eq!(0xff, fixture.apu.write_value());
    }
}

#[test]
fn input_memory_mapped_read() {
    let mut fixture = new_fixture();
    fixture.input.set_joy1(0xfe);
    fixture.input.set_joy2(0xff);
    let joy1_val = fixture.read(0x4016).unwrap();
    let joy2_val = fixture.read(0x4017).unwrap();
    assert_eq!(0xfe, joy1_val);
    assert_eq!(0xff, joy2_val);
}

#[test]
fn input_memory_mapped_write() {
    let mut fixture = new_fixture();
    fixture.write(0x4016, 0xff).unwrap();
    assert_eq!(0xff, fixture.input.probe());
}

mod mocks {
    use apu::Apu;
    use errors::*;
    use input::Input;
    use memory::nes_memory::{NesMemoryBase, StepAction};
    use ppu::Ppu;
    use rom::*;
    use std::io::Write;

    #[derive(Copy, Clone, Default)]
    pub struct InputMock {
        probe: u8,
        joy1: u8,
        joy2: u8,
    }

    impl InputMock {
        pub fn probe(&self) -> u8 {
            self.probe
        }

        pub fn set_joy1(&mut self, val: u8) {
            self.joy1 = val;
        }

        pub fn set_joy2(&mut self, val: u8) {
            self.joy2 = val;
        }
    }

    impl Input for InputMock {
        fn write_probe(&mut self, val: u8) {
            self.probe = val;
        }

        fn read_joy_1(&self) -> u8 {
            self.joy1
        }

        fn read_joy_2(&self) -> u8 {
            self.joy2
        }
    }

    #[derive(Copy, Clone, Default)]
    pub struct ApuMock {
        write_addr: u16,
        write_value: u8,
        control: u8,
    }

    impl ApuMock {
        pub fn write_addr(&self) -> u16 {
            self.write_addr
        }

        pub fn write_value(&self) -> u8 {
            self.write_value
        }

        pub fn set_control(&mut self, val: u8) {
            self.control = val;
        }
    }

    impl Apu for ApuMock {
        fn write(&mut self, addr: u16, value: u8) {
            self.write_addr = addr;
            self.write_value = value;
        }

        fn read_control(&self) -> u8 {
            self.control
        }
    }

    #[derive(Copy, Clone, Default)]
    pub struct PpuMock {
        addr: u16,
        value: u8,
    }

    impl PpuMock {
        pub fn addr(&self) -> u16 {
            self.addr
        }

        pub fn value(&self) -> u8 {
            self.value
        }

        pub fn set_value(&mut self, value: u8) {
            self.value = value;
        }
    }

    impl Ppu for PpuMock {
        fn write(&mut self, addr: u16, val: u8) -> Result<()> {
            self.addr = addr;
            self.value = val;
            Ok(())
        }

        fn read(&self, _: u16) -> Result<u8> {
            Ok(self.value)
        }

        fn step(&mut self) -> StepAction {
            unimplemented!()
        }

        fn dump_registers<T: Write>(&self, _: &mut T) -> usize {
            unimplemented!()
        }
    }

    pub type NesMemoryFixture = NesMemoryBase<PpuMock, ApuMock, InputMock>;

    pub fn new_fixture() -> NesMemoryFixture {
        let rom = NesRom {
            format: RomFormat::INes,
            video_standard: VideoStandard::Ntsc,
            mapper: 0,
            mirroring: Mirroring::Horizontal,
            prg_rom_banks: 1,
            prg_ram_banks: 1,
            chr_rom_banks: 1,
            has_sram: false,
            has_trainer: false,
            is_pc10: false,
            is_vs_unisystem: false,
            trainer: Vec::new(),
            chr: Vec::new(),
            prg: Vec::new(),
        };

        NesMemoryBase {
            ram: [0_u8; 0x800],
            rom: rom,
            ppu: PpuMock::default(),
            apu: ApuMock::default(),
            input: InputMock::default(),
        }
    }
}
<commit_msg>Formatting<commit_after>use self::mocks::new_fixture;
use memory::Memory;

#[test]
fn ram_memory_mapped_read() {
    let mut fixture = new_fixture();

    for addr in 0..0x800 {
        fixture.ram[addr] = (addr & 0xfe) as u8;
    }

    for addr in 0..0x2000_u16 {
        let expect = (addr & 0xfe) as u8;
        let val = fixture.read(addr).unwrap();
        assert_eq!(expect, val);
    }
}

#[test]
fn ram_memory_mapped_write() {
    let mut fixture = new_fixture();
    for addr in 0..0x2000_u16 {
        let ram_index = (addr & 0x7ff) as usize;
        fixture.write(addr, 0xff).unwrap();
        assert_eq!(0xff, fixture.ram[ram_index]);
        fixture.ram[ram_index] = 0; // reset it after asserting it was written correctly
    }
}

#[test]
fn ppu_memory_mapped_read() {
    let mut fixture = new_fixture();
    fixture.ppu.set_value(0xff);
    for addr in 0x2000..0x2008_u16 {
        let val = fixture.read(addr).unwrap();
        assert_eq!(0xff, val);
    }
}

#[test]
fn ppu_memory_mapped_write() {
    let mut fixture = new_fixture();
    for addr in 0x2000..0x2008_u16 {
        fixture.write(addr, 0xff).unwrap();
        assert_eq!(addr, fixture.ppu.addr());
        assert_eq!(0xff, fixture.ppu.value());
    }
}

#[test]
fn apu_memory_mapped_read() {
    let mut fixture = new_fixture();
    fixture.apu.set_control(0xff);
    // Only a single APU address is readable
    let val = fixture.read(0x4015).unwrap();
    assert_eq!(0xff, val);
}

#[test]
fn apu_memory_mapped_write() {
    let mut fixture = new_fixture();

    for addr in 0x4000..0x4016_u16 {
        fixture.write(addr, 0xff).unwrap();
        assert_eq!(addr, fixture.apu.write_addr());
        assert_eq!(0xff, fixture.apu.write_value());
    }

    for addr in 0x4017..0x4018_u16 {
        fixture.write(addr, 0xff).unwrap();
        assert_eq!(addr, fixture.apu.write_addr());
        assert_eq!(0xff, fixture.apu.write_value());
    }
}

#[test]
fn input_memory_mapped_read() {
    let mut fixture = new_fixture();
    fixture.input.set_joy1(0xfe);
    fixture.input.set_joy2(0xff);
    let joy1_val = fixture.read(0x4016).unwrap();
    let joy2_val = fixture.read(0x4017).unwrap();
    assert_eq!(0xfe, joy1_val);
    assert_eq!(0xff, joy2_val);
}

#[test]
fn input_memory_mapped_write() {
    let mut fixture = new_fixture();
    fixture.write(0x4016, 0xff).unwrap();
    assert_eq!(0xff, fixture.input.probe());
}

mod mocks {
    use apu::Apu;
    use errors::*;
    use input::Input;
    use memory::nes_memory::{NesMemoryBase, StepAction};
    use ppu::Ppu;
    use rom::*;
    use std::io::Write;

    #[derive(Copy, Clone, Default)]
    pub struct InputMock {
        probe: u8,
        joy1: u8,
        joy2: u8,
    }

    impl InputMock {
        pub fn probe(&self) -> u8 {
            self.probe
        }

        pub fn set_joy1(&mut self, val: u8) {
            self.joy1 = val;
        }

        pub fn set_joy2(&mut self, val: u8) {
            self.joy2 = val;
        }
    }

    impl Input for InputMock {
        fn write_probe(&mut self, val: u8) {
            self.probe = val;
        }

        fn read_joy_1(&self) -> u8 {
            self.joy1
        }

        fn read_joy_2(&self) -> u8 {
            self.joy2
        }
    }

    #[derive(Copy, Clone, Default)]
    pub struct ApuMock {
        write_addr: u16,
        write_value: u8,
        control: u8,
    }

    impl ApuMock {
        pub fn write_addr(&self) -> u16 {
            self.write_addr
        }

        pub fn write_value(&self) -> u8 {
            self.write_value
        }

        pub fn set_control(&mut self, val: u8) {
            self.control = val;
        }
    }

    impl Apu for ApuMock {
        fn write(&mut self, addr: u16, value: u8) {
            self.write_addr = addr;
            self.write_value = value;
        }

        fn read_control(&self) -> u8 {
            self.control
        }
    }

    #[derive(Copy, Clone, Default)]
    pub struct PpuMock {
        addr: u16,
        value: u8,
    }

    impl PpuMock {
        pub fn addr(&self) -> u16 {
            self.addr
        }

        pub fn value(&self) -> u8 {
            self.value
        }

        pub fn set_value(&mut self, value: u8) {
            self.value = value;
        }
    }

    impl Ppu for PpuMock {
        fn write(&mut self, addr: u16, val: u8) -> Result<()> {
            self.addr = addr;
            self.value = val;
            Ok(())
        }

        fn read(&self, _: u16) -> Result<u8> {
            Ok(self.value)
        }

        fn step(&mut self) -> StepAction {
            unimplemented!()
        }

        fn dump_registers<T: Write>(&self, _: &mut T) {
            unimplemented!()
        }
    }

    pub type NesMemoryFixture = NesMemoryBase<PpuMock, ApuMock, InputMock>;

    pub fn new_fixture() -> NesMemoryFixture {
        let rom = NesRom {
            format: RomFormat::INes,
            video_standard: VideoStandard::Ntsc,
            mapper: 0,
            mirroring: Mirroring::Horizontal,
            prg_rom_banks: 1,
            prg_ram_banks: 1,
            chr_rom_banks: 1,
            has_sram: false,
            has_trainer: false,
            is_pc10: false,
            is_vs_unisystem: false,
            trainer: Vec::new(),
            chr: Vec::new(),
            prg: Vec::new(),
        };

        NesMemoryBase {
            ram: [0_u8; 0x800],
            rom: rom,
            ppu: PpuMock::default(),
            apu: ApuMock::default(),
            input: InputMock::default(),
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2013-2015, The Gtk-rs Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

use ffi;
use glib::GString;
use glib_ffi;
use std::ptr;
use std::mem;
use glib::translate::*;

#[derive(Copy, Clone, PartialEq, Eq)]
pub struct Atom(ffi::GdkAtom);

pub const NONE: Atom = Atom(0 as *mut _);
pub const SELECTION_PRIMARY: Atom = Atom(1 as *mut _);
pub const SELECTION_SECONDARY: Atom = Atom(2 as *mut _);
pub const SELECTION_CLIPBOARD: Atom = Atom(69 as *mut _);
pub const TARGET_BITMAP: Atom = Atom(5 as *mut _);
pub const TARGET_COLORMAP: Atom = Atom(7 as *mut _);
pub const TARGET_DRAWABLE: Atom = Atom(17 as *mut _);
pub const TARGET_PIXMAP: Atom = Atom(20 as *mut _);
pub const TARGET_STRING: Atom = Atom(31 as *mut _);
pub const SELECTION_TYPE_ATOM: Atom = Atom(4 as *mut _);
pub const SELECTION_TYPE_BITMAP: Atom = Atom(5 as *mut _);
pub const SELECTION_TYPE_COLORMAP: Atom = Atom(7 as *mut _);
pub const SELECTION_TYPE_DRAWABLE: Atom = Atom(17 as *mut _);
pub const SELECTION_TYPE_INTEGER: Atom = Atom(19 as *mut _);
pub const SELECTION_TYPE_PIXMAP: Atom = Atom(20 as *mut _);
pub const SELECTION_TYPE_WINDOW: Atom = Atom(33 as *mut _);
pub const SELECTION_TYPE_STRING: Atom = Atom(31 as *mut _);

impl Atom {
    pub fn intern(atom_name: &str) -> Atom {
        assert_initialized_main_thread!();
        unsafe { Atom(ffi::gdk_atom_intern(atom_name.to_glib_none().0, false.to_glib())) }
    }

    pub fn name(self) -> GString {
        unsafe { from_glib_full(ffi::gdk_atom_name(self.0)) }
    }

    pub unsafe fn value(self) -> usize {
        self.0 as usize
    }
}

impl GlibPtrDefault for Atom {
    type GlibType = ffi::GdkAtom;
}

#[doc(hidden)]
impl Uninitialized for Atom {
    #[inline]
    unsafe fn uninitialized() -> Self {
        mem::uninitialized()
    }
}

impl<'a> ToGlibPtr<'a, ffi::GdkAtom> for Atom {
    type Storage = ();

    #[inline]
    fn to_glib_none(&self) -> Stash<'a, ffi::GdkAtom, Atom> {
        Stash(self.0, ())
    }
}

impl<'a> ToGlibPtrMut<'a, *mut ffi::GdkAtom> for Atom {
    type Storage = ();

    #[inline]
    fn to_glib_none_mut(&'a mut self) -> StashMut<'a, *mut ffi::GdkAtom, Atom> {
        StashMut(&mut self.0, ())
    }
}

impl<'a> ToGlibContainerFromSlice<'a, *mut ffi::GdkAtom> for &'a Atom {
    type Storage = (Vec<Stash<'a, ffi::GdkAtom, &'a Atom>>, Option<Vec<ffi::GdkAtom>>);

    fn to_glib_none_from_slice(t: &'a [&'a Atom]) -> (*mut ffi::GdkAtom, Self::Storage) {
        skip_assert_initialized!();

        let v: Vec<_> = t.iter().map(|s| s.to_glib_none()).collect();
        let mut v_ptr: Vec<_> = v.iter().map(|s| s.0).collect();
        v_ptr.push(ptr::null_mut());

        (v_ptr.as_ptr() as *mut ffi::GdkAtom, (v, Some(v_ptr)))
    }

    fn to_glib_container_from_slice(t: &'a [&'a Atom]) -> (*mut ffi::GdkAtom, Self::Storage) {
        skip_assert_initialized!();

        let v: Vec<_> = t.iter().map(|s| s.to_glib_none()).collect();

        let v_ptr = unsafe {
            let v_ptr = glib_ffi::g_malloc0(mem::size_of::<ffi::GdkAtom>() * t.len() + 1) as *mut ffi::GdkAtom;

            for (i, s) in v.iter().enumerate() {
                ptr::write(v_ptr.offset(i as isize), s.0);
            }

            v_ptr
        };

        (v_ptr, (v, None))
    }

    fn to_glib_full_from_slice(_: &[&'a Atom]) -> *mut ffi::GdkAtom {
        skip_assert_initialized!();

        unimplemented!()
    }
}

impl FromGlibPtrNone<ffi::GdkAtom> for Atom {
    #[inline]
    unsafe fn from_glib_none(ptr: ffi::GdkAtom) -> Atom { Atom(ptr) }
}

impl FromGlibPtrFull<ffi::GdkAtom> for Atom {
    #[inline]
    unsafe fn from_glib_full(_: ffi::GdkAtom) -> Atom { unimplemented!() }
}

impl FromGlibContainerAsVec<ffi::GdkAtom, *mut ffi::GdkAtom> for Atom {
    unsafe fn from_glib_none_num_as_vec(ptr: *mut ffi::GdkAtom, num: usize) -> Vec<Self> {
        if num == 0 || ptr.is_null() {
            return Vec::new();
        }

        let mut res = Vec::with_capacity(num);
        for i in 0..num {
            res.push(from_glib_none(ptr::read(ptr.offset(i as isize))));
        }
        res
    }

    unsafe fn from_glib_container_num_as_vec(ptr: *mut ffi::GdkAtom, num: usize) -> Vec<Self> {
        let res = FromGlibContainerAsVec::from_glib_none_num_as_vec(ptr, num);
        glib_ffi::g_free(ptr as *mut _);
        res
    }

    unsafe fn from_glib_full_num_as_vec(ptr: *mut ffi::GdkAtom, num: usize) -> Vec<Self> {
        if num == 0 || ptr.is_null() {
            return Vec::new();
        }

        let mut res = Vec::with_capacity(num);
        for i in 0..num {
            res.push(from_glib_full(ptr::read(ptr.offset(i as isize))));
        }
        glib_ffi::g_free(ptr as *mut _);
        res
    }
}

impl FromGlibPtrArrayContainerAsVec<ffi::GdkAtom, *mut ffi::GdkAtom> for Atom {
    unsafe fn from_glib_none_as_vec(ptr: *mut ffi::GdkAtom) -> Vec<Self> {
        FromGlibContainerAsVec::from_glib_none_num_as_vec(ptr, c_ptr_array_len(ptr))
    }

    unsafe fn from_glib_container_as_vec(ptr: *mut ffi::GdkAtom) -> Vec<Self> {
        FromGlibContainerAsVec::from_glib_container_num_as_vec(ptr, c_ptr_array_len(ptr))
    }

    unsafe fn from_glib_full_as_vec(ptr: *mut ffi::GdkAtom) -> Vec<Self> {
        FromGlibContainerAsVec::from_glib_full_num_as_vec(ptr, c_ptr_array_len(ptr))
    }
}

impl<'a> From<&'a str> for Atom {
    fn from(r: &'a str) -> Atom {
        skip_assert_initialized!();
        Atom::intern(r)
    }
}
<commit_msg>Support const array of gdk::Atom<commit_after>// Copyright 2013-2015, The Gtk-rs Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

use ffi;
use glib::GString;
use glib_ffi;
use std::ptr;
use std::mem;
use glib::translate::*;

#[derive(Copy, Clone, PartialEq, Eq)]
pub struct Atom(ffi::GdkAtom);

pub const NONE: Atom = Atom(0 as *mut _);
pub const SELECTION_PRIMARY: Atom = Atom(1 as *mut _);
pub const SELECTION_SECONDARY: Atom = Atom(2 as *mut _);
pub const SELECTION_CLIPBOARD: Atom = Atom(69 as *mut _);
pub const TARGET_BITMAP: Atom = Atom(5 as *mut _);
pub const TARGET_COLORMAP: Atom = Atom(7 as *mut _);
pub const TARGET_DRAWABLE: Atom = Atom(17 as *mut _);
pub const TARGET_PIXMAP: Atom = Atom(20 as *mut _);
pub const TARGET_STRING: Atom = Atom(31 as *mut _);
pub const SELECTION_TYPE_ATOM: Atom = Atom(4 as *mut _);
pub const SELECTION_TYPE_BITMAP: Atom = Atom(5 as *mut _);
pub const SELECTION_TYPE_COLORMAP: Atom = Atom(7 as *mut _);
pub const SELECTION_TYPE_DRAWABLE: Atom = Atom(17 as *mut _);
pub const SELECTION_TYPE_INTEGER: Atom = Atom(19 as *mut _);
pub const SELECTION_TYPE_PIXMAP: Atom = Atom(20 as *mut _);
pub const SELECTION_TYPE_WINDOW: Atom = Atom(33 as *mut _);
pub const SELECTION_TYPE_STRING: Atom = Atom(31 as *mut _);

impl Atom {
    pub fn intern(atom_name: &str) -> Atom {
        assert_initialized_main_thread!();
        unsafe { Atom(ffi::gdk_atom_intern(atom_name.to_glib_none().0, false.to_glib())) }
    }

    pub fn name(self) -> GString {
        unsafe { from_glib_full(ffi::gdk_atom_name(self.0)) }
    }

    pub unsafe fn value(self) -> usize {
        self.0 as usize
    }
}

impl GlibPtrDefault for Atom {
    type GlibType = ffi::GdkAtom;
}

#[doc(hidden)]
impl Uninitialized for Atom {
    #[inline]
    unsafe fn uninitialized() -> Self {
        mem::uninitialized()
    }
}

impl<'a> ToGlibPtr<'a, ffi::GdkAtom> for Atom {
    type Storage = ();

    #[inline]
    fn to_glib_none(&self) -> Stash<'a, ffi::GdkAtom, Atom> {
        Stash(self.0, ())
    }
}

impl<'a> ToGlibPtrMut<'a, *mut ffi::GdkAtom> for Atom {
    type Storage = ();

    #[inline]
    fn to_glib_none_mut(&'a mut self) -> StashMut<'a, *mut ffi::GdkAtom, Atom> {
        StashMut(&mut self.0, ())
    }
}

impl<'a> ToGlibContainerFromSlice<'a, *mut ffi::GdkAtom> for &'a Atom {
    type Storage = (Vec<Stash<'a, ffi::GdkAtom, &'a Atom>>, Option<Vec<ffi::GdkAtom>>);

    fn to_glib_none_from_slice(t: &'a [&'a Atom]) -> (*mut ffi::GdkAtom, Self::Storage) {
        skip_assert_initialized!();

        let v: Vec<_> = t.iter().map(|s| s.to_glib_none()).collect();
        let mut v_ptr: Vec<_> = v.iter().map(|s| s.0).collect();
        v_ptr.push(ptr::null_mut());

        (v_ptr.as_ptr() as *mut ffi::GdkAtom, (v, Some(v_ptr)))
    }

    fn to_glib_container_from_slice(t: &'a [&'a Atom]) -> (*mut ffi::GdkAtom, Self::Storage) {
        skip_assert_initialized!();

        let v: Vec<_> = t.iter().map(|s| s.to_glib_none()).collect();

        let v_ptr = unsafe {
            let v_ptr = glib_ffi::g_malloc0(mem::size_of::<ffi::GdkAtom>() * (t.len() + 1)) as *mut ffi::GdkAtom;

            for (i, s) in v.iter().enumerate() {
                ptr::write(v_ptr.offset(i as isize), s.0);
            }

            v_ptr
        };

        (v_ptr, (v, None))
    }

    fn to_glib_full_from_slice(_: &[&'a Atom]) -> *mut ffi::GdkAtom {
        skip_assert_initialized!();

        unimplemented!()
    }
}

impl<'a> ToGlibContainerFromSlice<'a, *const ffi::GdkAtom> for &'a Atom {
    type Storage = (Vec<Stash<'a, ffi::GdkAtom, &'a Atom>>, Option<Vec<ffi::GdkAtom>>);

    fn to_glib_none_from_slice(t: &'a [&'a Atom]) -> (*const ffi::GdkAtom, Self::Storage) {
        skip_assert_initialized!();

        let v: Vec<_> = t.iter().map(|s| s.to_glib_none()).collect();
        let mut v_ptr: Vec<_> = v.iter().map(|s| s.0).collect();
        v_ptr.push(ptr::null_mut());

        (v_ptr.as_ptr() as *const ffi::GdkAtom, (v, Some(v_ptr)))
    }

    fn to_glib_container_from_slice(t: &'a [&'a Atom]) -> (*const ffi::GdkAtom, Self::Storage) {
        skip_assert_initialized!();

        let v: Vec<_> = t.iter().map(|s| s.to_glib_none()).collect();

        let v_ptr = unsafe {
            let v_ptr = glib_ffi::g_malloc0(mem::size_of::<ffi::GdkAtom>() * (t.len() + 1)) as *mut ffi::GdkAtom;

            for (i, s) in v.iter().enumerate() {
                ptr::write(v_ptr.offset(i as isize), s.0);
            }

            v_ptr as *const ffi::GdkAtom
        };

        (v_ptr, (v, None))
    }

    fn to_glib_full_from_slice(_: &[&'a Atom]) -> *const ffi::GdkAtom {
        skip_assert_initialized!();

        unimplemented!()
    }
}

impl FromGlibPtrNone<ffi::GdkAtom> for Atom {
    #[inline]
    unsafe fn from_glib_none(ptr: ffi::GdkAtom) -> Atom { Atom(ptr) }
}

impl FromGlibPtrFull<ffi::GdkAtom> for Atom {
    #[inline]
    unsafe fn from_glib_full(_: ffi::GdkAtom) -> Atom { unimplemented!() }
}

impl FromGlibContainerAsVec<ffi::GdkAtom, *mut ffi::GdkAtom> for Atom {
    unsafe fn from_glib_none_num_as_vec(ptr: *mut ffi::GdkAtom, num: usize) -> Vec<Self> {
        if num == 0 || ptr.is_null() {
            return Vec::new();
        }

        let mut res = Vec::with_capacity(num);
        for i in 0..num {
            res.push(from_glib_none(ptr::read(ptr.offset(i as isize))));
        }
        res
    }

    unsafe fn from_glib_container_num_as_vec(ptr: *mut ffi::GdkAtom, num: usize) -> Vec<Self> {
        let res = FromGlibContainerAsVec::from_glib_none_num_as_vec(ptr, num);
        glib_ffi::g_free(ptr as *mut _);
        res
    }

    unsafe fn from_glib_full_num_as_vec(ptr: *mut ffi::GdkAtom, num: usize) -> Vec<Self> {
        if num == 0 || ptr.is_null() {
            return Vec::new();
        }

        let mut res = Vec::with_capacity(num);
        for i in 0..num {
            res.push(from_glib_full(ptr::read(ptr.offset(i as isize))));
        }
        glib_ffi::g_free(ptr as *mut _);
        res
    }
}

impl FromGlibPtrArrayContainerAsVec<ffi::GdkAtom, *mut ffi::GdkAtom> for Atom {
    unsafe fn from_glib_none_as_vec(ptr: *mut ffi::GdkAtom) -> Vec<Self> {
        FromGlibContainerAsVec::from_glib_none_num_as_vec(ptr, c_ptr_array_len(ptr))
    }

    unsafe fn from_glib_container_as_vec(ptr: *mut ffi::GdkAtom) -> Vec<Self> {
        FromGlibContainerAsVec::from_glib_container_num_as_vec(ptr, c_ptr_array_len(ptr))
    }

    unsafe fn from_glib_full_as_vec(ptr: *mut ffi::GdkAtom) -> Vec<Self> {
        FromGlibContainerAsVec::from_glib_full_num_as_vec(ptr, c_ptr_array_len(ptr))
    }
}

impl<'a> From<&'a str> for Atom {
    fn from(r: &'a str) -> Atom {
        skip_assert_initialized!();
        Atom::intern(r)
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::fmt;
use std::io;
use std::ops::Index;

use byteorder::WriteBytesExt;
use flate2::Compression;
use flate2::read::{GzDecoder, ZlibDecoder};
use flate2::write::{GzEncoder, ZlibEncoder};

use error::{Error, Result};
use raw;
use value::Value;

/// A generic, complete object in Named Binary Tag format.
///
/// This is essentially a map of names to `Value`s, with an optional top-level
/// name of its own. It can be created in a similar way to a `HashMap`, or read
/// from an `io::Read` source, and its binary representation can be written to
/// an `io::Write` destination.
///
/// These read and write methods support both uncompressed and compressed
/// (through Gzip or zlib compression) methods.
///
/// ```rust
/// use nbt::{Blob, Value};
///
/// // Create a `Blob` from key/value pairs.
/// let mut nbt = Blob::new();
/// nbt.insert("name", "Herobrine").unwrap();
/// nbt.insert("health", 100i8).unwrap();
/// nbt.insert("food", 20.0f32).unwrap();
///
/// // Write a compressed binary representation to a byte array.
/// let mut dst = Vec::new();
/// nbt.to_zlib_writer(&mut dst).unwrap();
/// ```
#[derive(Clone, Debug, PartialEq)]
pub struct Blob {
    title: String,
    content: HashMap<String, Value>
}

impl Blob {
    /// Create a new NBT file format representation with an empty name.
    pub fn new() -> Blob {
        Blob { title: "".to_string(), content: HashMap::new() }
    }

    /// Create a new NBT file format representation with the given name.
    pub fn named<S>(name: S) -> Blob where S: Into<String> {
        Blob { title: name.into(), content: HashMap::new() }
    }

    /// Extracts an `Blob` object from an `io::Read` source.
    pub fn from_reader<R>(src: &mut R) -> Result<Blob>
        where R: io::Read
    {
        let (tag, title) = try!(raw::emit_next_header(src));
        // Although it would be possible to read NBT format files composed of
        // arbitrary objects using the current API, by convention all files
        // have a top-level Compound.
        if tag != 0x0a {
            return Err(Error::NoRootCompound);
        }
        let content = try!(Value::from_reader(tag, src));
        match content {
            Value::Compound(map) => Ok(Blob { title: title, content: map }),
            _ => Err(Error::NoRootCompound),
        }
    }

    /// Extracts an `Blob` object from an `io::Read` source that is
    /// compressed using the Gzip format.
    pub fn from_gzip_reader<R>(src: &mut R) -> Result<Blob>
        where R: io::Read
    {
        // Reads the gzip header, and fails if it is incorrect.
        let mut data = try!(GzDecoder::new(src));
        Blob::from_reader(&mut data)
    }

    /// Extracts an `Blob` object from an `io::Read` source that is
    /// compressed using the zlib format.
    pub fn from_zlib_reader<R>(src: &mut R) -> Result<Blob>
        where R: io::Read
    {
        Blob::from_reader(&mut ZlibDecoder::new(src))
    }

    /// Writes the binary representation of this `Blob` to an `io::Write`
    /// destination.
    pub fn to_writer<W>(&self, mut dst: &mut W) -> Result<()>
        where W: io::Write
    {
        dst.write_u8(0x0a)?;
        raw::write_bare_string(&mut dst, &self.title)?;
        for (name, ref nbt) in self.content.iter() {
            dst.write_u8(nbt.id())?;
            raw::write_bare_string(&mut dst, name)?;
            nbt.to_writer(&mut dst)?;
        }
        raw::close_nbt(&mut dst)
    }

    /// Writes the binary representation of this `Blob`, compressed using
    /// the Gzip format, to an `io::Write` destination.
    pub fn to_gzip_writer<W>(&self, dst: &mut W) -> Result<()>
        where W: io::Write
    {
        self.to_writer(&mut GzEncoder::new(dst, Compression::Default))
    }

    /// Writes the binary representation of this `Blob`, compressed using
    /// the Zlib format, to an `io::Write` dst.
    pub fn to_zlib_writer<W>(&self, dst: &mut W) -> Result<()>
        where W: io::Write
    {
        self.to_writer(&mut ZlibEncoder::new(dst, Compression::Default))
    }

    /// Insert an `Value` with a given name into this `Blob` object. This
    /// method is just a thin wrapper around the underlying `HashMap` method of
    /// the same name.
    ///
    /// This method will also return an error if a `Value::List` with
    /// heterogeneous elements is passed in, because this is illegal in the NBT
    /// file format.
    pub fn insert<S, V>(&mut self, name: S, value: V) -> Result<()>
           where S: Into<String>, V: Into<Value> {
        // The follow prevents `List`s with heterogeneous tags from being
        // inserted into the file.
        let nvalue = value.into();
        if let Value::List(ref vals) = nvalue {
            if vals.len() != 0 {
                let first_id = vals[0].id();
                for nbt in vals {
                    if nbt.id() != first_id {
                        return Err(Error::HeterogeneousList)
                    }
                }
            }
        }
        self.content.insert(name.into(), nvalue);
        Ok(())
    }
}

impl<'a> Index<&'a str> for Blob {
    type Output = Value;

    fn index<'b>(&'b self, s: &'a str) -> &'b Value {
        self.content.get(s).unwrap()
    }
}

impl fmt::Display for Blob {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "TAG_Compound(\"{}\"): {} entry(ies)\n{{\n", self.title, self.content.len())?;
        for (name, tag) in self.content.iter() {
            write!(f, "  {}(\"{}\"): ", tag.tag_name(), name)?;
            tag.print(f, 2)?;
            write!(f, "\n")?;
        }
        write!(f, "}}")
    }
}

#[cfg(feature = "serde")]
use serde::{self, ser::SerializeMap};

#[cfg(feature = "serde")]
impl serde::Serialize for Blob {
    fn serialize<S>(&self, serializer: S) -> serde::export::Result<S::Ok, S::Error>
    where
        S: serde::ser::Serializer,
    {
        // No support for named Blobs.
        let mut state = serializer.serialize_map(Some(self.content.len()))?;
        for (k, v) in &self.content {
            state.serialize_entry(&k, &v)?;
        }
        state.end()
    }
}

#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for Blob {
    fn deserialize<D>(deserializer: D) -> serde::export::Result<Self, D::Error>
    where
        D: serde::de::Deserializer<'de>,
    {
        // No support for named Blobs.
        let map: HashMap<String, Value> = serde::de::Deserialize::deserialize(deserializer)?;
        Ok(Blob { title: "".to_string(), content: map })
    }
}
<commit_msg>Add get method for Blob<commit_after>use std::collections::HashMap;
use std::fmt;
use std::io;
use std::ops::Index;

use byteorder::WriteBytesExt;
use flate2::Compression;
use flate2::read::{GzDecoder, ZlibDecoder};
use flate2::write::{GzEncoder, ZlibEncoder};

use error::{Error, Result};
use raw;
use value::Value;

/// A generic, complete object in Named Binary Tag format.
///
/// This is essentially a map of names to `Value`s, with an optional top-level
/// name of its own. It can be created in a similar way to a `HashMap`, or read
/// from an `io::Read` source, and its binary representation can be written to
/// an `io::Write` destination.
///
/// These read and write methods support both uncompressed and compressed
/// (through Gzip or zlib compression) methods.
///
/// ```rust
/// use nbt::{Blob, Value};
///
/// // Create a `Blob` from key/value pairs.
/// let mut nbt = Blob::new();
/// nbt.insert("name", "Herobrine").unwrap();
/// nbt.insert("health", 100i8).unwrap();
/// nbt.insert("food", 20.0f32).unwrap();
///
/// // Write a compressed binary representation to a byte array.
/// let mut dst = Vec::new();
/// nbt.to_zlib_writer(&mut dst).unwrap();
/// ```
#[derive(Clone, Debug, PartialEq)]
pub struct Blob {
    title: String,
    content: HashMap<String, Value>
}

impl Blob {
    /// Create a new NBT file format representation with an empty name.
    pub fn new() -> Blob {
        Blob { title: "".to_string(), content: HashMap::new() }
    }

    /// Create a new NBT file format representation with the given name.
    pub fn named<S>(name: S) -> Blob where S: Into<String> {
        Blob { title: name.into(), content: HashMap::new() }
    }

    /// Extracts an `Blob` object from an `io::Read` source.
    pub fn from_reader<R>(src: &mut R) -> Result<Blob>
        where R: io::Read
    {
        let (tag, title) = try!(raw::emit_next_header(src));
        // Although it would be possible to read NBT format files composed of
        // arbitrary objects using the current API, by convention all files
        // have a top-level Compound.
        if tag != 0x0a {
            return Err(Error::NoRootCompound);
        }
        let content = try!(Value::from_reader(tag, src));
        match content {
            Value::Compound(map) => Ok(Blob { title: title, content: map }),
            _ => Err(Error::NoRootCompound),
        }
    }

    /// Extracts an `Blob` object from an `io::Read` source that is
    /// compressed using the Gzip format.
    pub fn from_gzip_reader<R>(src: &mut R) -> Result<Blob>
        where R: io::Read
    {
        // Reads the gzip header, and fails if it is incorrect.
        let mut data = try!(GzDecoder::new(src));
        Blob::from_reader(&mut data)
    }

    /// Extracts an `Blob` object from an `io::Read` source that is
    /// compressed using the zlib format.
    pub fn from_zlib_reader<R>(src: &mut R) -> Result<Blob>
        where R: io::Read
    {
        Blob::from_reader(&mut ZlibDecoder::new(src))
    }

    /// Writes the binary representation of this `Blob` to an `io::Write`
    /// destination.
    pub fn to_writer<W>(&self, mut dst: &mut W) -> Result<()>
        where W: io::Write
    {
        dst.write_u8(0x0a)?;
        raw::write_bare_string(&mut dst, &self.title)?;
        for (name, ref nbt) in self.content.iter() {
            dst.write_u8(nbt.id())?;
            raw::write_bare_string(&mut dst, name)?;
            nbt.to_writer(&mut dst)?;
        }
        raw::close_nbt(&mut dst)
    }

    /// Writes the binary representation of this `Blob`, compressed using
    /// the Gzip format, to an `io::Write` destination.
    pub fn to_gzip_writer<W>(&self, dst: &mut W) -> Result<()>
        where W: io::Write
    {
        self.to_writer(&mut GzEncoder::new(dst, Compression::Default))
    }

    /// Writes the binary representation of this `Blob`, compressed using
    /// the Zlib format, to an `io::Write` dst.
    pub fn to_zlib_writer<W>(&self, dst: &mut W) -> Result<()>
        where W: io::Write
    {
        self.to_writer(&mut ZlibEncoder::new(dst, Compression::Default))
    }

    /// Insert an `Value` with a given name into this `Blob` object. This
    /// method is just a thin wrapper around the underlying `HashMap` method of
    /// the same name.
    ///
    /// This method will also return an error if a `Value::List` with
    /// heterogeneous elements is passed in, because this is illegal in the NBT
    /// file format.
    pub fn insert<S, V>(&mut self, name: S, value: V) -> Result<()>
           where S: Into<String>, V: Into<Value> {
        // The follow prevents `List`s with heterogeneous tags from being
        // inserted into the file.
        let nvalue = value.into();
        if let Value::List(ref vals) = nvalue {
            if vals.len() != 0 {
                let first_id = vals[0].id();
                for nbt in vals {
                    if nbt.id() != first_id {
                        return Err(Error::HeterogeneousList)
                    }
                }
            }
        }
        self.content.insert(name.into(), nvalue);
        Ok(())
    }

    /// Tries to get a named `Value` in the blob.
    pub fn get<S>(&self, name: S) -> Option<&Value>
        where S: Into<&'static str>
    {
        self.content.get(name.into())
    }
}

impl<'a> Index<&'a str> for Blob {
    type Output = Value;

    fn index<'b>(&'b self, s: &'a str) -> &'b Value {
        self.content.get(s).unwrap()
    }
}

impl fmt::Display for Blob {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "TAG_Compound(\"{}\"): {} entry(ies)\n{{\n", self.title, self.content.len())?;
        for (name, tag) in self.content.iter() {
            write!(f, "  {}(\"{}\"): ", tag.tag_name(), name)?;
            tag.print(f, 2)?;
            write!(f, "\n")?;
        }
        write!(f, "}}")
    }
}

#[cfg(feature = "serde")]
use serde::{self, ser::SerializeMap};

#[cfg(feature = "serde")]
impl serde::Serialize for Blob {
    fn serialize<S>(&self, serializer: S) -> serde::export::Result<S::Ok, S::Error>
    where
        S: serde::ser::Serializer,
    {
        // No support for named Blobs.
        let mut state = serializer.serialize_map(Some(self.content.len()))?;
        for (k, v) in &self.content {
            state.serialize_entry(&k, &v)?;
        }
        state.end()
    }
}

#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for Blob {
    fn deserialize<D>(deserializer: D) -> serde::export::Result<Self, D::Error>
    where
        D: serde::de::Deserializer<'de>,
    {
        // No support for named Blobs.
        let map: HashMap<String, Value> = serde::de::Deserialize::deserialize(deserializer)?;
        Ok(Blob { title: "".to_string(), content: map })
    }
}
<|endoftext|>


<commit_before>use libc;
use wlroots_sys::{wlr_backend, wlr_headless_backend_create, wlr_headless_add_output,
                  wlr_headless_add_input_device, wlr_input_device_is_headless,
                  wlr_output_is_headless, wlr_input_device_type, wl_display};

use super::UnsafeRenderSetupFunction;
use {InputDevice, InputHandle, Output, OutputHandle};

/// In this backend the only resource the compositor uses is the Wayland file descriptor.
/// It doesn't try to grab actual keyboard/pointers and it doesn't render anything.
///
/// This backend is useful for testing as you can easily add "fake" inputs and outputs.
#[derive(Debug, Hash, Eq, PartialEq)]
pub struct HeadlessBackend {
    pub(crate) backend: *mut wlr_backend
}

impl HeadlessBackend {
    /// Creates a headless backend.
    ///
    /// A headless backend has no outputs or inputs by default.
    pub unsafe fn new(display: *mut wl_display,
                      render_setup_func: Option<UnsafeRenderSetupFunction>)
                      -> Self {
        let backend = wlr_headless_backend_create(display, render_setup_func);
        if backend.is_null() {
            panic!("Could not construct Headless backend");
        }
        HeadlessBackend { backend }
    }

    // TODO Specify the real function to use to get the pixels.

    /// Create a new headless output backed by an in-memory EGL framebuffer.
    ///
    /// You can read pixels from this framebuffer via `wlr_renderer_read_pixels`
    /// but it is otherwise not displayed.
    pub fn add_output(&self, width: libc::c_uint, height: libc::c_uint) -> Option<OutputHandle> {
        unsafe {
            let output_ptr = wlr_headless_add_output(self.backend, width, height);
            if output_ptr.is_null() {
                None
            } else {
                Some(OutputHandle::from_ptr(output_ptr))
            }
        }
    }

    /// Creates a new input device.
    ///
    /// The caller is responsible for manually raising any event signals on the
    /// new input device if it wants to simulate input events.
    pub fn add_input_device(&self, input_type: wlr_input_device_type) -> Option<InputHandle> {
        unsafe {
            let device = wlr_headless_add_input_device(self.backend, input_type);
            if device.is_null() {
                None
            } else {
                Some(InputDevice { device }.device())
            }
        }
    }

    pub fn is_headless_input_device(&self, input_device: &InputDevice) -> bool {
        unsafe {
            wlr_input_device_is_headless(input_device.as_ptr())
        }
    }

    pub fn is_headless_output(&self, output: &Output) -> bool {
        unsafe {
            wlr_output_is_headless(output.as_ptr())
        }
    }

    pub unsafe fn as_ptr(&self) -> *mut wlr_backend {
        self.backend
    }
}
<commit_msg>Specify real (not yet defined) function<commit_after>use libc;
use wlroots_sys::{wlr_backend, wlr_headless_backend_create, wlr_headless_add_output,
                  wlr_headless_add_input_device, wlr_input_device_is_headless,
                  wlr_output_is_headless, wlr_input_device_type, wl_display};

use super::UnsafeRenderSetupFunction;
use {InputDevice, InputHandle, Output, OutputHandle};

/// In this backend the only resource the compositor uses is the Wayland file descriptor.
/// It doesn't try to grab actual keyboard/pointers and it doesn't render anything.
///
/// This backend is useful for testing as you can easily add "fake" inputs and outputs.
#[derive(Debug, Hash, Eq, PartialEq)]
pub struct HeadlessBackend {
    pub(crate) backend: *mut wlr_backend
}

impl HeadlessBackend {
    /// Creates a headless backend.
    ///
    /// A headless backend has no outputs or inputs by default.
    pub unsafe fn new(display: *mut wl_display,
                      render_setup_func: Option<UnsafeRenderSetupFunction>)
                      -> Self {
        let backend = wlr_headless_backend_create(display, render_setup_func);
        if backend.is_null() {
            panic!("Could not construct Headless backend");
        }
        HeadlessBackend { backend }
    }

    /// Create a new headless output backed by an in-memory EGL framebuffer.
    ///
    /// You can read pixels from this framebuffer via `Renderer::read_pixels`
    /// but it is otherwise not displayed.
    pub fn add_output(&self, width: libc::c_uint, height: libc::c_uint) -> Option<OutputHandle> {
        unsafe {
            let output_ptr = wlr_headless_add_output(self.backend, width, height);
            if output_ptr.is_null() {
                None
            } else {
                Some(OutputHandle::from_ptr(output_ptr))
            }
        }
    }

    /// Creates a new input device.
    ///
    /// The caller is responsible for manually raising any event signals on the
    /// new input device if it wants to simulate input events.
    pub fn add_input_device(&self, input_type: wlr_input_device_type) -> Option<InputHandle> {
        unsafe {
            let device = wlr_headless_add_input_device(self.backend, input_type);
            if device.is_null() {
                None
            } else {
                Some(InputDevice { device }.device())
            }
        }
    }

    pub fn is_headless_input_device(&self, input_device: &InputDevice) -> bool {
        unsafe {
            wlr_input_device_is_headless(input_device.as_ptr())
        }
    }

    pub fn is_headless_output(&self, output: &Output) -> bool {
        unsafe {
            wlr_output_is_headless(output.as_ptr())
        }
    }

    pub unsafe fn as_ptr(&self) -> *mut wlr_backend {
        self.backend
    }
}
<|endoftext|>


<commit_before>use hyper::header::{CONTENT_LENGTH, CONTENT_TYPE};
use hyper::{Request, Body, client::Client};

use crate::errors::RequestError;

#[derive(Clone, Debug)]
pub struct Action(String);

impl Action {
    pub fn new(action: &str) -> Action {
        Action(action.into())
    }
}

const HEADER_NAME: &str = "SOAPAction";

pub async fn send_async(
    url: &str,
    action: Action,
    body: &str,
) -> Result<String, RequestError> {

    let client = Client::new();

    let req = Request::builder()
        .uri(url)
        .method("POST")
        .header(HEADER_NAME, action.0)
        .header(CONTENT_TYPE, "xml")
        .header(CONTENT_LENGTH, body.len() as u64)
        .body(Body::from(body.to_string()))?;

    let resp = client.request(req).await?;
    let body = hyper::body::to_bytes(resp.into_body()).await?;
    let string = String::from_utf8(body.to_vec())?;
    Ok(string)
}
<commit_msg>Fix SOAP request mime type<commit_after>use hyper::header::{CONTENT_LENGTH, CONTENT_TYPE};
use hyper::{Request, Body, client::Client};

use crate::errors::RequestError;

#[derive(Clone, Debug)]
pub struct Action(String);

impl Action {
    pub fn new(action: &str) -> Action {
        Action(action.into())
    }
}

const HEADER_NAME: &str = "SOAPAction";

pub async fn send_async(
    url: &str,
    action: Action,
    body: &str,
) -> Result<String, RequestError> {

    let client = Client::new();

    let req = Request::builder()
        .uri(url)
        .method("POST")
        .header(HEADER_NAME, action.0)
        .header(CONTENT_TYPE, "text/xml")
        .header(CONTENT_LENGTH, body.len() as u64)
        .body(Body::from(body.to_string()))?;

    let resp = client.request(req).await?;
    let body = hyper::body::to_bytes(resp.into_body()).await?;
    let string = String::from_utf8(body.to_vec())?;
    Ok(string)
}
<|endoftext|>


<commit_before>use pest::prelude::*;

use std::vec::Vec;
use std::string::String;
use std::collections::LinkedList;

impl_rdp! {
    grammar! {
        program = _{ instruction ~ (["\n"] ~ instruction)* }
        instruction = _{ opcode ~ operand ~ operand ~ operand }
        opcode = _{ number }
        operand = _{ number }

        number = @{ (['0'..'9'] | ['a'..'f'] | ['A'..'F'])+ }

        whitespace = _{ [" "] }
    }

    process! {
        compile(&self) -> Vec<u32> {
            (list: _numbers()) => {
                let mut instructions = Vec::new();
                instructions.extend(list.iter());
                instructions
            }
        }

        _numbers(&self) -> LinkedList<u32> {
            (&head: number, mut tail: _numbers()) => {
                tail.push_front(u32::from_str_radix(head, 16).unwrap());
                tail
            },
            () => {
                LinkedList::new()
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use pest::*;

    #[test]
    fn it_parses_programs_as_machine_code() {
        let mut parser = Rdp::new(StringInput::new("0 a b c\n1 2 3 4"));

        assert!(parser.program());
        assert!(parser.end());

        let tokens = vec![
            // Token::new(Rule::program, 0, 15),
            // Token::new(Rule::instruction, 0, 7),
            Token::new(Rule::number, 0, 1),
            Token::new(Rule::number, 2, 3),
            Token::new(Rule::number, 4, 5),
            Token::new(Rule::number, 6, 7),
            // Token::new(Rule::instruction, 8, 15),
            Token::new(Rule::number, 8, 9),
            Token::new(Rule::number, 10, 11),
            Token::new(Rule::number, 12, 13),
            Token::new(Rule::number, 14, 15),
        ];

        assert_eq!(parser.queue(), &tokens);
        assert_eq!(parser.compile(),
                   vec![0x0, 0xa, 0xb, 0xc, 0x1, 0x2, 0x3, 0x4]);
    }
}
<commit_msg>Teach Chifir to compile assembly with "brk" opcodes.<commit_after>use pest::prelude::*;

use std::vec::Vec;
use std::string::String;
use std::collections::LinkedList;

impl_rdp! {
    grammar! {
        program = _{ instruction ~ (["\n"] ~ instruction)* }
        instruction = _{ opcode ~ operand ~ operand ~ operand }
        opcode = _{ brk | number }
        operand = _{ number }

        brk = { [i"brk"] }
        number = @{ (['0'..'9'] | ['a'..'f'] | ['A'..'F'])+ }

        whitespace = _{ [" "] }
    }

    process! {
        compile(&self) -> Vec<u32> {
            (list: _numbers()) => {
                let mut instructions = Vec::new();
                instructions.extend(list.iter());
                instructions
            }
        }

        _numbers(&self) -> LinkedList<u32> {
            (_: brk, mut tail: _numbers()) => {
                tail.push_front(0x0);
                tail
            },
            (&head: number, mut tail: _numbers()) => {
                tail.push_front(u32::from_str_radix(head, 16).unwrap());
                tail
            },
            () => {
                LinkedList::new()
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use pest::*;

    #[test]
    fn it_parses_programs_as_machine_code() {
        let mut parser = Rdp::new(StringInput::new("0 a b c\n1 2 3 4"));

        assert!(parser.program());
        assert!(parser.end());

        let tokens = vec![
            Token::new(Rule::number, 0, 1),
            Token::new(Rule::number, 2, 3),
            Token::new(Rule::number, 4, 5),
            Token::new(Rule::number, 6, 7),
            Token::new(Rule::number, 8, 9),
            Token::new(Rule::number, 10, 11),
            Token::new(Rule::number, 12, 13),
            Token::new(Rule::number, 14, 15),
        ];

        assert_eq!(parser.queue(), &tokens);
        assert_eq!(parser.compile(),
                   vec![0x0, 0xa, 0xb, 0xc, 0x1, 0x2, 0x3, 0x4]);
    }

    #[test]
    fn it_parses_opcode_brk() {
        let mut parser = Rdp::new(StringInput::new("brk a b c"));

        assert!(parser.program());
        assert!(parser.end());

        let tokens = vec![
            Token::new(Rule::brk, 0, 3),
            Token::new(Rule::number, 4, 5),
            Token::new(Rule::number, 6, 7),
            Token::new(Rule::number, 8, 9),
        ];

        assert_eq!(parser.queue(), &tokens);
        assert_eq!(parser.compile(), vec![0x0, 0xa, 0xb, 0xc]);
    }
}
<|endoftext|>