<commit_before>//! `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.
//!
//! ## Assists Guidelines
//!
//! Assists are the main mechanism to deliver advanced IDE features to the user,
//! so we should pay extra attention to the UX.
//!
//! The power of assists comes from their context-awareness. The main problem
//! with IDE features is that there are a lot of them, and it's hard to teach
//! the user what's available. Assists solve this problem nicely: 💡 signifies
//! that *something* is possible, and clicking on it reveals a *short* list of
//! actions. Contrast it with Emacs `M-x`, which just spits an infinite list of
//! all the features.
//!
//! Here are some considerations when creating a new assist:
//!
//! * It's good to preserve semantics, and it's good to keep the code compiling,
//!   but it isn't necessary. Example: "flip binary operation" might change
//!   semantics.
//! * Assist shouldn't necessary make the code "better". A lot of assist come in
//!   pairs: "if let <-> match".
//! * Assists should have as narrow scope as possible. Each new assists greatly
//!   improves UX for cases where the user actually invokes it, but it makes UX
//!   worse for every case where the user clicks 💡 to invoke some *other*
//!   assist. So, a rarely useful assist which is always applicable can be a net
//!   negative.
//! * Rarely useful actions are tricky. Sometimes there are features which are
//!   clearly useful to some users, but are just noise most of the time. We
//!   don't have a good solution here, our current approach is to make this
//!   functionality available only if assist is applicable to the whole
//!   selection. Example: `sort_items` sorts items alphabetically. Naively, it
//!   should be available more or less everywhere, which isn't useful. So
//!   instead we only show it if the user *selects* the items they want to sort.
//! * Consider grouping related assists together (see [`Assists::add_group`]).
//! * Make assists robust. If the assist depends on results of type-inference to
//!   much, it might only fire in fully-correct code. This makes assist less
//!   useful and (worse) less predictable. The user should have a clear
//!   intuition when each particular assist is available.
//! * Make small assists, which compose. Example: rather than auto-importing
//!   enums in `add_missing_match_arms`, we use fully-qualified names. There's a
//!   separate assist to shorten a fully-qualified name.
//! * Distinguish between assists and fixits for diagnostics. Internally, fixits
//!   and assists are equivalent. They have the same "show a list + invoke a
//!   single element" workflow, and both use [`Assist`] data structure. The main
//!   difference is in the UX: while 💡 looks only at the cursor position,
//!   diagnostics squigglies and fixits are calculated for the whole file and
//!   are presented to the user eagerly. So, diagnostics should be fixable
//!   errors, while assists can be just suggestions for an alternative way to do
//!   something. If something *could* be a diagnostic, it should be a
//!   diagnostic. Conversely, it might be valuable to turn a diagnostic with a
//!   lot of false errors into an assist.
//! *
//!
//! See also this post:
//! <https://rust-analyzer.github.io/blog/2020/09/28/how-to-make-a-light-bulb.html>
#[allow(unused)]
macro_rules! eprintln {
    ($($tt:tt)*) => { stdx::eprintln!($($tt)*) };
}

mod assist_config;
mod assist_context;
#[cfg(test)]
mod tests;
pub mod utils;

use hir::Semantics;
use ide_db::{base_db::FileRange, RootDatabase};
use syntax::TextRange;

pub(crate) use crate::assist_context::{AssistContext, Assists};

pub use assist_config::AssistConfig;
pub use ide_db::assists::{
    Assist, AssistId, AssistKind, AssistResolveStrategy, GroupLabel, SingleResolve,
};

/// Return all the assists applicable at the given position.
///
// NOTE: We don't have a `Feature: ` section for assists, they are special-cased
// in the manual.
pub fn assists(
    db: &RootDatabase,
    config: &AssistConfig,
    resolve: AssistResolveStrategy,
    range: FileRange,
) -> Vec<Assist> {
    let sema = Semantics::new(db);
    let ctx = AssistContext::new(sema, config, range);
    let mut acc = Assists::new(&ctx, resolve);
    handlers::all().iter().for_each(|handler| {
        handler(&mut acc, &ctx);
    });
    acc.finish()
}

mod handlers {
    use crate::{AssistContext, Assists};

    pub(crate) type Handler = fn(&mut Assists, &AssistContext) -> Option<()>;

    mod add_explicit_type;
    mod add_lifetime_to_type;
    mod add_missing_impl_members;
    mod add_turbo_fish;
    mod apply_demorgan;
    mod auto_import;
    mod change_visibility;
    mod convert_bool_then;
    mod convert_comment_block;
    mod convert_integer_literal;
    mod convert_into_to_from;
    mod convert_iter_for_each_to_for;
    mod convert_tuple_struct_to_named_struct;
    mod convert_to_guarded_return;
    mod convert_while_to_loop;
    mod destructure_tuple_binding;
    mod expand_glob_import;
    mod extract_function;
    mod extract_module;
    mod extract_struct_from_enum_variant;
    mod extract_type_alias;
    mod extract_variable;
    mod add_missing_match_arms;
    mod fix_visibility;
    mod flip_binexpr;
    mod flip_comma;
    mod flip_trait_bound;
    mod generate_constant;
    mod generate_default_from_enum_variant;
    mod generate_default_from_new;
    mod generate_deref;
    mod generate_derive;
    mod generate_documentation_template;
    mod generate_enum_is_method;
    mod generate_enum_projection_method;
    mod generate_from_impl_for_enum;
    mod generate_function;
    mod generate_getter;
    mod generate_impl;
    mod generate_is_empty_from_len;
    mod generate_new;
    mod generate_setter;
    mod generate_delegate_methods;
    mod add_return_type;
    mod inline_call;
    mod inline_local_variable;
    mod inline_type_alias;
    mod introduce_named_lifetime;
    mod invert_if;
    mod merge_imports;
    mod merge_match_arms;
    mod move_bounds;
    mod move_guard;
    mod move_module_to_file;
    mod move_to_mod_rs;
    mod move_from_mod_rs;
    mod number_representation;
    mod promote_local_to_const;
    mod pull_assignment_up;
    mod qualify_path;
    mod qualify_method_call;
    mod raw_string;
    mod remove_dbg;
    mod remove_mut;
    mod remove_unused_param;
    mod reorder_fields;
    mod reorder_impl;
    mod replace_try_expr_with_match;
    mod replace_derive_with_manual_impl;
    mod replace_if_let_with_match;
    mod introduce_named_generic;
    mod replace_let_with_if_let;
    mod replace_qualified_name_with_use;
    mod replace_string_with_char;
    mod replace_turbofish_with_explicit_type;
    mod split_import;
    mod sort_items;
    mod toggle_ignore;
    mod unmerge_use;
    mod unnecessary_async;
    mod unwrap_block;
    mod unwrap_result_return_type;
    mod wrap_return_type_in_result;

    pub(crate) fn all() -> &'static [Handler] {
        &[
            // These are alphabetic for the foolish consistency
            add_explicit_type::add_explicit_type,
            add_missing_match_arms::add_missing_match_arms,
            add_lifetime_to_type::add_lifetime_to_type,
            add_return_type::add_return_type,
            add_turbo_fish::add_turbo_fish,
            apply_demorgan::apply_demorgan,
            auto_import::auto_import,
            change_visibility::change_visibility,
            convert_bool_then::convert_bool_then_to_if,
            convert_bool_then::convert_if_to_bool_then,
            convert_comment_block::convert_comment_block,
            convert_integer_literal::convert_integer_literal,
            convert_into_to_from::convert_into_to_from,
            convert_iter_for_each_to_for::convert_iter_for_each_to_for,
            convert_iter_for_each_to_for::convert_for_loop_with_for_each,
            convert_to_guarded_return::convert_to_guarded_return,
            convert_tuple_struct_to_named_struct::convert_tuple_struct_to_named_struct,
            convert_while_to_loop::convert_while_to_loop,
            destructure_tuple_binding::destructure_tuple_binding,
            expand_glob_import::expand_glob_import,
            extract_struct_from_enum_variant::extract_struct_from_enum_variant,
            extract_type_alias::extract_type_alias,
            fix_visibility::fix_visibility,
            flip_binexpr::flip_binexpr,
            flip_comma::flip_comma,
            flip_trait_bound::flip_trait_bound,
            generate_constant::generate_constant,
            generate_default_from_enum_variant::generate_default_from_enum_variant,
            generate_default_from_new::generate_default_from_new,
            generate_derive::generate_derive,
            generate_documentation_template::generate_documentation_template,
            generate_enum_is_method::generate_enum_is_method,
            generate_enum_projection_method::generate_enum_as_method,
            generate_enum_projection_method::generate_enum_try_into_method,
            generate_from_impl_for_enum::generate_from_impl_for_enum,
            generate_function::generate_function,
            generate_impl::generate_impl,
            generate_is_empty_from_len::generate_is_empty_from_len,
            generate_new::generate_new,
            inline_call::inline_call,
            inline_call::inline_into_callers,
            inline_local_variable::inline_local_variable,
            inline_type_alias::inline_type_alias,
            introduce_named_generic::introduce_named_generic,
            introduce_named_lifetime::introduce_named_lifetime,
            invert_if::invert_if,
            merge_imports::merge_imports,
            merge_match_arms::merge_match_arms,
            move_bounds::move_bounds_to_where_clause,
            move_guard::move_arm_cond_to_match_guard,
            move_guard::move_guard_to_arm_body,
            move_module_to_file::move_module_to_file,
            move_to_mod_rs::move_to_mod_rs,
            move_from_mod_rs::move_from_mod_rs,
            number_representation::reformat_number_literal,
            pull_assignment_up::pull_assignment_up,
            promote_local_to_const::promote_local_to_const,
            qualify_path::qualify_path,
            qualify_method_call::qualify_method_call,
            raw_string::add_hash,
            raw_string::make_usual_string,
            raw_string::remove_hash,
            remove_dbg::remove_dbg,
            remove_mut::remove_mut,
            remove_unused_param::remove_unused_param,
            reorder_fields::reorder_fields,
            reorder_impl::reorder_impl,
            replace_try_expr_with_match::replace_try_expr_with_match,
            replace_derive_with_manual_impl::replace_derive_with_manual_impl,
            replace_if_let_with_match::replace_if_let_with_match,
            replace_if_let_with_match::replace_match_with_if_let,
            replace_let_with_if_let::replace_let_with_if_let,
            replace_turbofish_with_explicit_type::replace_turbofish_with_explicit_type,
            replace_qualified_name_with_use::replace_qualified_name_with_use,
            sort_items::sort_items,
            split_import::split_import,
            toggle_ignore::toggle_ignore,
            unmerge_use::unmerge_use,
            unnecessary_async::unnecessary_async,
            unwrap_block::unwrap_block,
            unwrap_result_return_type::unwrap_result_return_type,
            wrap_return_type_in_result::wrap_return_type_in_result,
            // These are manually sorted for better priorities. By default,
            // priority is determined by the size of the target range (smaller
            // target wins). If the ranges are equal, position in this list is
            // used as a tie-breaker.
            add_missing_impl_members::add_missing_impl_members,
            add_missing_impl_members::add_missing_default_members,
            //
            replace_string_with_char::replace_string_with_char,
            replace_string_with_char::replace_char_with_string,
            raw_string::make_raw_string,
            //
            extract_variable::extract_variable,
            extract_function::extract_function,
            extract_module::extract_module,
            //
            generate_getter::generate_getter,
            generate_getter::generate_getter_mut,
            generate_setter::generate_setter,
            generate_delegate_methods::generate_delegate_methods,
            generate_deref::generate_deref,
            // Are you sure you want to add new assist here, and not to the
            // sorted list above?
        ]
    }
}
<commit_msg>Fix some typos in `ide-assists/src/lib.rs`<commit_after>//! `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.
//!
//! ## Assists Guidelines
//!
//! Assists are the main mechanism to deliver advanced IDE features to the user,
//! so we should pay extra attention to the UX.
//!
//! The power of assists comes from their context-awareness. The main problem
//! with IDE features is that there are a lot of them, and it's hard to teach
//! the user what's available. Assists solve this problem nicely: 💡 signifies
//! that *something* is possible, and clicking on it reveals a *short* list of
//! actions. Contrast it with Emacs `M-x`, which just spits an infinite list of
//! all the features.
//!
//! Here are some considerations when creating a new assist:
//!
//! * It's good to preserve semantics, and it's good to keep the code compiling,
//!   but it isn't necessary. Example: "flip binary operation" might change
//!   semantics.
//! * Assist shouldn't necessary make the code "better". A lot of assist come in
//!   pairs: "if let <-> match".
//! * Assists should have as narrow scope as possible. Each new assists greatly
//!   improves UX for cases where the user actually invokes it, but it makes UX
//!   worse for every case where the user clicks 💡 to invoke some *other*
//!   assist. So, a rarely useful assist which is always applicable can be a net
//!   negative.
//! * Rarely useful actions are tricky. Sometimes there are features which are
//!   clearly useful to some users, but are just noise most of the time. We
//!   don't have a good solution here, our current approach is to make this
//!   functionality available only if assist is applicable to the whole
//!   selection. Example: `sort_items` sorts items alphabetically. Naively, it
//!   should be available more or less everywhere, which isn't useful. So
//!   instead we only show it if the user *selects* the items they want to sort.
//! * Consider grouping related assists together (see [`Assists::add_group`]).
//! * Make assists robust. If the assist depends on results of type-inference too
//!   much, it might only fire in fully-correct code. This makes assist less
//!   useful and (worse) less predictable. The user should have a clear
//!   intuition when each particular assist is available.
//! * Make small assists, which compose. Example: rather than auto-importing
//!   enums in `add_missing_match_arms`, we use fully-qualified names. There's a
//!   separate assist to shorten a fully-qualified name.
//! * Distinguish between assists and fixits for diagnostics. Internally, fixits
//!   and assists are equivalent. They have the same "show a list + invoke a
//!   single element" workflow, and both use [`Assist`] data structure. The main
//!   difference is in the UX: while 💡 looks only at the cursor position,
//!   diagnostics squigglies and fixits are calculated for the whole file and
//!   are presented to the user eagerly. So, diagnostics should be fixable
//!   errors, while assists can be just suggestions for an alternative way to do
//!   something. If something *could* be a diagnostic, it should be a
//!   diagnostic. Conversely, it might be valuable to turn a diagnostic with a
//!   lot of false errors into an assist.
//!
//! See also this post:
//! <https://rust-analyzer.github.io/blog/2020/09/28/how-to-make-a-light-bulb.html>
#[allow(unused)]
macro_rules! eprintln {
    ($($tt:tt)*) => { stdx::eprintln!($($tt)*) };
}

mod assist_config;
mod assist_context;
#[cfg(test)]
mod tests;
pub mod utils;

use hir::Semantics;
use ide_db::{base_db::FileRange, RootDatabase};
use syntax::TextRange;

pub(crate) use crate::assist_context::{AssistContext, Assists};

pub use assist_config::AssistConfig;
pub use ide_db::assists::{
    Assist, AssistId, AssistKind, AssistResolveStrategy, GroupLabel, SingleResolve,
};

/// Return all the assists applicable at the given position.
///
// NOTE: We don't have a `Feature: ` section for assists, they are special-cased
// in the manual.
pub fn assists(
    db: &RootDatabase,
    config: &AssistConfig,
    resolve: AssistResolveStrategy,
    range: FileRange,
) -> Vec<Assist> {
    let sema = Semantics::new(db);
    let ctx = AssistContext::new(sema, config, range);
    let mut acc = Assists::new(&ctx, resolve);
    handlers::all().iter().for_each(|handler| {
        handler(&mut acc, &ctx);
    });
    acc.finish()
}

mod handlers {
    use crate::{AssistContext, Assists};

    pub(crate) type Handler = fn(&mut Assists, &AssistContext) -> Option<()>;

    mod add_explicit_type;
    mod add_lifetime_to_type;
    mod add_missing_impl_members;
    mod add_turbo_fish;
    mod apply_demorgan;
    mod auto_import;
    mod change_visibility;
    mod convert_bool_then;
    mod convert_comment_block;
    mod convert_integer_literal;
    mod convert_into_to_from;
    mod convert_iter_for_each_to_for;
    mod convert_tuple_struct_to_named_struct;
    mod convert_to_guarded_return;
    mod convert_while_to_loop;
    mod destructure_tuple_binding;
    mod expand_glob_import;
    mod extract_function;
    mod extract_module;
    mod extract_struct_from_enum_variant;
    mod extract_type_alias;
    mod extract_variable;
    mod add_missing_match_arms;
    mod fix_visibility;
    mod flip_binexpr;
    mod flip_comma;
    mod flip_trait_bound;
    mod generate_constant;
    mod generate_default_from_enum_variant;
    mod generate_default_from_new;
    mod generate_deref;
    mod generate_derive;
    mod generate_documentation_template;
    mod generate_enum_is_method;
    mod generate_enum_projection_method;
    mod generate_from_impl_for_enum;
    mod generate_function;
    mod generate_getter;
    mod generate_impl;
    mod generate_is_empty_from_len;
    mod generate_new;
    mod generate_setter;
    mod generate_delegate_methods;
    mod add_return_type;
    mod inline_call;
    mod inline_local_variable;
    mod inline_type_alias;
    mod introduce_named_lifetime;
    mod invert_if;
    mod merge_imports;
    mod merge_match_arms;
    mod move_bounds;
    mod move_guard;
    mod move_module_to_file;
    mod move_to_mod_rs;
    mod move_from_mod_rs;
    mod number_representation;
    mod promote_local_to_const;
    mod pull_assignment_up;
    mod qualify_path;
    mod qualify_method_call;
    mod raw_string;
    mod remove_dbg;
    mod remove_mut;
    mod remove_unused_param;
    mod reorder_fields;
    mod reorder_impl;
    mod replace_try_expr_with_match;
    mod replace_derive_with_manual_impl;
    mod replace_if_let_with_match;
    mod introduce_named_generic;
    mod replace_let_with_if_let;
    mod replace_qualified_name_with_use;
    mod replace_string_with_char;
    mod replace_turbofish_with_explicit_type;
    mod split_import;
    mod sort_items;
    mod toggle_ignore;
    mod unmerge_use;
    mod unnecessary_async;
    mod unwrap_block;
    mod unwrap_result_return_type;
    mod wrap_return_type_in_result;

    pub(crate) fn all() -> &'static [Handler] {
        &[
            // These are alphabetic for the foolish consistency
            add_explicit_type::add_explicit_type,
            add_missing_match_arms::add_missing_match_arms,
            add_lifetime_to_type::add_lifetime_to_type,
            add_return_type::add_return_type,
            add_turbo_fish::add_turbo_fish,
            apply_demorgan::apply_demorgan,
            auto_import::auto_import,
            change_visibility::change_visibility,
            convert_bool_then::convert_bool_then_to_if,
            convert_bool_then::convert_if_to_bool_then,
            convert_comment_block::convert_comment_block,
            convert_integer_literal::convert_integer_literal,
            convert_into_to_from::convert_into_to_from,
            convert_iter_for_each_to_for::convert_iter_for_each_to_for,
            convert_iter_for_each_to_for::convert_for_loop_with_for_each,
            convert_to_guarded_return::convert_to_guarded_return,
            convert_tuple_struct_to_named_struct::convert_tuple_struct_to_named_struct,
            convert_while_to_loop::convert_while_to_loop,
            destructure_tuple_binding::destructure_tuple_binding,
            expand_glob_import::expand_glob_import,
            extract_struct_from_enum_variant::extract_struct_from_enum_variant,
            extract_type_alias::extract_type_alias,
            fix_visibility::fix_visibility,
            flip_binexpr::flip_binexpr,
            flip_comma::flip_comma,
            flip_trait_bound::flip_trait_bound,
            generate_constant::generate_constant,
            generate_default_from_enum_variant::generate_default_from_enum_variant,
            generate_default_from_new::generate_default_from_new,
            generate_derive::generate_derive,
            generate_documentation_template::generate_documentation_template,
            generate_enum_is_method::generate_enum_is_method,
            generate_enum_projection_method::generate_enum_as_method,
            generate_enum_projection_method::generate_enum_try_into_method,
            generate_from_impl_for_enum::generate_from_impl_for_enum,
            generate_function::generate_function,
            generate_impl::generate_impl,
            generate_is_empty_from_len::generate_is_empty_from_len,
            generate_new::generate_new,
            inline_call::inline_call,
            inline_call::inline_into_callers,
            inline_local_variable::inline_local_variable,
            inline_type_alias::inline_type_alias,
            introduce_named_generic::introduce_named_generic,
            introduce_named_lifetime::introduce_named_lifetime,
            invert_if::invert_if,
            merge_imports::merge_imports,
            merge_match_arms::merge_match_arms,
            move_bounds::move_bounds_to_where_clause,
            move_guard::move_arm_cond_to_match_guard,
            move_guard::move_guard_to_arm_body,
            move_module_to_file::move_module_to_file,
            move_to_mod_rs::move_to_mod_rs,
            move_from_mod_rs::move_from_mod_rs,
            number_representation::reformat_number_literal,
            pull_assignment_up::pull_assignment_up,
            promote_local_to_const::promote_local_to_const,
            qualify_path::qualify_path,
            qualify_method_call::qualify_method_call,
            raw_string::add_hash,
            raw_string::make_usual_string,
            raw_string::remove_hash,
            remove_dbg::remove_dbg,
            remove_mut::remove_mut,
            remove_unused_param::remove_unused_param,
            reorder_fields::reorder_fields,
            reorder_impl::reorder_impl,
            replace_try_expr_with_match::replace_try_expr_with_match,
            replace_derive_with_manual_impl::replace_derive_with_manual_impl,
            replace_if_let_with_match::replace_if_let_with_match,
            replace_if_let_with_match::replace_match_with_if_let,
            replace_let_with_if_let::replace_let_with_if_let,
            replace_turbofish_with_explicit_type::replace_turbofish_with_explicit_type,
            replace_qualified_name_with_use::replace_qualified_name_with_use,
            sort_items::sort_items,
            split_import::split_import,
            toggle_ignore::toggle_ignore,
            unmerge_use::unmerge_use,
            unnecessary_async::unnecessary_async,
            unwrap_block::unwrap_block,
            unwrap_result_return_type::unwrap_result_return_type,
            wrap_return_type_in_result::wrap_return_type_in_result,
            // These are manually sorted for better priorities. By default,
            // priority is determined by the size of the target range (smaller
            // target wins). If the ranges are equal, position in this list is
            // used as a tie-breaker.
            add_missing_impl_members::add_missing_impl_members,
            add_missing_impl_members::add_missing_default_members,
            //
            replace_string_with_char::replace_string_with_char,
            replace_string_with_char::replace_char_with_string,
            raw_string::make_raw_string,
            //
            extract_variable::extract_variable,
            extract_function::extract_function,
            extract_module::extract_module,
            //
            generate_getter::generate_getter,
            generate_getter::generate_getter_mut,
            generate_setter::generate_setter,
            generate_delegate_methods::generate_delegate_methods,
            generate_deref::generate_deref,
            // Are you sure you want to add new assist here, and not to the
            // sorted list above?
        ]
    }
}
<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use std::error::Error;
use std::path::{Path, PathBuf};
use std::process::Command;

use devicemapper::DM;
use devicemapper::Sectors;
use devicemapper::{ThinDev, ThinDevId, ThinStatus};

use super::super::engine::{Filesystem, HasName, HasUuid};
use super::super::errors::{EngineError, EngineResult, ErrorEnum};
use super::super::types::{FilesystemUuid, PoolUuid};

use super::dmdevice::{ThinRole, format_thin_name};
use super::serde_structs::{FilesystemSave, Recordable};
use super::thinpool::ThinPool;

#[derive(Debug)]
pub struct StratFilesystem {
    fs_id: FilesystemUuid,
    name: String,
    thin_dev: ThinDev,
}

pub enum FilesystemStatus {
    Good,
    Failed,
}

impl StratFilesystem {
    pub fn initialize(pool_uuid: &PoolUuid,
                      fs_id: FilesystemUuid,
                      name: &str,
                      dm: &DM,
                      thin_pool: &mut ThinPool)
                      -> EngineResult<StratFilesystem> {
        let device_name = format_thin_name(pool_uuid, ThinRole::Filesystem(fs_id));
        let thin_dev = try!(thin_pool.make_thin_device(dm, &device_name));
        let fs = StratFilesystem {
            fs_id: fs_id,
            name: name.to_owned(),
            thin_dev: thin_dev,
        };

        try!(create_fs(try!(fs.devnode()).as_path()));
        Ok(fs)
    }

    /// Setup a filesystem, setting up the thin device as necessary.
    // FIXME: Check for still existing device mapper devices.
    pub fn setup(pool_uuid: PoolUuid,
                 fs_id: FilesystemUuid,
                 thindev_id: ThinDevId,
                 name: &str,
                 size: Sectors,
                 dm: &DM,
                 thin_pool: &ThinPool)
                 -> EngineResult<StratFilesystem> {
        let device_name = format_thin_name(&pool_uuid, ThinRole::Filesystem(fs_id));
        let thin_dev = try!(thin_pool.setup_thin_device(dm, &device_name, thindev_id, size));

        Ok(StratFilesystem {
               fs_id: fs_id,
               name: name.to_owned(),
               thin_dev: thin_dev,
           })
    }

    pub fn check(&self, dm: &DM) -> EngineResult<FilesystemStatus> {
        match try!(self.thin_dev.status(dm)) {
            ThinStatus::Good((_mapped, _highest)) => {
                // TODO: check if filesystem is getting full and might need to
                // be extended (hint: use statfs(2))
                // TODO: periodically kick off fstrim?
            }
            ThinStatus::Fail => return Ok(FilesystemStatus::Failed),
        }
        Ok(FilesystemStatus::Good)
    }

    /// The thin id for the thin device that backs this filesystem.
    pub fn thin_id(&self) -> ThinDevId {
        self.thin_dev.id()
    }
}

impl HasName for StratFilesystem {
    fn name(&self) -> &str {
        &self.name
    }
}

impl HasUuid for StratFilesystem {
    fn uuid(&self) -> &FilesystemUuid {
        &self.fs_id
    }
}

impl Filesystem for StratFilesystem {
    fn rename(&mut self, name: &str) {
        self.name = name.to_owned();
    }

    fn destroy(self) -> EngineResult<()> {
        let dm = try!(DM::new());
        match self.thin_dev.teardown(&dm) {
            Ok(_) => Ok(()),
            Err(e) => Err(EngineError::Engine(ErrorEnum::Error, e.description().into())),
        }
    }

    fn devnode(&self) -> EngineResult<PathBuf> {
        Ok(try!(self.thin_dev.devnode()))
    }
}

impl Recordable<FilesystemSave> for StratFilesystem {
    fn record(&self) -> EngineResult<FilesystemSave> {
        Ok(FilesystemSave {
               name: self.name.clone(),
               uuid: self.fs_id,
               thin_id: self.thin_dev.id(),
               size: self.thin_dev.size(),
           })
    }
}

pub fn create_fs(dev_path: &Path) -> EngineResult<()> {

    debug!("Create filesystem for : {:?}", dev_path);
    let output = try!(Command::new("mkfs.xfs")
                          .arg("-f")
                          .arg(&dev_path)
                          .output());

    if output.status.success() {
        debug!("Created xfs filesystem on {:?}", dev_path)
    } else {
        let message = String::from_utf8_lossy(&output.stderr);
        debug!("stderr: {}", message);
        return Err(EngineError::Engine(ErrorEnum::Error, message.into()));
    }
    Ok(())
}
<commit_msg>Simplify create_fs<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use std::error::Error;
use std::path::{Path, PathBuf};
use std::process::Command;

use devicemapper::DM;
use devicemapper::Sectors;
use devicemapper::{ThinDev, ThinDevId, ThinStatus};

use super::super::engine::{Filesystem, HasName, HasUuid};
use super::super::errors::{EngineError, EngineResult, ErrorEnum};
use super::super::types::{FilesystemUuid, PoolUuid};

use super::dmdevice::{ThinRole, format_thin_name};
use super::serde_structs::{FilesystemSave, Recordable};
use super::thinpool::ThinPool;

#[derive(Debug)]
pub struct StratFilesystem {
    fs_id: FilesystemUuid,
    name: String,
    thin_dev: ThinDev,
}

pub enum FilesystemStatus {
    Good,
    Failed,
}

impl StratFilesystem {
    pub fn initialize(pool_uuid: &PoolUuid,
                      fs_id: FilesystemUuid,
                      name: &str,
                      dm: &DM,
                      thin_pool: &mut ThinPool)
                      -> EngineResult<StratFilesystem> {
        let device_name = format_thin_name(pool_uuid, ThinRole::Filesystem(fs_id));
        let thin_dev = try!(thin_pool.make_thin_device(dm, &device_name));
        let fs = StratFilesystem {
            fs_id: fs_id,
            name: name.to_owned(),
            thin_dev: thin_dev,
        };

        try!(create_fs(try!(fs.devnode()).as_path()));
        Ok(fs)
    }

    /// Setup a filesystem, setting up the thin device as necessary.
    // FIXME: Check for still existing device mapper devices.
    pub fn setup(pool_uuid: PoolUuid,
                 fs_id: FilesystemUuid,
                 thindev_id: ThinDevId,
                 name: &str,
                 size: Sectors,
                 dm: &DM,
                 thin_pool: &ThinPool)
                 -> EngineResult<StratFilesystem> {
        let device_name = format_thin_name(&pool_uuid, ThinRole::Filesystem(fs_id));
        let thin_dev = try!(thin_pool.setup_thin_device(dm, &device_name, thindev_id, size));

        Ok(StratFilesystem {
               fs_id: fs_id,
               name: name.to_owned(),
               thin_dev: thin_dev,
           })
    }

    pub fn check(&self, dm: &DM) -> EngineResult<FilesystemStatus> {
        match try!(self.thin_dev.status(dm)) {
            ThinStatus::Good((_mapped, _highest)) => {
                // TODO: check if filesystem is getting full and might need to
                // be extended (hint: use statfs(2))
                // TODO: periodically kick off fstrim?
            }
            ThinStatus::Fail => return Ok(FilesystemStatus::Failed),
        }
        Ok(FilesystemStatus::Good)
    }

    /// The thin id for the thin device that backs this filesystem.
    pub fn thin_id(&self) -> ThinDevId {
        self.thin_dev.id()
    }
}

impl HasName for StratFilesystem {
    fn name(&self) -> &str {
        &self.name
    }
}

impl HasUuid for StratFilesystem {
    fn uuid(&self) -> &FilesystemUuid {
        &self.fs_id
    }
}

impl Filesystem for StratFilesystem {
    fn rename(&mut self, name: &str) {
        self.name = name.to_owned();
    }

    fn destroy(self) -> EngineResult<()> {
        let dm = try!(DM::new());
        match self.thin_dev.teardown(&dm) {
            Ok(_) => Ok(()),
            Err(e) => Err(EngineError::Engine(ErrorEnum::Error, e.description().into())),
        }
    }

    fn devnode(&self) -> EngineResult<PathBuf> {
        Ok(try!(self.thin_dev.devnode()))
    }
}

impl Recordable<FilesystemSave> for StratFilesystem {
    fn record(&self) -> EngineResult<FilesystemSave> {
        Ok(FilesystemSave {
               name: self.name.clone(),
               uuid: self.fs_id,
               thin_id: self.thin_dev.id(),
               size: self.thin_dev.size(),
           })
    }
}

/// Create a filesystem on devnode.
pub fn create_fs(devnode: &Path) -> EngineResult<()> {
    if try!(Command::new("mkfs.xfs")
                .arg("-f")
                .arg(&devnode)
                .status())
               .success() {
        Ok(())
    } else {
        let err_msg = format!("Failed to create new filesystem at {:?}", devnode);
        Err(EngineError::Engine(ErrorEnum::Error, err_msg))
    }
}
<|endoftext|>


<commit_before>use cssparser::{BasicParseError, Parser, ParserInput, Token};
use markup5ever::QualName;

use std::str;

use crate::error::{NodeError, ValueErrorKind};

#[derive(Debug, Clone, PartialEq)]
pub struct ParseError {
    pub display: String,
}

impl ParseError {
    pub fn new<T: AsRef<str>>(msg: T) -> ParseError {
        ParseError {
            display: msg.as_ref().to_string(),
        }
    }
}

impl<'a> From<BasicParseError<'a>> for ParseError {
    fn from(_: BasicParseError<'_>) -> ParseError {
        ParseError::new("parse error")
    }
}

/// Trait to parse values using `cssparser::Parser`.
pub trait Parse: Sized {
    /// Error type for parse errors.
    type Err;

    /// Parses a value out of the `parser`.
    ///
    /// All value types should implement this for composability.
    fn parse(parser: &mut Parser<'_, '_>) -> Result<Self, Self::Err>;

    /// Convenience function to parse a value out of a `&str`.
    ///
    /// This is useful mostly for tests which want to avoid creating a
    /// `cssparser::Parser` by hand.
    fn parse_str(s: &str) -> Result<Self, Self::Err> {
        let mut input = ParserInput::new(s);
        let mut parser = Parser::new(&mut input);

        Self::parse(&mut parser).and_then(|r| {
            // FIXME: parser.expect_exhausted()?;
            Ok(r)
        })
    }
}

/// Extra utility methods for `cssparser::Parser`.
pub trait CssParserExt {
    /// Avoid infinities.
    fn expect_finite_number(&mut self) -> Result<f32, ValueErrorKind>;

    /// Consumes a comma if it exists, or does nothing.
    fn optional_comma(&mut self);
}

impl<'i, 't> CssParserExt for Parser<'i, 't> {
    fn expect_finite_number(&mut self) -> Result<f32, ValueErrorKind> {
        finite_f32(self.expect_number()?)
    }

    fn optional_comma(&mut self) {
        let _ = self.try_parse(|p| p.expect_comma());
    }
}

pub fn finite_f32(n: f32) -> Result<f32, ValueErrorKind> {
    if n.is_finite() {
        Ok(n)
    } else {
        Err(ValueErrorKind::Value("expected finite number".to_string()))
    }
}

pub trait ParseValue<T: Parse<Err = ValueErrorKind>> {
    /// Parses a `value` string into a type `T`.
    fn parse(&self, value: &str) -> Result<T, NodeError>;

    /// Parses a `value` string into a type `T` with an optional validation function.
    fn parse_and_validate<F: FnOnce(T) -> Result<T, ValueErrorKind>>(
        &self,
        value: &str,
        validate: F,
    ) -> Result<T, NodeError>;
}

impl<T: Parse<Err = ValueErrorKind>> ParseValue<T> for QualName {
    fn parse(&self, value: &str) -> Result<T, NodeError> {
        let mut input = ParserInput::new(value);
        let mut parser = Parser::new(&mut input);

        T::parse(&mut parser).map_err(|e| NodeError::attribute_error(self.clone(), e))
    }

    fn parse_and_validate<F: FnOnce(T) -> Result<T, ValueErrorKind>>(
        &self,
        value: &str,
        validate: F,
    ) -> Result<T, NodeError> {
        let mut input = ParserInput::new(value);
        let mut parser = Parser::new(&mut input);

        T::parse(&mut parser)
            .and_then(validate)
            .map_err(|e| NodeError::attribute_error(self.clone(), e))
    }
}

impl Parse for f64 {
    type Err = ValueErrorKind;

    fn parse(parser: &mut Parser<'_, '_>) -> Result<f64, ValueErrorKind> {
        Ok(f64::from(parser.expect_finite_number().map_err(|_| {
            ValueErrorKind::Parse(ParseError::new("expected number"))
        })?))
    }
}

// number
//
// https://www.w3.org/TR/SVG11/types.html#DataTypeNumber
pub fn number(s: &str) -> Result<f64, ValueErrorKind> {
    let mut input = ParserInput::new(s);
    let mut parser = Parser::new(&mut input);

    Ok(f64::from(parser.expect_finite_number()?))
}

// number-optional-number
//
// https://www.w3.org/TR/SVG/types.html#DataTypeNumberOptionalNumber

pub fn number_optional_number(s: &str) -> Result<(f64, f64), ValueErrorKind> {
    let mut input = ParserInput::new(s);
    let mut parser = Parser::new(&mut input);

    let x = f64::from(parser.expect_finite_number()?);

    if !parser.is_exhausted() {
        let state = parser.state();

        match *parser.next()? {
            Token::Comma => {}
            _ => parser.reset(&state),
        };

        let y = f64::from(parser.expect_finite_number()?);

        parser.expect_exhausted()?;

        Ok((x, y))
    } else {
        Ok((x, x))
    }
}

// integer
//
// https://www.w3.org/TR/SVG11/types.html#DataTypeInteger
pub fn integer(s: &str) -> Result<i32, ValueErrorKind> {
    let mut input = ParserInput::new(s);
    let mut parser = Parser::new(&mut input);

    Ok(parser.expect_integer()?)
}

// integer-optional-integer
//
// Like number-optional-number but with integers.
pub fn integer_optional_integer(s: &str) -> Result<(i32, i32), ValueErrorKind> {
    let mut input = ParserInput::new(s);
    let mut parser = Parser::new(&mut input);

    let x = parser.expect_integer()?;

    if !parser.is_exhausted() {
        let state = parser.state();

        match *parser.next()? {
            Token::Comma => {}
            _ => parser.reset(&state),
        };

        let y = parser.expect_integer()?;

        parser.expect_exhausted()?;

        Ok((x, y))
    } else {
        Ok((x, x))
    }
}

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

    #[test]
    fn parses_number_optional_number() {
        assert_eq!(number_optional_number("1, 2"), Ok((1.0, 2.0)));
        assert_eq!(number_optional_number("1 2"), Ok((1.0, 2.0)));
        assert_eq!(number_optional_number("1"), Ok((1.0, 1.0)));

        assert_eq!(number_optional_number("-1, -2"), Ok((-1.0, -2.0)));
        assert_eq!(number_optional_number("-1 -2"), Ok((-1.0, -2.0)));
        assert_eq!(number_optional_number("-1"), Ok((-1.0, -1.0)));
    }

    #[test]
    fn invalid_number_optional_number() {
        assert!(number_optional_number("").is_err());
        assert!(number_optional_number("1x").is_err());
        assert!(number_optional_number("x1").is_err());
        assert!(number_optional_number("1 x").is_err());
        assert!(number_optional_number("1 , x").is_err());
        assert!(number_optional_number("1 , 2x").is_err());
        assert!(number_optional_number("1 2 x").is_err());
    }

    #[test]
    fn parses_integer() {
        assert_eq!(integer("1"), Ok(1));
        assert_eq!(integer("-1"), Ok(-1));
    }

    #[test]
    fn invalid_integer() {
        assert!(integer("").is_err());
        assert!(integer("1x").is_err());
        assert!(integer("1.5").is_err());
    }

    #[test]
    fn parses_integer_optional_integer() {
        assert_eq!(integer_optional_integer("1, 2"), Ok((1, 2)));
        assert_eq!(integer_optional_integer("1 2"), Ok((1, 2)));
        assert_eq!(integer_optional_integer("1"), Ok((1, 1)));

        assert_eq!(integer_optional_integer("-1, -2"), Ok((-1, -2)));
        assert_eq!(integer_optional_integer("-1 -2"), Ok((-1, -2)));
        assert_eq!(integer_optional_integer("-1"), Ok((-1, -1)));
    }

    #[test]
    fn invalid_integer_optional_integer() {
        assert!(integer_optional_integer("").is_err());
        assert!(integer_optional_integer("1x").is_err());
        assert!(integer_optional_integer("x1").is_err());
        assert!(integer_optional_integer("1 x").is_err());
        assert!(integer_optional_integer("1 , x").is_err());
        assert!(integer_optional_integer("1 , 2x").is_err());
        assert!(integer_optional_integer("1 2 x").is_err());
        assert!(integer_optional_integer("1.5").is_err());
        assert!(integer_optional_integer("1 2.5").is_err());
        assert!(integer_optional_integer("1, 2.5").is_err());
    }
}
<commit_msg>Generate slightly more meaningful ParseError messages<commit_after>use cssparser::{BasicParseError, BasicParseErrorKind, Parser, ParserInput, Token};
use markup5ever::QualName;

use std::str;

use crate::error::{NodeError, ValueErrorKind};

#[derive(Debug, Clone, PartialEq)]
pub struct ParseError {
    pub display: String,
}

impl ParseError {
    pub fn new<T: AsRef<str>>(msg: T) -> ParseError {
        ParseError {
            display: msg.as_ref().to_string(),
        }
    }
}

impl<'a> From<BasicParseError<'a>> for ParseError {
    fn from(e: BasicParseError<'_>) -> ParseError {
        let BasicParseError { kind, location: _ } =  e;

        let msg = match kind {
            BasicParseErrorKind::UnexpectedToken(_) => "unexpected token",
            BasicParseErrorKind::EndOfInput => "unexpected end of input",
            BasicParseErrorKind::AtRuleInvalid(_) => "invalid @-rule",
            BasicParseErrorKind::AtRuleBodyInvalid => "invalid @-rule body",
            BasicParseErrorKind::QualifiedRuleInvalid => "invalid qualified rule",
        };

        ParseError::new(msg)
    }
}

/// Trait to parse values using `cssparser::Parser`.
pub trait Parse: Sized {
    /// Error type for parse errors.
    type Err;

    /// Parses a value out of the `parser`.
    ///
    /// All value types should implement this for composability.
    fn parse(parser: &mut Parser<'_, '_>) -> Result<Self, Self::Err>;

    /// Convenience function to parse a value out of a `&str`.
    ///
    /// This is useful mostly for tests which want to avoid creating a
    /// `cssparser::Parser` by hand.
    fn parse_str(s: &str) -> Result<Self, Self::Err> {
        let mut input = ParserInput::new(s);
        let mut parser = Parser::new(&mut input);

        Self::parse(&mut parser).and_then(|r| {
            // FIXME: parser.expect_exhausted()?;
            Ok(r)
        })
    }
}

/// Extra utility methods for `cssparser::Parser`.
pub trait CssParserExt {
    /// Avoid infinities.
    fn expect_finite_number(&mut self) -> Result<f32, ValueErrorKind>;

    /// Consumes a comma if it exists, or does nothing.
    fn optional_comma(&mut self);
}

impl<'i, 't> CssParserExt for Parser<'i, 't> {
    fn expect_finite_number(&mut self) -> Result<f32, ValueErrorKind> {
        finite_f32(self.expect_number()?)
    }

    fn optional_comma(&mut self) {
        let _ = self.try_parse(|p| p.expect_comma());
    }
}

pub fn finite_f32(n: f32) -> Result<f32, ValueErrorKind> {
    if n.is_finite() {
        Ok(n)
    } else {
        Err(ValueErrorKind::Value("expected finite number".to_string()))
    }
}

pub trait ParseValue<T: Parse<Err = ValueErrorKind>> {
    /// Parses a `value` string into a type `T`.
    fn parse(&self, value: &str) -> Result<T, NodeError>;

    /// Parses a `value` string into a type `T` with an optional validation function.
    fn parse_and_validate<F: FnOnce(T) -> Result<T, ValueErrorKind>>(
        &self,
        value: &str,
        validate: F,
    ) -> Result<T, NodeError>;
}

impl<T: Parse<Err = ValueErrorKind>> ParseValue<T> for QualName {
    fn parse(&self, value: &str) -> Result<T, NodeError> {
        let mut input = ParserInput::new(value);
        let mut parser = Parser::new(&mut input);

        T::parse(&mut parser).map_err(|e| NodeError::attribute_error(self.clone(), e))
    }

    fn parse_and_validate<F: FnOnce(T) -> Result<T, ValueErrorKind>>(
        &self,
        value: &str,
        validate: F,
    ) -> Result<T, NodeError> {
        let mut input = ParserInput::new(value);
        let mut parser = Parser::new(&mut input);

        T::parse(&mut parser)
            .and_then(validate)
            .map_err(|e| NodeError::attribute_error(self.clone(), e))
    }
}

impl Parse for f64 {
    type Err = ValueErrorKind;

    fn parse(parser: &mut Parser<'_, '_>) -> Result<f64, ValueErrorKind> {
        Ok(f64::from(parser.expect_finite_number().map_err(|_| {
            ValueErrorKind::Parse(ParseError::new("expected number"))
        })?))
    }
}

// number
//
// https://www.w3.org/TR/SVG11/types.html#DataTypeNumber
pub fn number(s: &str) -> Result<f64, ValueErrorKind> {
    let mut input = ParserInput::new(s);
    let mut parser = Parser::new(&mut input);

    Ok(f64::from(parser.expect_finite_number()?))
}

// number-optional-number
//
// https://www.w3.org/TR/SVG/types.html#DataTypeNumberOptionalNumber

pub fn number_optional_number(s: &str) -> Result<(f64, f64), ValueErrorKind> {
    let mut input = ParserInput::new(s);
    let mut parser = Parser::new(&mut input);

    let x = f64::from(parser.expect_finite_number()?);

    if !parser.is_exhausted() {
        let state = parser.state();

        match *parser.next()? {
            Token::Comma => {}
            _ => parser.reset(&state),
        };

        let y = f64::from(parser.expect_finite_number()?);

        parser.expect_exhausted()?;

        Ok((x, y))
    } else {
        Ok((x, x))
    }
}

// integer
//
// https://www.w3.org/TR/SVG11/types.html#DataTypeInteger
pub fn integer(s: &str) -> Result<i32, ValueErrorKind> {
    let mut input = ParserInput::new(s);
    let mut parser = Parser::new(&mut input);

    Ok(parser.expect_integer()?)
}

// integer-optional-integer
//
// Like number-optional-number but with integers.
pub fn integer_optional_integer(s: &str) -> Result<(i32, i32), ValueErrorKind> {
    let mut input = ParserInput::new(s);
    let mut parser = Parser::new(&mut input);

    let x = parser.expect_integer()?;

    if !parser.is_exhausted() {
        let state = parser.state();

        match *parser.next()? {
            Token::Comma => {}
            _ => parser.reset(&state),
        };

        let y = parser.expect_integer()?;

        parser.expect_exhausted()?;

        Ok((x, y))
    } else {
        Ok((x, x))
    }
}

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

    #[test]
    fn parses_number_optional_number() {
        assert_eq!(number_optional_number("1, 2"), Ok((1.0, 2.0)));
        assert_eq!(number_optional_number("1 2"), Ok((1.0, 2.0)));
        assert_eq!(number_optional_number("1"), Ok((1.0, 1.0)));

        assert_eq!(number_optional_number("-1, -2"), Ok((-1.0, -2.0)));
        assert_eq!(number_optional_number("-1 -2"), Ok((-1.0, -2.0)));
        assert_eq!(number_optional_number("-1"), Ok((-1.0, -1.0)));
    }

    #[test]
    fn invalid_number_optional_number() {
        assert!(number_optional_number("").is_err());
        assert!(number_optional_number("1x").is_err());
        assert!(number_optional_number("x1").is_err());
        assert!(number_optional_number("1 x").is_err());
        assert!(number_optional_number("1 , x").is_err());
        assert!(number_optional_number("1 , 2x").is_err());
        assert!(number_optional_number("1 2 x").is_err());
    }

    #[test]
    fn parses_integer() {
        assert_eq!(integer("1"), Ok(1));
        assert_eq!(integer("-1"), Ok(-1));
    }

    #[test]
    fn invalid_integer() {
        assert!(integer("").is_err());
        assert!(integer("1x").is_err());
        assert!(integer("1.5").is_err());
    }

    #[test]
    fn parses_integer_optional_integer() {
        assert_eq!(integer_optional_integer("1, 2"), Ok((1, 2)));
        assert_eq!(integer_optional_integer("1 2"), Ok((1, 2)));
        assert_eq!(integer_optional_integer("1"), Ok((1, 1)));

        assert_eq!(integer_optional_integer("-1, -2"), Ok((-1, -2)));
        assert_eq!(integer_optional_integer("-1 -2"), Ok((-1, -2)));
        assert_eq!(integer_optional_integer("-1"), Ok((-1, -1)));
    }

    #[test]
    fn invalid_integer_optional_integer() {
        assert!(integer_optional_integer("").is_err());
        assert!(integer_optional_integer("1x").is_err());
        assert!(integer_optional_integer("x1").is_err());
        assert!(integer_optional_integer("1 x").is_err());
        assert!(integer_optional_integer("1 , x").is_err());
        assert!(integer_optional_integer("1 , 2x").is_err());
        assert!(integer_optional_integer("1 2 x").is_err());
        assert!(integer_optional_integer("1.5").is_err());
        assert!(integer_optional_integer("1 2.5").is_err());
        assert!(integer_optional_integer("1, 2.5").is_err());
    }
}
<|endoftext|>


<commit_before>use std;

use std::collections::HashMap;
use std::fmt::Display;
use std::path::Path;

use ::opcodes::{AddressingMode, OpCode};
use assembler::lexer::{Lexer, LexerError};
use assembler::parser::{Parser, ParserError};
use assembler::token::{LexerToken, ParserToken};

#[derive(Debug, PartialEq)]
pub struct Label(u16);

#[derive(Debug)]
pub struct AssemblerError {
    message: String,
}

impl AssemblerError {
    fn unknown_label<S>(label: S) -> AssemblerError
        where S: Into<String> + std::fmt::Display
    {
        AssemblerError::from(format!("Unknown label: '{}'", label))
    }

    fn relative_offset_too_large<S>(context: S) -> AssemblerError
        where S: Into<String> + Display
    {
        AssemblerError::from(format!("Branch too far: {}", context))
    }
}

impl From<String> for AssemblerError {
    fn from(error: String) -> AssemblerError {
        AssemblerError { message: error }
    }
}

impl From<LexerError> for AssemblerError {
    fn from(error: LexerError) -> AssemblerError {
        AssemblerError { message: error.message }
    }
}

impl From<ParserError> for AssemblerError {
    fn from(error: ParserError) -> AssemblerError {
        AssemblerError { message: error.message }
    }
}

pub struct CodeSegment {
    pub address: u16,
    pub code: Vec<u8>,
}

pub struct Assembler {
    symbol_table: HashMap<String, Label>,
}

impl Assembler {
    pub fn new() -> Assembler {
        Assembler { symbol_table: HashMap::new() }
    }

    pub fn assemble_string<S, O>(&mut self,
                                 code: S,
                                 offset: O)
                                 -> Result<Vec<CodeSegment>, AssemblerError>
        where S: Into<String>,
              O: Into<Option<u16>>
    {
        let code = code.into();
        let mut lexer = Lexer::new();
        let tokens = lexer.lex_string(code)?;
        let mut parser = Parser::new();
        let tokens = parser.parse(tokens)?;

        Ok(self.assemble(tokens, offset)?)
    }

    pub fn assemble_file<P, O>(&mut self,
                               path: P,
                               offset: O)
                               -> Result<Vec<CodeSegment>, AssemblerError>
        where P: AsRef<Path>,
              O: Into<Option<u16>>
    {
        let mut lexer = Lexer::new();
        let tokens = lexer.lex_file(path)?;
        let mut parser = Parser::new();
        let tokens = parser.parse(tokens)?;

        Ok(self.assemble(tokens, offset)?)
    }

    fn assemble<O>(&mut self,
                   tokens: Vec<ParserToken>,
                   offset: O)
                   -> Result<Vec<CodeSegment>, AssemblerError>
        where O: Into<Option<u16>>
    {
        let mut addr: u16 = offset.into().unwrap_or(0);

        // First, index the labels so we have addresses for them
        self.index_labels(&tokens, addr);

        // Now assemble the code
        let mut result = Vec::new();
        let mut last_addressing_mode = AddressingMode::Absolute;
        let mut current_segment = CodeSegment {
            address: addr,
            code: Vec::new(),
        };

        for token in tokens {
            // Push an opcode into the output and increment our address
            // offset
            if let ParserToken::OpCode(opcode) = token {
                current_segment.code.push(opcode.code);
                addr += opcode.length as u16;
                last_addressing_mode = opcode.mode;
            } else if let ParserToken::OrgDirective(org_addr) = token {
                result.push(current_segment);
                current_segment = CodeSegment {
                    address: addr,
                    code: Vec::new(),
                };
            } else if let ParserToken::RawByte(byte) = token {
                // Push raw bytes directly into the output
                current_segment.code.push(byte);
            } else if let ParserToken::LabelArg(ref label) = token {
                // Labels as arguments should be in the symbol table, look
                // it up and calculate the address direction/location
                if let Some(&Label(label_addr)) = self.symbol_table.get(label) {
                    if last_addressing_mode == AddressingMode::Absolute {
                        let low_byte = (label_addr & 0xFF) as u8;
                        let high_byte = ((label_addr >> 8) & 0xFF) as u8;

                        current_segment.code.push(low_byte);
                        current_segment.code.push(high_byte);
                    } else {
                        // Its relative.. lets generate a relative branch
                        if addr > label_addr {
                            let distance = (label_addr as i16 - addr as i16) as i8;
                            if distance < -128 || distance > 127 {
                                return Err(AssemblerError::relative_offset_too_large(format!("Attempted jump to {} at {:04X}", label, addr)));
                            }
                            current_segment.code.push(distance as u8);
                        } else {
                            let distance = label_addr - addr;
                            if distance > 127 {
                                return Err(AssemblerError::relative_offset_too_large(format!("Attempted jump to {} at {:04X}", label, addr)));
                            }
                            current_segment.code.push(distance as u8);
                        }
                    }
                } else {
                    return Err(AssemblerError::unknown_label(label.clone()));
                }
            }
        }

        result.push(current_segment);

        Ok(result)
    }

    /// Stores all labels in the code in a Symbol table for lookup later
    fn index_labels(&mut self, tokens: &[ParserToken], offset: u16) {
        let mut addr: u16 = offset;
        let mut last_addressing_mode = AddressingMode::Absolute;

        for token in tokens {
            if let &ParserToken::Label(ref label) = token {
                // Insert a label with the specified memory address
                // as its offset
                self.symbol_table.insert(label.clone(), Label(addr));
            } else if let &ParserToken::OpCode(opcode) = token {
                // Add the length of this opcode to our
                // address offset
                addr += opcode.length as u16;
                last_addressing_mode = opcode.mode;
            } else if let &ParserToken::OrgDirective(new_addr) = token {
                addr = new_addr
            }
        }
    }
}

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

    #[test]
    fn can_assemble_basic_code() {
        let mut assembler = Assembler::new();
        let segments = assembler.assemble_string("
            LDA $4400
        ",
                             None)
            .unwrap();

        assert_eq!(&[0xAD, 0x00, 0x44], &segments[0].code[..]);
    }

    #[test]
    fn can_jump_to_label_behind() {
        let mut assembler = Assembler::new();
        let segments = assembler.assemble_string("
            MAIN LDA $4400
            PHA
            JMP MAIN
        ",
                             None)
            .unwrap();

        assert_eq!(&[0xAD, 0x00, 0x44, 0x48, 0x4C, 0x00, 0x00],
                   &segments[0].code[..]);
    }

    #[test]
    fn can_jump_to_label_with_colon_behind() {
        let mut assembler = Assembler::new();
        let segments = assembler.assemble_string("
            MAIN:
                LDA $4400
                PHA
                JMP MAIN
        ",
                             None)
            .unwrap();

        assert_eq!(&[0xAD, 0x00, 0x44, 0x48, 0x4C, 0x00, 0x00],
                   &segments[0].code[..]);
    }

    #[test]
    fn can_jump_to_label_ahead() {
        let mut assembler = Assembler::new();
        let segments = assembler.assemble_string("
            JMP MAIN
            PHA
            LDX #15
            MAIN LDA $4400
            RTS
        ",
                             None)
            .unwrap();

        assert_eq!(&[0x4C, 0x06, 0x00, 0x48, 0xA2, 0x0F, 0xAD, 0x00, 0x44, 0x60],
                   &segments[0].code[..]);
    }

    #[test]
    fn can_use_variables() {
        let mut assembler = Assembler::new();
        let segments = assembler.assemble_string("
            MAIN_ADDRESS = $0000
            MAIN:
            LDX #15
            JMP MAIN_ADDRESS
        ",
                             None)
            .unwrap();

        assert_eq!(&[0xA2, 0x0F, 0x4C, 0x00, 0x00], &segments[0].code[..]);
    }

    #[test]
    fn can_use_variables_assigned_to_variables() {
        let mut assembler = Assembler::new();
        let segments = assembler.assemble_string("
            MAIN_ADDRESS = $0000
            MAIN_ADDRESS_INDIRECT_ONE = MAIN_ADDRESS
            MAIN_ADDRESS_INDIRECT_TWO = MAIN_ADDRESS_INDIRECT_ONE
            MAIN:
            LDX #15
            JMP MAIN_ADDRESS_INDIRECT_TWO
        ",
                             None)
            .unwrap();

        assert_eq!(&[0xA2, 0x0F, 0x4C, 0x00, 0x00], &segments[0].code[..]);
    }

    #[test]
    fn can_assemble_clearmem_implementation() {
        let mut assembler = Assembler::new();
        let segments = assembler.assemble_string("
            CLRMEM  LDA #$00
                    TAY
            CLRM1   STA ($FF),Y
                    INY
                    DEX
                    BNE CLRM1
                    RTS
        ",
                             None)
            .unwrap();

        assert_eq!(&[0xA9, 0x00, 0xA8, 0x91, 0xFF, 0xC8, 0xCA, 0xD0, 0xFA, 0x60],
                   &segments[0].code[..]);
    }

    #[test]
    fn can_assemble_clearmem_implementation_that_jumps_forward_and_is_lowercase() {
        let mut assembler = Assembler::new();
        let segments = assembler.assemble_string("
            jmp     clrmem
            lda     #$00
            beq     clrm1
            nop
            nop
            clrm1   sta ($ff),y
                    iny
                    dex
                    bne clrm1
                    rts
            clrmem  lda #$00
                    tay
            jmp     clrm1
        ",
                             None)
            .unwrap();

        assert_eq!(&[0x4C, 0x10, 0x00, 0xA9, 0x00, 0xF0, 0x02, 0xEA, 0xEA, 0x91, 0xFF, 0xC8,
                     0xCA, 0xD0, 0xFA, 0x60, 0xA9, 0x00, 0xA8, 0x4C, 0x09, 0x00],
                   &segments[0].code[..]);
    }

    #[test]
    fn can_assemble_clearmem_implementation_that_jumps_forward() {
        let mut assembler = Assembler::new();
        let segments = assembler.assemble_string("
            JMP     CLRMEM
            LDA     #$00
            BEQ     CLRM1
            NOP
            NOP
            BRK
            CLRM1   STA ($FF),Y
                    INY
                    DEX
                    BNE CLRM1
                    RTS
            CLRMEM  LDA #$00
                    TAY
            JMP     CLRM1
        ",
                             None)
            .unwrap();

        assert_eq!(&[0x4C, 0x11, 0x00, 0xA9, 0x00, 0xF0, 0x03, 0xEA, 0xEA, 0x00, 0x91, 0xFF,
                     0xC8, 0xCA, 0xD0, 0xFA, 0x60, 0xA9, 0x00, 0xA8, 0x4C, 0x0A, 0x00],
                   &segments[0].code[..]);
    }

    #[test]
    fn can_use_variables_for_indirect_addressing() {
        let mut assembler = Assembler::new();
        let segments = assembler.assemble_string("
            MAIN_ADDRESS = $0000
            MAIN:
            LDX #15
            LDA (MAIN_ADDRESS),Y
        ",
                             None)
            .unwrap();

        assert_eq!(&[0xA2, 0x0F, 0xB1, 0x00, 0x00], &segments[0].code[..]);
    }
}<commit_msg>Add code segmentation test<commit_after>use std;

use std::collections::HashMap;
use std::fmt::Display;
use std::path::Path;

use ::opcodes::{AddressingMode, OpCode};
use assembler::lexer::{Lexer, LexerError};
use assembler::parser::{Parser, ParserError};
use assembler::token::{LexerToken, ParserToken};

#[derive(Debug, PartialEq)]
pub struct Label(u16);

#[derive(Debug)]
pub struct AssemblerError {
    message: String,
}

impl AssemblerError {
    fn unknown_label<S>(label: S) -> AssemblerError
        where S: Into<String> + std::fmt::Display
    {
        AssemblerError::from(format!("Unknown label: '{}'", label))
    }

    fn relative_offset_too_large<S>(context: S) -> AssemblerError
        where S: Into<String> + Display
    {
        AssemblerError::from(format!("Branch too far: {}", context))
    }
}

impl From<String> for AssemblerError {
    fn from(error: String) -> AssemblerError {
        AssemblerError { message: error }
    }
}

impl From<LexerError> for AssemblerError {
    fn from(error: LexerError) -> AssemblerError {
        AssemblerError { message: error.message }
    }
}

impl From<ParserError> for AssemblerError {
    fn from(error: ParserError) -> AssemblerError {
        AssemblerError { message: error.message }
    }
}

#[derive(Debug)]
pub struct CodeSegment {
    pub address: u16,
    pub code: Vec<u8>,
}

pub struct Assembler {
    symbol_table: HashMap<String, Label>,
}

impl Assembler {
    pub fn new() -> Assembler {
        Assembler { symbol_table: HashMap::new() }
    }

    pub fn assemble_string<S, O>(&mut self,
                                 code: S,
                                 offset: O)
                                 -> Result<Vec<CodeSegment>, AssemblerError>
        where S: Into<String>,
              O: Into<Option<u16>>
    {
        let code = code.into();
        let mut lexer = Lexer::new();
        let tokens = lexer.lex_string(code)?;
        let mut parser = Parser::new();
        let tokens = parser.parse(tokens)?;

        Ok(self.assemble(tokens, offset)?)
    }

    pub fn assemble_file<P, O>(&mut self,
                               path: P,
                               offset: O)
                               -> Result<Vec<CodeSegment>, AssemblerError>
        where P: AsRef<Path>,
              O: Into<Option<u16>>
    {
        let mut lexer = Lexer::new();
        let tokens = lexer.lex_file(path)?;
        let mut parser = Parser::new();
        let tokens = parser.parse(tokens)?;

        Ok(self.assemble(tokens, offset)?)
    }

    fn assemble<O>(&mut self,
                   tokens: Vec<ParserToken>,
                   offset: O)
                   -> Result<Vec<CodeSegment>, AssemblerError>
        where O: Into<Option<u16>>
    {
        let mut addr: u16 = offset.into().unwrap_or(0);

        // First, index the labels so we have addresses for them
        self.index_labels(&tokens, addr);

        // Now assemble the code
        let mut result = Vec::new();
        let mut last_addressing_mode = AddressingMode::Absolute;
        let mut current_segment = CodeSegment {
            address: addr,
            code: Vec::new(),
        };

        for token in tokens {
            // Push an opcode into the output and increment our address
            // offset
            if let ParserToken::OpCode(opcode) = token {
                current_segment.code.push(opcode.code);
                addr += opcode.length as u16;
                last_addressing_mode = opcode.mode;
            } else if let ParserToken::OrgDirective(org_addr) = token {
                if current_segment.code.len() > 0 {
                    result.push(current_segment);
                }
                current_segment = CodeSegment {
                    address: org_addr,
                    code: Vec::new(),
                };
            } else if let ParserToken::RawByte(byte) = token {
                // Push raw bytes directly into the output
                current_segment.code.push(byte);
            } else if let ParserToken::LabelArg(ref label) = token {
                // Labels as arguments should be in the symbol table, look
                // it up and calculate the address direction/location
                if let Some(&Label(label_addr)) = self.symbol_table.get(label) {
                    if last_addressing_mode == AddressingMode::Absolute {
                        let low_byte = (label_addr & 0xFF) as u8;
                        let high_byte = ((label_addr >> 8) & 0xFF) as u8;

                        current_segment.code.push(low_byte);
                        current_segment.code.push(high_byte);
                    } else {
                        // Its relative.. lets generate a relative branch
                        if addr > label_addr {
                            let distance = (label_addr as i16 - addr as i16) as i8;
                            if distance < -128 || distance > 127 {
                                return Err(AssemblerError::relative_offset_too_large(format!("Attempted jump to {} at {:04X}", label, addr)));
                            }
                            current_segment.code.push(distance as u8);
                        } else {
                            let distance = label_addr - addr;
                            if distance > 127 {
                                return Err(AssemblerError::relative_offset_too_large(format!("Attempted jump to {} at {:04X}", label, addr)));
                            }
                            current_segment.code.push(distance as u8);
                        }
                    }
                } else {
                    return Err(AssemblerError::unknown_label(label.clone()));
                }
            }
        }

        result.push(current_segment);

        Ok(result)
    }

    /// Stores all labels in the code in a Symbol table for lookup later
    fn index_labels(&mut self, tokens: &[ParserToken], offset: u16) {
        let mut addr: u16 = offset;
        let mut last_addressing_mode = AddressingMode::Absolute;

        for token in tokens {
            if let &ParserToken::Label(ref label) = token {
                // Insert a label with the specified memory address
                // as its offset
                self.symbol_table.insert(label.clone(), Label(addr));
            } else if let &ParserToken::OpCode(opcode) = token {
                // Add the length of this opcode to our
                // address offset
                addr += opcode.length as u16;
                last_addressing_mode = opcode.mode;
            } else if let &ParserToken::OrgDirective(new_addr) = token {
                addr = new_addr
            }
        }
    }
}

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

    #[test]
    fn can_assemble_basic_code() {
        let mut assembler = Assembler::new();
        let segments = assembler.assemble_string("
            LDA $4400
        ",
                             None)
            .unwrap();

        assert_eq!(&[0xAD, 0x00, 0x44], &segments[0].code[..]);
    }

    #[test]
    fn can_jump_to_label_behind() {
        let mut assembler = Assembler::new();
        let segments = assembler.assemble_string("
            MAIN LDA $4400
            PHA
            JMP MAIN
        ",
                             None)
            .unwrap();

        assert_eq!(&[0xAD, 0x00, 0x44, 0x48, 0x4C, 0x00, 0x00],
                   &segments[0].code[..]);
    }

    #[test]
    fn can_jump_to_label_with_colon_behind() {
        let mut assembler = Assembler::new();
        let segments = assembler.assemble_string("
            MAIN:
                LDA $4400
                PHA
                JMP MAIN
        ",
                             None)
            .unwrap();

        assert_eq!(&[0xAD, 0x00, 0x44, 0x48, 0x4C, 0x00, 0x00],
                   &segments[0].code[..]);
    }

    #[test]
    fn can_jump_to_label_ahead() {
        let mut assembler = Assembler::new();
        let segments = assembler.assemble_string("
            JMP MAIN
            PHA
            LDX #15
            MAIN LDA $4400
            RTS
        ",
                             None)
            .unwrap();

        assert_eq!(&[0x4C, 0x06, 0x00, 0x48, 0xA2, 0x0F, 0xAD, 0x00, 0x44, 0x60],
                   &segments[0].code[..]);
    }

    #[test]
    fn can_use_variables() {
        let mut assembler = Assembler::new();
        let segments = assembler.assemble_string("
            MAIN_ADDRESS = $0000
            MAIN:
            LDX #15
            JMP MAIN_ADDRESS
        ",
                             None)
            .unwrap();

        assert_eq!(&[0xA2, 0x0F, 0x4C, 0x00, 0x00], &segments[0].code[..]);
    }

    #[test]
    fn can_use_variables_assigned_to_variables() {
        let mut assembler = Assembler::new();
        let segments = assembler.assemble_string("
            MAIN_ADDRESS = $0000
            MAIN_ADDRESS_INDIRECT_ONE = MAIN_ADDRESS
            MAIN_ADDRESS_INDIRECT_TWO = MAIN_ADDRESS_INDIRECT_ONE
            MAIN:
            LDX #15
            JMP MAIN_ADDRESS_INDIRECT_TWO
        ",
                             None)
            .unwrap();

        assert_eq!(&[0xA2, 0x0F, 0x4C, 0x00, 0x00], &segments[0].code[..]);
    }

    #[test]
    fn can_assemble_clearmem_implementation() {
        let mut assembler = Assembler::new();
        let segments = assembler.assemble_string("
            CLRMEM  LDA #$00
                    TAY
            CLRM1   STA ($FF),Y
                    INY
                    DEX
                    BNE CLRM1
                    RTS
        ",
                             None)
            .unwrap();

        assert_eq!(&[0xA9, 0x00, 0xA8, 0x91, 0xFF, 0xC8, 0xCA, 0xD0, 0xFA, 0x60],
                   &segments[0].code[..]);
    }

    #[test]
    fn can_assemble_clearmem_implementation_that_jumps_forward_and_is_lowercase() {
        let mut assembler = Assembler::new();
        let segments = assembler.assemble_string("
            jmp     clrmem
            lda     #$00
            beq     clrm1
            nop
            nop
            clrm1   sta ($ff),y
                    iny
                    dex
                    bne clrm1
                    rts
            clrmem  lda #$00
                    tay
            jmp     clrm1
        ",
                             None)
            .unwrap();

        assert_eq!(&[0x4C, 0x10, 0x00, 0xA9, 0x00, 0xF0, 0x02, 0xEA, 0xEA, 0x91, 0xFF, 0xC8,
                     0xCA, 0xD0, 0xFA, 0x60, 0xA9, 0x00, 0xA8, 0x4C, 0x09, 0x00],
                   &segments[0].code[..]);
    }

    #[test]
    fn can_assemble_clearmem_implementation_that_jumps_forward() {
        let mut assembler = Assembler::new();
        let segments = assembler.assemble_string("
            JMP     CLRMEM
            LDA     #$00
            BEQ     CLRM1
            NOP
            NOP
            BRK
            CLRM1   STA ($FF),Y
                    INY
                    DEX
                    BNE CLRM1
                    RTS
            CLRMEM  LDA #$00
                    TAY
            JMP     CLRM1
        ",
                             None)
            .unwrap();

        assert_eq!(&[0x4C, 0x11, 0x00, 0xA9, 0x00, 0xF0, 0x03, 0xEA, 0xEA, 0x00, 0x91, 0xFF,
                     0xC8, 0xCA, 0xD0, 0xFA, 0x60, 0xA9, 0x00, 0xA8, 0x4C, 0x0A, 0x00],
                   &segments[0].code[..]);
    }

    #[test]
    fn can_use_variables_for_indirect_addressing() {
        let mut assembler = Assembler::new();
        let segments = assembler.assemble_string("
            MAIN_ADDRESS = $0000
            MAIN:
            LDX #15
            LDA (MAIN_ADDRESS),Y
        ",
                             None)
            .unwrap();

        assert_eq!(&[0xA2, 0x0F, 0xB1, 0x00, 0x00], &segments[0].code[..]);
    }

    #[test]
    fn can_assign_code_segments_to_different_memory_addresses() {
        let mut assembler = Assembler::new();
        let segments = assembler.assemble_string("
            .ORG $C000
            LDA #$FF
            STA $2000

            .ORG $100
            LDA #$AA
            STA $2001
        ",
                             None)
            .unwrap();

        println!("SEGMENTS: {:?}", segments);

        assert_eq!(0xC000, segments[0].address);
        assert_eq!(0x0100, segments[1].address);
    }
}<|endoftext|>


<commit_before>use kernel::eyre::Result;
use kernel_micro::{include_file, MicroKernel};

pub async fn new() -> Result<MicroKernel> {
    MicroKernel::new(
        "python",
        ("python3", "*", &["{{script}}"]),
        include_file!("python_kernel.py"),
        &[include_file!("python_codec.py")],
        "{{name}} = decode_value(\"{{json}}\")",
        "{{name}}",
    )
    .await
}

#[cfg(test)]
mod tests {
    use super::*;
    use kernel::{stencila_schema::Node, KernelTrait};
    use test_utils::{assert_json_eq, print_logs, serde_json::json};

    /// Tests of basic functionality
    /// This test is replicated in all the microkernels.
    /// Other test should be written for language specific quirks and regressions.
    #[tokio::test]
    async fn basics() -> Result<()> {
        print_logs();

        let mut kernel = new().await?;
        kernel.start().await?;

        // Assign a variable and output it
        let (outputs, messages) = kernel.exec("a = 2\na").await?;
        assert_json_eq!(messages, json!([]));
        assert_json_eq!(outputs, [2]);

        // Print the variable twice and then output it
        let (outputs, messages) = kernel.exec("print(a)\nprint(a)\na").await?;
        assert_json_eq!(messages, json!([]));
        assert_json_eq!(outputs, [2, 2, 2]);

        // Syntax error
        let (outputs, messages) = kernel.exec("bad ^ # syntax").await?;
        assert_json_eq!(messages[0].error_type, "SyntaxError");
        assert_json_eq!(messages[0].error_message, "invalid syntax (<code>, line 1)");
        assert!(messages[0].stack_trace.is_some());
        assert_json_eq!(outputs, json!([]));

        // Runtime error
        let (outputs, messages) = kernel.exec("foo").await?;
        assert_json_eq!(messages[0].error_type, "NameError");
        assert_json_eq!(messages[0].error_message, "name 'foo' is not defined");
        assert!(messages[0].stack_trace.is_some());
        assert_json_eq!(outputs, json!([]));

        // Set and get another variable
        kernel.set("b", Node::Integer(3)).await?;
        let b = kernel.get("b").await?;
        assert_json_eq!(b, 3);

        // Use both variables
        let (outputs, messages) = kernel.exec("a*b").await?;
        assert_json_eq!(messages, json!([]));
        assert_json_eq!(outputs, [6]);

        Ok(())
    }
}
<commit_msg>test(Python kernel): Remove print logs<commit_after>use kernel::eyre::Result;
use kernel_micro::{include_file, MicroKernel};

pub async fn new() -> Result<MicroKernel> {
    MicroKernel::new(
        "python",
        ("python3", "*", &["{{script}}"]),
        include_file!("python_kernel.py"),
        &[include_file!("python_codec.py")],
        "{{name}} = decode_value(\"{{json}}\")",
        "{{name}}",
    )
    .await
}

#[cfg(test)]
mod tests {
    use super::*;
    use kernel::{stencila_schema::Node, KernelTrait};
    use test_utils::{assert_json_eq, serde_json::json};

    /// Tests of basic functionality
    /// This test is replicated in all the microkernels.
    /// Other test should be written for language specific quirks and regressions.
    #[tokio::test]
    async fn basics() -> Result<()> {
        let mut kernel = new().await?;
        kernel.start().await?;

        // Assign a variable and output it
        let (outputs, messages) = kernel.exec("a = 2\na").await?;
        assert_json_eq!(messages, json!([]));
        assert_json_eq!(outputs, [2]);

        // Print the variable twice and then output it
        let (outputs, messages) = kernel.exec("print(a)\nprint(a)\na").await?;
        assert_json_eq!(messages, json!([]));
        assert_json_eq!(outputs, [2, 2, 2]);

        // Syntax error
        let (outputs, messages) = kernel.exec("bad ^ # syntax").await?;
        assert_json_eq!(messages[0].error_type, "SyntaxError");
        assert_json_eq!(messages[0].error_message, "invalid syntax (<code>, line 1)");
        assert!(messages[0].stack_trace.is_some());
        assert_json_eq!(outputs, json!([]));

        // Runtime error
        let (outputs, messages) = kernel.exec("foo").await?;
        assert_json_eq!(messages[0].error_type, "NameError");
        assert_json_eq!(messages[0].error_message, "name 'foo' is not defined");
        assert!(messages[0].stack_trace.is_some());
        assert_json_eq!(outputs, json!([]));

        // Set and get another variable
        kernel.set("b", Node::Integer(3)).await?;
        let b = kernel.get("b").await?;
        assert_json_eq!(b, 3);

        // Use both variables
        let (outputs, messages) = kernel.exec("a*b").await?;
        assert_json_eq!(messages, json!([]));
        assert_json_eq!(outputs, [6]);

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


<commit_before>use test_tools::test_suite;

#[ derive( Debug ) ]
struct IntegrationModuleTest< 'a >
{
  dependency_name : &'a str,
  version : &'a str,
  code : &'a str,
  test_path : std::path::PathBuf,
}

impl< 'a > IntegrationModuleTest< 'a >
{
  fn new( dependency_name : &'a str ) -> IntegrationModuleTest< 'a >
  {
    let mut test_path = std::env::temp_dir();
    test_path.push( dependency_name );

    IntegrationModuleTest
    {
      dependency_name,
      version : "*",
      code : "",
      test_path,
    }
  }

  fn version( &mut self, version : &'a str ) -> &mut IntegrationModuleTest< 'a >
  {
    self.version = version;
    self
  }

  fn code( &mut self, code : &'a str ) -> &mut IntegrationModuleTest< 'a >
  {
    self.code = code;
    self
  }

  fn form( &self ) -> Result<(), &'static str>
  {
    std::fs::create_dir( &self.test_path ).unwrap();
    let output = std::process::Command::new( "cargo" )
    .current_dir( &self.test_path )
    .args( [ "new", "--bin", self.dependency_name ] )
    .output()
    .expect("Failed to execute command");
    println!( "{:?}", output );

    Ok( () )
  }

  fn perform( &self ) -> Result<(), &'static str>
  {
    let mut test_path = self.test_path.clone();
    test_path.push( self.dependency_name );

    let output = std::process::Command::new( "cargo" )
    .current_dir( test_path )
    .arg( "run --release" )
    .output()
    .expect("Failed to execute command");
    println!( "{:?}", output );

    Ok( () )
  }

  fn clean( &self ) -> Result<(), &'static str>
  {
    std::fs::remove_dir_all( &self.test_path ).unwrap();
    Ok( () )
  }
}

//

fn run_tests_test()
{
  let t = IntegrationModuleTest::new( "wtools" );
  t.form().unwrap();
  t.clean().unwrap();
}

//

test_suite!
{
  run_tests,
  // run_samples,
}

/* xxx */
<commit_msg>Extend integration test `integration_test`, setup config and write code for test case<commit_after>use test_tools::test_suite;

#[ derive( Debug ) ]
struct IntegrationModuleTest< 'a >
{
  dependency_name : &'a str,
  version : &'a str,
  code : &'a str,
  test_path : std::path::PathBuf,
}

impl< 'a > IntegrationModuleTest< 'a >
{
  fn new( dependency_name : &'a str ) -> IntegrationModuleTest< 'a >
  {
    let mut test_path = std::env::temp_dir();
    test_path.push( dependency_name );

    IntegrationModuleTest
    {
      dependency_name,
      version : "*",
      code : "",
      test_path,
    }
  }

  #[ allow( dead_code ) ]
  fn version( &mut self, version : &'a str ) -> &mut IntegrationModuleTest< 'a >
  {
    self.version = version;
    self
  }

  #[ allow( dead_code ) ]
  fn code( &mut self, code : &'a str ) -> &mut IntegrationModuleTest< 'a >
  {
    self.code = code;
    self
  }

  fn form( &self ) -> Result<(), &'static str>
  {
    std::fs::create_dir( &self.test_path ).unwrap();

    let mut test_path = self.test_path.clone();

    /* create binary test module */
    let test_name = format!( "{}_test", self.dependency_name );
    let output = std::process::Command::new( "cargo" )
    .current_dir( &test_path )
    .args([ "new", "--bin", &test_name ])
    .output()
    .expect( "Failed to execute command" );
    println!( "{}", std::str::from_utf8( &output.stderr ).expect( "Found invalid UTF-8" ) );

    test_path.push( test_name );

    /* setup config */
    let dependency_name = format!( "dependencies.{}", self.dependency_name );
    let output = std::process::Command::new( "selector" )
    .current_dir( &test_path )
    .args([ "set", "./Cargo.toml", &dependency_name, "*" ])
    .output()
    .expect( "Failed to execute command" );
    let output = std::str::from_utf8( &output.stdout ).expect( "Found invalid UTF-8" );
    let mut config_path = test_path.clone();
    config_path.push( "Cargo.toml" );
    std::fs::write( config_path, output ).unwrap();

    /* write code */
    test_path.push( "src" );
    test_path.push( "main.rs" );
    let code = format!( "#[ allow( unused_imports ) ]\nfn main()\n{{\n  use {}::*;\n  {}\n}}", self.dependency_name, self.code );
    std::fs::write( &test_path, code ).unwrap();

    Ok( () )
  }

  fn run( &self ) -> Result<(), &'static str>
  {
    let mut test_path = self.test_path.clone();
    let test_name = format!( "{}_test", self.dependency_name );
    test_path.push( test_name );

    let output = std::process::Command::new( "cargo" )
    .current_dir( test_path )
    .args([ "test", "--release" ])
    .output()
    .unwrap();
    println!( "{}", std::str::from_utf8( &output.stderr ).expect( "Found invalid UTF-8" ) );

    Ok( () )
  }

  fn clean( &self ) -> Result<(), &'static str>
  {
    std::fs::remove_dir_all( &self.test_path ).unwrap();
    Ok( () )
  }
}

//

fn run_tests_test()
{
  test_default( "wtools" );
  test_default( "wtest_basic" );

  //

  fn test_default( module_name : &str )
  {
    let t = IntegrationModuleTest::new( module_name );
    t.form().unwrap();
    t.run().unwrap();
    t.clean().unwrap();
  }
}

//

test_suite!
{
  run_tests,
  // run_samples,
}

/* xxx */
<|endoftext|>


<commit_before>use crate::context::LintContext;
use crate::rustc_middle::ty::TypeFoldable;
use crate::LateContext;
use crate::LateLintPass;
use rustc_hir::def::DefKind;
use rustc_hir::{Expr, ExprKind};
use rustc_middle::ty;
use rustc_span::symbol::sym;

declare_lint! {
    /// The `noop_method_call` lint detects specific calls to noop methods
    /// such as a calling `<&T as Clone>::clone` where `T: !Clone`.
    ///
    /// ### Example
    ///
    /// ```rust
    /// # #![allow(unused)]
    /// #![deny(noop_method_call)]
    /// struct Foo;
    /// let foo = &Foo;
    /// let clone: &Foo = foo.clone();
    /// ```
    ///
    /// {{produces}}
    ///
    /// ### Explanation
    ///
    /// Some method calls are noops meaning that they do nothing. Usually such methods
    /// are the result of blanket implementations that happen to create some method invocations
    /// that end up not doing anything. For instance, `Clone` is implemented on all `&T`, but
    /// calling `clone` on a `&T` where `T` does not implement clone, actually doesn't do anything
    /// as references are copy. This lint detects these calls and warns the user about them.
    pub NOOP_METHOD_CALL,
    Allow,
    "detects the use of well-known noop methods"
}

declare_lint_pass!(NoopMethodCall => [NOOP_METHOD_CALL]);

impl<'tcx> LateLintPass<'tcx> for NoopMethodCall {
    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
        // We only care about method calls.
        let (call, elements) = match expr.kind {
            ExprKind::MethodCall(call, _, elements, _) => (call, elements),
            _ => return,
        };
        // We only care about method calls corresponding to the `Clone`, `Deref` and `Borrow`
        // traits and ignore any other method call.
        let (trait_id, did) = match cx.typeck_results().type_dependent_def(expr.hir_id) {
            // Verify we are dealing with a method/associated function.
            Some((DefKind::AssocFn, did)) => match cx.tcx.trait_of_item(did) {
                // Check that we're dealing with a trait method for one of the traits we care about.
                Some(trait_id)
                    if [sym::Clone, sym::Deref, sym::Borrow]
                        .iter()
                        .any(|s| cx.tcx.is_diagnostic_item(*s, trait_id)) =>
                {
                    (trait_id, did)
                }
                _ => return,
            },
            _ => return,
        };
        let substs = cx.typeck_results().node_substs(expr.hir_id);
        if substs.needs_subst() {
            // We can't resolve on types that require monomorphization, so we don't handle them if
            // we need to perfom substitution.
            return;
        }
        let param_env = cx.tcx.param_env(trait_id);
        // Resolve the trait method instance.
        let i = match ty::Instance::resolve(cx.tcx, param_env, did, substs) {
            Ok(Some(i)) => i,
            _ => return,
        };
        // (Re)check that it implements the noop diagnostic.
        for s in [sym::noop_method_clone, sym::noop_method_deref, sym::noop_method_borrow].iter() {
            if cx.tcx.is_diagnostic_item(*s, i.def_id()) {
                let method = &call.ident.name;
                let receiver = &elements[0];
                let receiver_ty = cx.typeck_results().expr_ty(receiver);
                let expr_ty = cx.typeck_results().expr_ty_adjusted(expr);
                if receiver_ty != expr_ty {
                    // This lint will only trigger if the receiver type and resulting expression \
                    // type are the same, implying that the method call is unnecessary.
                    return;
                }
                let expr_span = expr.span;
                let note = format!(
                    "the type `{:?}` which `{}` is being called on is the same as \
                     the type returned from `{}`, so the method call does not do \
                     anything and can be removed",
                    receiver_ty, method, method,
                );

                let span = expr_span.with_lo(receiver.span.hi());
                cx.struct_span_lint(NOOP_METHOD_CALL, span, |lint| {
                    let method = &call.ident.name;
                    let message = format!(
                        "call to `.{}()` on a reference in this situation does nothing",
                        &method,
                    );
                    lint.build(&message)
                        .span_label(span, "unnecessary method call")
                        .note(&note)
                        .emit()
                });
            }
        }
    }
}
<commit_msg>Warn in doc test<commit_after>use crate::context::LintContext;
use crate::rustc_middle::ty::TypeFoldable;
use crate::LateContext;
use crate::LateLintPass;
use rustc_hir::def::DefKind;
use rustc_hir::{Expr, ExprKind};
use rustc_middle::ty;
use rustc_span::symbol::sym;

declare_lint! {
    /// The `noop_method_call` lint detects specific calls to noop methods
    /// such as a calling `<&T as Clone>::clone` where `T: !Clone`.
    ///
    /// ### Example
    ///
    /// ```rust
    /// # #![allow(unused)]
    /// #![warn(noop_method_call)]
    /// struct Foo;
    /// let foo = &Foo;
    /// let clone: &Foo = foo.clone();
    /// ```
    ///
    /// {{produces}}
    ///
    /// ### Explanation
    ///
    /// Some method calls are noops meaning that they do nothing. Usually such methods
    /// are the result of blanket implementations that happen to create some method invocations
    /// that end up not doing anything. For instance, `Clone` is implemented on all `&T`, but
    /// calling `clone` on a `&T` where `T` does not implement clone, actually doesn't do anything
    /// as references are copy. This lint detects these calls and warns the user about them.
    pub NOOP_METHOD_CALL,
    Allow,
    "detects the use of well-known noop methods"
}

declare_lint_pass!(NoopMethodCall => [NOOP_METHOD_CALL]);

impl<'tcx> LateLintPass<'tcx> for NoopMethodCall {
    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
        // We only care about method calls.
        let (call, elements) = match expr.kind {
            ExprKind::MethodCall(call, _, elements, _) => (call, elements),
            _ => return,
        };
        // We only care about method calls corresponding to the `Clone`, `Deref` and `Borrow`
        // traits and ignore any other method call.
        let (trait_id, did) = match cx.typeck_results().type_dependent_def(expr.hir_id) {
            // Verify we are dealing with a method/associated function.
            Some((DefKind::AssocFn, did)) => match cx.tcx.trait_of_item(did) {
                // Check that we're dealing with a trait method for one of the traits we care about.
                Some(trait_id)
                    if [sym::Clone, sym::Deref, sym::Borrow]
                        .iter()
                        .any(|s| cx.tcx.is_diagnostic_item(*s, trait_id)) =>
                {
                    (trait_id, did)
                }
                _ => return,
            },
            _ => return,
        };
        let substs = cx.typeck_results().node_substs(expr.hir_id);
        if substs.needs_subst() {
            // We can't resolve on types that require monomorphization, so we don't handle them if
            // we need to perfom substitution.
            return;
        }
        let param_env = cx.tcx.param_env(trait_id);
        // Resolve the trait method instance.
        let i = match ty::Instance::resolve(cx.tcx, param_env, did, substs) {
            Ok(Some(i)) => i,
            _ => return,
        };
        // (Re)check that it implements the noop diagnostic.
        for s in [sym::noop_method_clone, sym::noop_method_deref, sym::noop_method_borrow].iter() {
            if cx.tcx.is_diagnostic_item(*s, i.def_id()) {
                let method = &call.ident.name;
                let receiver = &elements[0];
                let receiver_ty = cx.typeck_results().expr_ty(receiver);
                let expr_ty = cx.typeck_results().expr_ty_adjusted(expr);
                if receiver_ty != expr_ty {
                    // This lint will only trigger if the receiver type and resulting expression \
                    // type are the same, implying that the method call is unnecessary.
                    return;
                }
                let expr_span = expr.span;
                let note = format!(
                    "the type `{:?}` which `{}` is being called on is the same as \
                     the type returned from `{}`, so the method call does not do \
                     anything and can be removed",
                    receiver_ty, method, method,
                );

                let span = expr_span.with_lo(receiver.span.hi());
                cx.struct_span_lint(NOOP_METHOD_CALL, span, |lint| {
                    let method = &call.ident.name;
                    let message = format!(
                        "call to `.{}()` on a reference in this situation does nothing",
                        &method,
                    );
                    lint.build(&message)
                        .span_label(span, "unnecessary method call")
                        .note(&note)
                        .emit()
                });
            }
        }
    }
}
<|endoftext|>