<commit_before>use std::fmt;
use super::utils::{into_map, into_string, remove};
use super::{
    CurrentUser,
    FriendSourceFlags,
    GuildContainer,
    GuildId,
    GuildInfo,
    Mention,
    RoleId,
    UserSettings,
    User,
};
use ::client::rest::GuildPagination;
use ::internal::prelude::*;
use ::utils::decode_array;

#[cfg(feature = "methods")]
use serde_json::builder::ObjectBuilder;
#[cfg(feature = "methods")]
use super::Message;
#[cfg(feature = "methods")]
use time::Timespec;
#[cfg(feature = "methods")]
use ::client::rest;

#[cfg(feature = "cache")]
use ::client::CACHE;

impl CurrentUser {
    /// Returns the formatted URL of the user's icon, if one exists.
    pub fn avatar_url(&self) -> Option<String> {
        self.avatar.as_ref().map(|av|
            format!(cdn!("/avatars/{}/{}.jpg"), self.id, av))
    }

    /// Gets a list of guilds that the current user is in.
    pub fn guilds(&self) -> Result<Vec<GuildInfo>> {
        rest::get_guilds(GuildPagination::After(GuildId(0)), 100)
    }
}

impl User {
    /// Returns the formatted URL of the user's icon, if one exists.
    pub fn avatar_url(&self) -> Option<String> {
        self.avatar.as_ref().map(|av|
            format!(cdn!("/avatars/{}/{}.jpg"), self.id, av))
    }

    /// Retrieves the time that this user was created at.
    #[cfg(feature = "methods")]
    #[inline]
    pub fn created_at(&self) -> Timespec {
        self.id.created_at()
    }

    /// This is an alias of [direct_message].
    ///
    /// [direct_message]: #method.direct_message
    #[cfg(feature="methods")]
    pub fn dm(&self, content: &str) -> Result<Message> {
        self.direct_message(content)
    }

    /// Send a direct message to a user. This will create or retrieve the
    /// PrivateChannel over REST if one is not already in the cache, and then
    /// send a message to it.
    #[cfg(feature="methods")]
    pub fn direct_message(&self, content: &str)
        -> Result<Message> {
        let private_channel_id = feature_cache! {{
            let finding = CACHE.read()
                .unwrap()
                .private_channels
                .values()
                .find(|ch| ch.recipient.id == self.id)
                .map(|ch| ch.id);

            if let Some(finding) = finding {
                finding
            } else {
                let map = ObjectBuilder::new()
                    .insert("recipient_id", self.id.0)
                    .build();

                try!(rest::create_private_channel(map)).id
            }
        } else {
            let map = ObjectBuilder::new()
                .insert("recipient_id", self.id.0)
                .build();

            try!(rest::create_private_channel(map)).id
        }};

        let map = ObjectBuilder::new()
            .insert("content", content)
            .insert("nonce", "")
            .insert("tts", false)
            .build();

        rest::send_message(private_channel_id.0, map)
    }

    /// Check if a user has a [`Role`]. This will retrieve the
    /// [`Guild`] from the [`Cache`] if it is available, and then check if that
    /// guild has the given [`Role`].
    ///
    /// If the [`Guild`] is not present, then the guild will be retrieved from
    /// the API and the cache will be updated with it.
    ///
    /// If there are issues with requesting the API, then `false` will be
    /// returned.
    ///
    /// Three forms of data may be passed in to the guild parameter: either a
    /// [`Guild`] itself, a [`GuildId`], or a `u64`.
    ///
    /// # Examples
    ///
    /// Check if a guild has a [`Role`] by Id:
    ///
    /// ```rust,ignore
    /// // Assumes a 'guild' and `role_id` have already been bound
    /// context.message.author.has_role(guild, role_id);
    /// ```
    ///
    /// [`Guild`]: struct.Guild.html
    /// [`GuildId`]: struct.GuildId.html
    /// [`Role`]: struct.Role.html
    /// [`Cache`]: ../ext/cache/struct.Cache.html
    pub fn has_role<G, R>(&self, guild: G, role: R) -> bool
        where G: Into<GuildContainer>, R: Into<RoleId> {
        let role_id = role.into();

        match guild.into() {
            GuildContainer::Guild(guild) => {
                guild.roles.get(&role_id).is_some()
            },
            GuildContainer::Id(guild_id) => {
                feature_cache! {{
                    let cache = CACHE.read().unwrap();

                    cache.get_role(guild_id, role_id).is_some()
                } else {
                    true
                }}
            },
        }
    }

    /// Return a [`Mention`] which will ping this user.
    ///
    /// [`Mention`]: struct.Mention.html
    pub fn mention(&self) -> Mention {
        self.id.mention()
    }
}

impl fmt::Display for User {
    /// Formats a string which will mention the user.
    // This is in the format of: `<@USER_ID>`
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(&self.id.mention(), f)
    }
}

impl UserSettings {
    #[doc(hidden)]
    pub fn decode(value: Value) -> Result<Option<UserSettings>> {
        let mut map = try!(into_map(value));

        if map.is_empty() {
            return Ok(None);
        }

        Ok(UserSettings {
            convert_emoticons: req!(try!(remove(&mut map, "convert_emoticons")).as_bool()),
            enable_tts_command: req!(try!(remove(&mut map, "enable_tts_command")).as_bool()),
            friend_source_flags: try!(remove(&mut map, "friend_source_flags").and_then(FriendSourceFlags::decode)),
            inline_attachment_media: req!(try!(remove(&mut map, "inline_attachment_media")).as_bool()),
            inline_embed_media: req!(try!(remove(&mut map, "inline_embed_media")).as_bool()),
            locale: try!(remove(&mut map, "locale").and_then(into_string)),
            message_display_compact: req!(try!(remove(&mut map, "message_display_compact")).as_bool()),
            render_embeds: req!(try!(remove(&mut map, "render_embeds")).as_bool()),
            restricted_guilds: try!(remove(&mut map, "restricted_guilds").and_then(|v| decode_array(v, GuildId::decode))),
            show_current_game: req!(try!(remove(&mut map, "show_current_game")).as_bool()),
            theme: try!(remove(&mut map, "theme").and_then(into_string)),
        }).map(Some)
    }
}
<commit_msg>Add CurrentUser::edit<commit_after>use std::{fmt, mem};
use super::utils::{into_map, into_string, remove};
use super::{
    CurrentUser,
    FriendSourceFlags,
    GuildContainer,
    GuildId,
    GuildInfo,
    Mention,
    RoleId,
    UserSettings,
    User,
};
use ::client::rest::GuildPagination;
use ::internal::prelude::*;
use ::utils::builder::EditProfile;
use ::utils::decode_array;

#[cfg(feature = "methods")]
use serde_json::builder::ObjectBuilder;
#[cfg(feature = "methods")]
use super::Message;
#[cfg(feature = "methods")]
use time::Timespec;
#[cfg(feature = "methods")]
use ::client::rest;

#[cfg(feature = "cache")]
use ::client::CACHE;

impl CurrentUser {
    /// Returns the formatted URL of the user's icon, if one exists.
    pub fn avatar_url(&self) -> Option<String> {
        self.avatar.as_ref().map(|av|
            format!(cdn!("/avatars/{}/{}.jpg"), self.id, av))
    }

    /// Edits the current user's profile settings.
    ///
    /// This mutates the current user in-place.
    ///
    /// Refer to `EditProfile`'s documentation for its methods.
    ///
    /// # Examples
    ///
    /// Change the avatar:
    ///
    /// ```rust,no_run
    /// use serenity::client::CACHE;
    ///
    /// let avatar = serenity::utils::read_image("./avatar.png").unwrap();
    ///
    /// CACHE.write()
    ///     .unwrap()
    ///     .user
    ///     .edit(|p| p
    ///         .avatar(Some(&avatar)));
    /// ```
    #[cfg(feature = "methods")]
    pub fn edit<F>(&mut self, f: F) -> Result<()>
        where F: FnOnce(EditProfile) -> EditProfile {
        let mut map = ObjectBuilder::new()
            .insert("avatar", Some(&self.avatar))
            .insert("username", &self.name);

        if let Some(email) = self.email.as_ref() {
            map = map.insert("email", email)
        }

        let edited = f(EditProfile(map)).0.build();

        match rest::edit_profile(edited) {
            Ok(new) => {
                mem::replace(self, new);

                Ok(())
            },
            Err(why) => Err(why),
        }
    }

    /// Gets a list of guilds that the current user is in.
    pub fn guilds(&self) -> Result<Vec<GuildInfo>> {
        rest::get_guilds(GuildPagination::After(GuildId(0)), 100)
    }
}

impl User {
    /// Returns the formatted URL of the user's icon, if one exists.
    pub fn avatar_url(&self) -> Option<String> {
        self.avatar.as_ref().map(|av|
            format!(cdn!("/avatars/{}/{}.jpg"), self.id, av))
    }

    /// Retrieves the time that this user was created at.
    #[cfg(feature = "methods")]
    #[inline]
    pub fn created_at(&self) -> Timespec {
        self.id.created_at()
    }

    /// This is an alias of [direct_message].
    ///
    /// [direct_message]: #method.direct_message
    #[cfg(feature="methods")]
    pub fn dm(&self, content: &str) -> Result<Message> {
        self.direct_message(content)
    }

    /// Send a direct message to a user. This will create or retrieve the
    /// PrivateChannel over REST if one is not already in the cache, and then
    /// send a message to it.
    #[cfg(feature="methods")]
    pub fn direct_message(&self, content: &str)
        -> Result<Message> {
        let private_channel_id = feature_cache! {{
            let finding = CACHE.read()
                .unwrap()
                .private_channels
                .values()
                .find(|ch| ch.recipient.id == self.id)
                .map(|ch| ch.id);

            if let Some(finding) = finding {
                finding
            } else {
                let map = ObjectBuilder::new()
                    .insert("recipient_id", self.id.0)
                    .build();

                try!(rest::create_private_channel(map)).id
            }
        } else {
            let map = ObjectBuilder::new()
                .insert("recipient_id", self.id.0)
                .build();

            try!(rest::create_private_channel(map)).id
        }};

        let map = ObjectBuilder::new()
            .insert("content", content)
            .insert("nonce", "")
            .insert("tts", false)
            .build();

        rest::send_message(private_channel_id.0, map)
    }

    /// Check if a user has a [`Role`]. This will retrieve the
    /// [`Guild`] from the [`Cache`] if it is available, and then check if that
    /// guild has the given [`Role`].
    ///
    /// If the [`Guild`] is not present, then the guild will be retrieved from
    /// the API and the cache will be updated with it.
    ///
    /// If there are issues with requesting the API, then `false` will be
    /// returned.
    ///
    /// Three forms of data may be passed in to the guild parameter: either a
    /// [`Guild`] itself, a [`GuildId`], or a `u64`.
    ///
    /// # Examples
    ///
    /// Check if a guild has a [`Role`] by Id:
    ///
    /// ```rust,ignore
    /// // Assumes a 'guild' and `role_id` have already been bound
    /// context.message.author.has_role(guild, role_id);
    /// ```
    ///
    /// [`Guild`]: struct.Guild.html
    /// [`GuildId`]: struct.GuildId.html
    /// [`Role`]: struct.Role.html
    /// [`Cache`]: ../ext/cache/struct.Cache.html
    pub fn has_role<G, R>(&self, guild: G, role: R) -> bool
        where G: Into<GuildContainer>, R: Into<RoleId> {
        let role_id = role.into();

        match guild.into() {
            GuildContainer::Guild(guild) => {
                guild.roles.get(&role_id).is_some()
            },
            GuildContainer::Id(guild_id) => {
                feature_cache! {{
                    let cache = CACHE.read().unwrap();

                    cache.get_role(guild_id, role_id).is_some()
                } else {
                    true
                }}
            },
        }
    }

    /// Return a [`Mention`] which will ping this user.
    ///
    /// [`Mention`]: struct.Mention.html
    pub fn mention(&self) -> Mention {
        self.id.mention()
    }
}

impl fmt::Display for User {
    /// Formats a string which will mention the user.
    // This is in the format of: `<@USER_ID>`
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(&self.id.mention(), f)
    }
}

impl UserSettings {
    #[doc(hidden)]
    pub fn decode(value: Value) -> Result<Option<UserSettings>> {
        let mut map = try!(into_map(value));

        if map.is_empty() {
            return Ok(None);
        }

        Ok(UserSettings {
            convert_emoticons: req!(try!(remove(&mut map, "convert_emoticons")).as_bool()),
            enable_tts_command: req!(try!(remove(&mut map, "enable_tts_command")).as_bool()),
            friend_source_flags: try!(remove(&mut map, "friend_source_flags").and_then(FriendSourceFlags::decode)),
            inline_attachment_media: req!(try!(remove(&mut map, "inline_attachment_media")).as_bool()),
            inline_embed_media: req!(try!(remove(&mut map, "inline_embed_media")).as_bool()),
            locale: try!(remove(&mut map, "locale").and_then(into_string)),
            message_display_compact: req!(try!(remove(&mut map, "message_display_compact")).as_bool()),
            render_embeds: req!(try!(remove(&mut map, "render_embeds")).as_bool()),
            restricted_guilds: try!(remove(&mut map, "restricted_guilds").and_then(|v| decode_array(v, GuildId::decode))),
            show_current_game: req!(try!(remove(&mut map, "show_current_game")).as_bool()),
            theme: try!(remove(&mut map, "theme").and_then(into_string)),
        }).map(Some)
    }
}
<|endoftext|>


<commit_before>
use std::str::FromStr;

#[derive(Debug,PartialEq)]
pub enum OperationSource {
    UNKNOWN(String),
    SOAP,
    REST,
    WEBSITE,
    BATCH
}

impl OperationSource {
    fn from_str(data: &str) -> OperationSource {
        match data {
            "SOAP" => OperationSource::SOAP,
            "REST" => OperationSource::REST,
            "WEBSITE" => OperationSource::WEBSITE,
            "BATCH" => OperationSource::BATCH,
            _ => OperationSource::UNKNOWN(data.to_string())
        }
    }
}

#[derive(Debug,PartialEq)]
pub enum OperationMethod {
    UNKNOWN(String),
    GET,
    PUT,
    DELETE
}

impl OperationMethod {
    fn from_str(data: &str) -> OperationMethod {
        match data {
            "GET" => OperationMethod::GET,
            "PUT" => OperationMethod::PUT,
            "DELETE" => OperationMethod::DELETE,
            _ => OperationMethod::UNKNOWN(data.to_string())
        }
    }
}

#[derive(Debug,PartialEq)]
pub enum OperationResourceType {
    UNKNOWN(String),
    OBJECT
}

impl OperationResourceType {
    fn from_str(data: &str) -> OperationResourceType {
        match data {
            "OBJECT" => OperationResourceType::OBJECT,
            _ => OperationResourceType::UNKNOWN(data.to_string())
        }
    }
}

#[derive(Debug)]
pub struct Operation {
    pub source: OperationSource,
    pub method: OperationMethod,
    pub resource_type: OperationResourceType
}

impl Default for Operation {
    fn default() -> Operation {
        Operation {
            source: OperationSource::BATCH,
            method: OperationMethod::GET,
            resource_type: OperationResourceType::OBJECT
        }
    }
}

impl FromStr for Operation {
    type Err = &'static str;

    fn from_str(data: &str) -> Result<Operation, &'static str> {
        let mut parts = data.split(".");
        let source_option = parts.next();
        let method_option = parts.next();
        let resource_type_option = parts.next();

        let source = match source_option {
            Some(s) => s,
            None => return Err("Unable to find source")
        };

        let method = match method_option {
            Some(s) => s,
            None => return Err("Unable to find match")
        };

        let resource_type = match resource_type_option {
            Some(s) => s,
            None => return Err("Unable to find resource type")
        };

        Ok(Operation {
            source: OperationSource::from_str(source),
            method: OperationMethod::from_str(method),
            resource_type: OperationResourceType::from_str(resource_type)
        })
    }
}

<commit_msg>making more idiomatic?<commit_after>
use std::str::FromStr;

#[derive(Debug,PartialEq)]
pub enum OperationSource {
    UNKNOWN(String),
    SOAP,
    REST,
    WEBSITE,
    BATCH
}

impl OperationSource {
    fn from_str(data: &str) -> OperationSource {
        match data {
            "SOAP" => OperationSource::SOAP,
            "REST" => OperationSource::REST,
            "WEBSITE" => OperationSource::WEBSITE,
            "BATCH" => OperationSource::BATCH,
            _ => OperationSource::UNKNOWN(data.to_string())
        }
    }
}

#[derive(Debug,PartialEq)]
pub enum OperationMethod {
    UNKNOWN(String),
    GET,
    PUT,
    DELETE
}

impl OperationMethod {
    fn from_str(data: &str) -> OperationMethod {
        match data {
            "GET" => OperationMethod::GET,
            "PUT" => OperationMethod::PUT,
            "DELETE" => OperationMethod::DELETE,
            _ => OperationMethod::UNKNOWN(data.to_string())
        }
    }
}

#[derive(Debug,PartialEq)]
pub enum OperationResourceType {
    UNKNOWN(String),
    OBJECT
}

impl OperationResourceType {
    fn from_str(data: &str) -> OperationResourceType {
        match data {
            "OBJECT" => OperationResourceType::OBJECT,
            _ => OperationResourceType::UNKNOWN(data.to_string())
        }
    }
}

#[derive(Debug)]
pub struct Operation {
    pub source: OperationSource,
    pub method: OperationMethod,
    pub resource_type: OperationResourceType
}

impl Default for Operation {
    fn default() -> Operation {
        Operation {
            source: OperationSource::BATCH,
            method: OperationMethod::GET,
            resource_type: OperationResourceType::OBJECT
        }
    }
}

impl FromStr for Operation {
    type Err = &'static str;

    fn from_str(data: &str) -> Result<Operation, &'static str> {
        let mut parts = data.split(".");

        let source = parts.next().ok_or("Unable to find source");
        let method = parts.next().ok_or("Unable to find match");
        let resource_type = parts.next().ok_or("Unable to find resource type");

        let all_ok = source.and(method).and(resource_type);

        match all_ok {
            Ok(_) => Ok(Operation {
                source: OperationSource::from_str(source.unwrap()),
                method: OperationMethod::from_str(method.unwrap()),
                resource_type: OperationResourceType::from_str(resource_type.unwrap())
            }),
            Err(x) => Err(x)
        }
    }
}

<|endoftext|>


<commit_before>use crate::{LateContext, LateLintPass, LintContext};
use rustc_ast as ast;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_middle::ty;
use rustc_span::sym;

declare_lint! {
    /// The `panic_fmt` lint detects `panic!("..")` with `{` or `}` in the string literal.
    ///
    /// ### Example
    ///
    /// ```rust,no_run
    /// panic!("{}");
    /// ```
    ///
    /// {{produces}}
    ///
    /// ### Explanation
    ///
    /// `panic!("{}")` panics with the message `"{}"`, as a `panic!()` invocation
    /// with a single argument does not use `format_args!()`.
    /// A future version of Rust will interpret this string as format string,
    /// which would break this.
    PANIC_FMT,
    Warn,
    "detect braces in single-argument panic!() invocations",
    report_in_external_macro
}

declare_lint_pass!(PanicFmt => [PANIC_FMT]);

impl<'tcx> LateLintPass<'tcx> for PanicFmt {
    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
        if let hir::ExprKind::Call(f, [arg]) = &expr.kind {
            if let &ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(f).kind() {
                if Some(def_id) == cx.tcx.lang_items().begin_panic_fn()
                    || Some(def_id) == cx.tcx.lang_items().panic_fn()
                {
                    check_panic(cx, f, arg);
                }
            }
        }
    }
}

fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tcx hir::Expr<'tcx>) {
    if let hir::ExprKind::Lit(lit) = &arg.kind {
        if let ast::LitKind::Str(sym, _) = lit.node {
            let s = sym.as_str();
            let open = s.find('{');
            let close = s[open.unwrap_or(0)..].find('}');
            let looks_like_placeholder = match (open, close) {
                (Some(_), Some(_)) => true,
                (Some(_), None) | (None, Some(_)) => false,
                (None, None) => return, // OK, no braces.
            };
            let expn = f.span.ctxt().outer_expn_data();
            if let Some(id) = expn.macro_def_id {
                if cx.tcx.is_diagnostic_item(sym::std_panic_macro, id)
                    || cx.tcx.is_diagnostic_item(sym::core_panic_macro, id)
                {
                    let expn = {
                        // Unwrap another level of macro expansion if this
                        // panic!() was expanded from assert!().
                        let parent = expn.call_site.ctxt().outer_expn_data();
                        if parent
                            .macro_def_id
                            .map_or(false, |id| cx.tcx.is_diagnostic_item(sym::assert_macro, id))
                        {
                            parent
                        } else {
                            expn
                        }
                    };
                    if looks_like_placeholder {
                        cx.struct_span_lint(PANIC_FMT, arg.span.source_callsite(), |lint| {
                            let mut l = lint.build("Panic message contains an unused formatting placeholder");
                            l.note("This message is not used as a format string when given without arguments, but will be in a future Rust version");
                            if expn.call_site.contains(arg.span) {
                                l.span_suggestion(
                                    arg.span.shrink_to_hi(),
                                    "add the missing argument(s)",
                                    ", argument".into(),
                                    Applicability::HasPlaceholders,
                                );
                                l.span_suggestion(
                                    arg.span.shrink_to_lo(),
                                    "or add a \"{}\" format string to use the message literally",
                                    "\"{}\", ".into(),
                                    Applicability::MaybeIncorrect,
                                );
                            }
                            l.emit();
                        });
                    } else {
                        cx.struct_span_lint(PANIC_FMT, expn.call_site, |lint| {
                            let mut l = lint.build("Panic message contains a brace");
                            l.note("This message is not used as a format string, but will be in a future Rust version");
                            if expn.call_site.contains(arg.span) {
                                l.span_suggestion(
                                    arg.span.shrink_to_lo(),
                                    "add a \"{}\" format string to use the message literally",
                                    "\"{}\", ".into(),
                                    Applicability::MachineApplicable,
                                );
                            }
                            l.emit();
                        });
                    }
                }
            }
        }
    }
}
<commit_msg>Don't see `{{}}` as placeholder in panic_fmt lint.<commit_after>use crate::{LateContext, LateLintPass, LintContext};
use rustc_ast as ast;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_middle::ty;
use rustc_span::sym;

declare_lint! {
    /// The `panic_fmt` lint detects `panic!("..")` with `{` or `}` in the string literal.
    ///
    /// ### Example
    ///
    /// ```rust,no_run
    /// panic!("{}");
    /// ```
    ///
    /// {{produces}}
    ///
    /// ### Explanation
    ///
    /// `panic!("{}")` panics with the message `"{}"`, as a `panic!()` invocation
    /// with a single argument does not use `format_args!()`.
    /// A future version of Rust will interpret this string as format string,
    /// which would break this.
    PANIC_FMT,
    Warn,
    "detect braces in single-argument panic!() invocations",
    report_in_external_macro
}

declare_lint_pass!(PanicFmt => [PANIC_FMT]);

impl<'tcx> LateLintPass<'tcx> for PanicFmt {
    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
        if let hir::ExprKind::Call(f, [arg]) = &expr.kind {
            if let &ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(f).kind() {
                if Some(def_id) == cx.tcx.lang_items().begin_panic_fn()
                    || Some(def_id) == cx.tcx.lang_items().panic_fn()
                {
                    check_panic(cx, f, arg);
                }
            }
        }
    }
}

fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tcx hir::Expr<'tcx>) {
    if let hir::ExprKind::Lit(lit) = &arg.kind {
        if let ast::LitKind::Str(sym, _) = lit.node {
            let expn = f.span.ctxt().outer_expn_data();
            if let Some(id) = expn.macro_def_id {
                if cx.tcx.is_diagnostic_item(sym::std_panic_macro, id)
                    || cx.tcx.is_diagnostic_item(sym::core_panic_macro, id)
                {
                    let s = sym.as_str();
                    if !s.contains(&['{', '}'][..]) {
                        return;
                    }
                    let s = s.replace("{{", "").replace("}}", "");
                    let looks_like_placeholder =
                        s.find('{').map_or(false, |i| s[i + 1..].contains('}'));
                    let expn = {
                        // Unwrap another level of macro expansion if this
                        // panic!() was expanded from assert!().
                        let parent = expn.call_site.ctxt().outer_expn_data();
                        if parent
                            .macro_def_id
                            .map_or(false, |id| cx.tcx.is_diagnostic_item(sym::assert_macro, id))
                        {
                            parent
                        } else {
                            expn
                        }
                    };
                    if looks_like_placeholder {
                        cx.struct_span_lint(PANIC_FMT, arg.span.source_callsite(), |lint| {
                            let mut l = lint.build("Panic message contains an unused formatting placeholder");
                            l.note("This message is not used as a format string when given without arguments, but will be in a future Rust version");
                            if expn.call_site.contains(arg.span) {
                                l.span_suggestion(
                                    arg.span.shrink_to_hi(),
                                    "add the missing argument(s)",
                                    ", argument".into(),
                                    Applicability::HasPlaceholders,
                                );
                                l.span_suggestion(
                                    arg.span.shrink_to_lo(),
                                    "or add a \"{}\" format string to use the message literally",
                                    "\"{}\", ".into(),
                                    Applicability::MaybeIncorrect,
                                );
                            }
                            l.emit();
                        });
                    } else {
                        cx.struct_span_lint(PANIC_FMT, expn.call_site, |lint| {
                            let mut l = lint.build("Panic message contains a brace");
                            l.note("This message is not used as a format string, but will be in a future Rust version");
                            if expn.call_site.contains(arg.span) {
                                l.span_suggestion(
                                    arg.span.shrink_to_lo(),
                                    "add a \"{}\" format string to use the message literally",
                                    "\"{}\", ".into(),
                                    Applicability::MachineApplicable,
                                );
                            }
                            l.emit();
                        });
                    }
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::collections::BTreeMap;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::marker::PhantomData;

use util::tree::GornTree;

mod from_str;
// mod relabel;
pub mod negra;

/// Variable or terminal symbol in an MCFG.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Hash)]
pub enum VarT<T> {
    /// `Var(i, j)` represents the `j`th component of the `i`th successor.
    /// Indexing starts from `0`.
    Var(usize, usize),
    T(T),
}

/// Composition function in an MCFG.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Hash)]
pub struct Composition<T> {
    pub composition: Vec<Vec<VarT<T>>>,
}

impl<T> From<Vec<Vec<VarT<T>>>> for Composition<T> {
    fn from(encapsulated_value: Vec<Vec<VarT<T>>>) -> Self {
        Composition { composition: encapsulated_value }
    }
}

/// Rule of a weighted MCFG.
#[derive(Debug, PartialOrd, Ord, Clone)]
pub struct PMCFGRule<N, T, W> {
    pub head: N,
    pub tail: Vec<N>,
    pub composition: Composition<T>,
    pub weight: W,
}

impl<N, T, W> PMCFGRule<N, T, W>
    where T: Clone,
          W: Clone,
{
    pub fn map_nonterminals<F, M>(&self, f: F) -> PMCFGRule<M, T, W>
        where F: Fn(&N) -> M,
    {
        PMCFGRule {
            head: f(&self.head),
            tail: self.tail.iter().map(f).collect(),
            composition: self.composition.clone(),
            weight: self.weight.clone()
        }
    }
}

/// A weighted MCFG.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
pub struct PMCFG<N, T, W> {
    pub _dummy: PhantomData<T>,
    pub initial: Vec<N>,
    pub rules: Vec<PMCFGRule<N, T, W>>,
}

impl<N: Hash, T: Hash, W> Hash for PMCFGRule<N, T, W> {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.head.hash(state);
        self.tail.hash(state);
        self.composition.hash(state);
    }
}

impl<N: PartialEq, T: PartialEq, W> PartialEq for PMCFGRule<N, T, W> {
    fn eq(&self, other: &Self) -> bool {
        self.head == other.head && self.tail == other.tail && self.composition == other.composition
    }
}

impl<N: Eq, T: Eq, W> Eq for PMCFGRule<N, T, W> {}

impl<T: fmt::Display> fmt::Display for Composition<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut buffer = "".to_string();

        let mut iter0 = self.composition.iter().peekable();
        let mut iter1;

        buffer.push_str("[");
        while let Some(proj) = iter0.next() {
            iter1 = proj.into_iter().peekable();
            buffer.push_str("[");
            while let Some(vart) = iter1.next() {
                buffer.push_str(format!("{}", vart).as_str());
                if iter1.peek().is_some() {
                    buffer.push_str(", ");
                }
            }
            buffer.push_str("]");
            if iter0.peek().is_some() {
                buffer.push_str(", ");
            }
        }
        buffer.push_str("]");

        write!(f, "{}", buffer)
    }
}

impl<T: fmt::Display> fmt::Display for VarT<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            VarT::Var(i, j) => {
                write!(f, "Var {} {}", i, j)
            },
            VarT::T(ref x) => {
                write!(f, "T \"{}\"", x)
            },
        }
    }
}

impl<N: fmt::Display, T: fmt::Display, W: fmt::Display> fmt::Display for PMCFGRule<N, T, W> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
           let mut buffer = "".to_string();

           let mut iter = self.tail.iter().peekable();

           buffer.push_str("(");
           while let Some(nt) = iter.next() {
               buffer.push_str(format!("\"{}\"", nt).as_str());
               if iter.peek().is_some() {
                   buffer.push_str(", ");
               }
           }
           buffer.push_str(")");

        write!(f, "\"{}\" → {} {}  # {}", self.head, self.composition, buffer, self.weight)
        // write!(f, "\"{}\" → {}", self.head, self.composition)
    }
}

impl<N: fmt::Display, T: fmt::Display, W: fmt::Display> fmt::Display for PMCFG<N, T, W> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut buffer = "".to_string();

        let mut iter = self.initial.iter().peekable();

        buffer.push_str("initial: [");
        while let Some(nt) = iter.next() {
            buffer.push_str(format!("\"{}\"", nt).as_str());
            if iter.peek().is_some() {
                buffer.push_str(", ");
            }
        }
        buffer.push_str("]\n\n");

        for r in &self.rules {
            buffer.push_str(format!("{}\n", r).as_str());
        }

        write!(f, "{}", buffer)
    }
}

pub fn evaluate<T: Clone + fmt::Debug>(term_map: &GornTree<Composition<T>>)
        -> Composition<T>
{
    evaluate_pos(term_map, vec![])
}

pub fn evaluate_pos<T>(term_map: &GornTree<Composition<T>>, address: Vec<usize>)
        -> Composition<T>
    where T: Clone + fmt::Debug,
{
    let unexpanded_composition = &term_map.get(&address).unwrap().composition;
    let mut expanded_nonterminals: BTreeMap<_, Vec<Vec<VarT<T>>>> = BTreeMap::new();
    let mut expanded_composition = Vec::new();

    for component in unexpanded_composition {
        let mut expanded_component = Vec::new();

        for variable in component {
            match variable {
                &VarT::Var(num_nonter, num_compon) => {
                    if !expanded_nonterminals.contains_key(&num_nonter) {
                        let mut child_address = address.clone();
                        child_address.push(num_nonter);
                        let nonter_compos = evaluate_pos(term_map, child_address).composition;
                        expanded_nonterminals.insert(num_nonter, nonter_compos);
                    }

                    let nonter_compos = expanded_nonterminals.get(&num_nonter).unwrap();

                    if let Some(compon) = nonter_compos.get(num_compon) {
                        for terminal in compon {
                            expanded_component.push(terminal.clone());
                        }
                    } else {
                        panic!("{:?}: use of {}-th component of nonterminal {} that has only {} components!",
                               unexpanded_composition, num_compon, num_nonter, nonter_compos.len());
                    }

                },
                &VarT::T(ref terminal) => {
                    expanded_component.push(VarT::T(terminal.clone()));
                },
            }
        }

        expanded_composition.push(expanded_component);
    }

    Composition::from(expanded_composition)
}

pub fn to_term<H, T, W>(tree_map: &GornTree<PMCFGRule<H, T, W>>)
        -> (GornTree<Composition<T>>, GornTree<H>)
    where H: Clone, T: Clone,
{
    let mut term_map = GornTree::new();
    let mut head_map = GornTree::new();

    for (address, &PMCFGRule { ref head, tail: _, ref composition, weight: _ }) in tree_map {
        term_map.insert(address.clone(), composition.clone());
        head_map.insert(address.clone(), head.clone());
    }

    (term_map, head_map)
}

#[cfg(test)]
mod tests {
    use super::*;
    use self::VarT::{Var, T};
    use std::str::FromStr;

    pub fn example_tree_map() -> GornTree<PMCFGRule<String, char, usize>> {
        let mut tree_map = GornTree::new();

        tree_map.insert(vec![], PMCFGRule::from_str(
            "S -> [[Var 0 0, Var 1 0, Var 0 1, Var 1 1]] (A, B) # 1"
        ).unwrap());
        tree_map.insert(vec![0], PMCFGRule::from_str(
            "A -> [[Var 1 0, Var 0 0], [Var 2 0, Var 0 1]] (A, a, c) # 1"
        ).unwrap());
        tree_map.insert(vec![0, 0], PMCFGRule::from_str(
            "A -> [[Var 1 0, Var 0 0], [Var 2 0, Var 0 1]] (A, a, c) # 1"
        ).unwrap());
        tree_map.insert(vec![0, 0, 0], PMCFGRule::from_str(
            "A -> [[], []] () # 1"
        ).unwrap());
        tree_map.insert(vec![0, 0, 1], PMCFGRule::from_str(
            "a -> [[T a]] () # 1"
        ).unwrap());
        tree_map.insert(vec![0, 0, 2], PMCFGRule::from_str(
            "c -> [[T c]] () # 1"
        ).unwrap());
        tree_map.insert(vec![0, 1], PMCFGRule::from_str(
            "a -> [[T a]] () # 1"
        ).unwrap());
        tree_map.insert(vec![0, 2], PMCFGRule::from_str(
            "c -> [[T c]] () # 1"
        ).unwrap());
        tree_map.insert(vec![1], PMCFGRule::from_str(
            "B -> [[Var 1 0, Var 0 0], [Var 2 0, Var 0 1]] (B, b, d) # 1"
        ).unwrap());
        tree_map.insert(vec![1, 0], PMCFGRule::from_str(
            "B -> [[], []] () # 1"
        ).unwrap());
        tree_map.insert(vec![1, 1], PMCFGRule::from_str(
            "b -> [[T b]] () # 1"
        ).unwrap());
        tree_map.insert(vec![1, 2], PMCFGRule::from_str(
            "d -> [[T d]] () # 1"
        ).unwrap());

        tree_map
    }

    #[test]
    fn test_evaluate() {
        let tree_map = example_tree_map();
        let mut term_map = GornTree::new();

        for (address, PMCFGRule { head: _, tail: _, composition, weight: _ }) in tree_map {
            term_map.insert(address, composition);
        }

        let expanded_compos = Composition::from(vec![
            vec![T('a'), T('a'), T('b'), T('c'), T('c'), T('d')]
        ]);

        assert_eq!(expanded_compos, evaluate(&term_map));
    }

    #[test]
    #[should_panic(expected =
        "[[Var(0, 0), Var(0, 1)]]: use of 1-th component of nonterminal 0 that has only 1 components!"
    )]
    fn test_evaluate_invalid_composition() {
        let mut term_map = GornTree::new();
        term_map.insert(vec![], Composition::from(vec![
            vec![Var(0, 0), Var(0, 1)]
        ]));
        term_map.insert(vec![0], Composition::from(vec![
            vec![T('a')]
        ]));

        evaluate(&term_map);
    }

    #[test]
    fn test_to_term() {
        let mut tree_map: GornTree<PMCFGRule<String, char, usize>> = GornTree::new();

        tree_map.insert(vec![], PMCFGRule::from_str(
            "A -> [[Var 0 0, T a, Var 0 1, T b]] (B) # 1"
        ).unwrap());
        tree_map.insert(vec![0], PMCFGRule::from_str(
            "B -> [[Var 1 0], [T c]] (C) # 1"
        ).unwrap());
        tree_map.insert(vec![0, 1], PMCFGRule::from_str(
            "C -> [[], []] () # 1"
        ).unwrap());

        let mut term_map = GornTree::new();
        term_map.insert(vec![], Composition::from(vec![
            vec![Var(0, 0), T('a'), Var(0, 1), T('b')]
        ]));
        term_map.insert(vec![0], Composition::from(vec![
            vec![Var(1, 0)],
            vec![T('c')]
        ]));
        term_map.insert(vec![0, 1], Composition::from(vec![
            vec![],
            vec![]
        ]));

        let mut head_map = GornTree::new();
        head_map.insert(vec![], String::from("A"));
        head_map.insert(vec![0], String::from("B"));
        head_map.insert(vec![0, 1], String::from("C"));

        assert_eq!((term_map, head_map), to_term(&tree_map));
    }
}
<commit_msg>src/pmcfg/mod.rs: added to_term() test<commit_after>use std::collections::BTreeMap;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::marker::PhantomData;

use util::tree::GornTree;

mod from_str;
// mod relabel;
pub mod negra;

/// Variable or terminal symbol in an MCFG.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Hash)]
pub enum VarT<T> {
    /// `Var(i, j)` represents the `j`th component of the `i`th successor.
    /// Indexing starts from `0`.
    Var(usize, usize),
    T(T),
}

/// Composition function in an MCFG.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Hash)]
pub struct Composition<T> {
    pub composition: Vec<Vec<VarT<T>>>,
}

impl<T> From<Vec<Vec<VarT<T>>>> for Composition<T> {
    fn from(encapsulated_value: Vec<Vec<VarT<T>>>) -> Self {
        Composition { composition: encapsulated_value }
    }
}

/// Rule of a weighted MCFG.
#[derive(Debug, PartialOrd, Ord, Clone)]
pub struct PMCFGRule<N, T, W> {
    pub head: N,
    pub tail: Vec<N>,
    pub composition: Composition<T>,
    pub weight: W,
}

impl<N, T, W> PMCFGRule<N, T, W>
    where T: Clone,
          W: Clone,
{
    pub fn map_nonterminals<F, M>(&self, f: F) -> PMCFGRule<M, T, W>
        where F: Fn(&N) -> M,
    {
        PMCFGRule {
            head: f(&self.head),
            tail: self.tail.iter().map(f).collect(),
            composition: self.composition.clone(),
            weight: self.weight.clone()
        }
    }
}

/// A weighted MCFG.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
pub struct PMCFG<N, T, W> {
    pub _dummy: PhantomData<T>,
    pub initial: Vec<N>,
    pub rules: Vec<PMCFGRule<N, T, W>>,
}

impl<N: Hash, T: Hash, W> Hash for PMCFGRule<N, T, W> {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.head.hash(state);
        self.tail.hash(state);
        self.composition.hash(state);
    }
}

impl<N: PartialEq, T: PartialEq, W> PartialEq for PMCFGRule<N, T, W> {
    fn eq(&self, other: &Self) -> bool {
        self.head == other.head && self.tail == other.tail && self.composition == other.composition
    }
}

impl<N: Eq, T: Eq, W> Eq for PMCFGRule<N, T, W> {}

impl<T: fmt::Display> fmt::Display for Composition<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut buffer = "".to_string();

        let mut iter0 = self.composition.iter().peekable();
        let mut iter1;

        buffer.push_str("[");
        while let Some(proj) = iter0.next() {
            iter1 = proj.into_iter().peekable();
            buffer.push_str("[");
            while let Some(vart) = iter1.next() {
                buffer.push_str(format!("{}", vart).as_str());
                if iter1.peek().is_some() {
                    buffer.push_str(", ");
                }
            }
            buffer.push_str("]");
            if iter0.peek().is_some() {
                buffer.push_str(", ");
            }
        }
        buffer.push_str("]");

        write!(f, "{}", buffer)
    }
}

impl<T: fmt::Display> fmt::Display for VarT<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            VarT::Var(i, j) => {
                write!(f, "Var {} {}", i, j)
            },
            VarT::T(ref x) => {
                write!(f, "T \"{}\"", x)
            },
        }
    }
}

impl<N: fmt::Display, T: fmt::Display, W: fmt::Display> fmt::Display for PMCFGRule<N, T, W> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
           let mut buffer = "".to_string();

           let mut iter = self.tail.iter().peekable();

           buffer.push_str("(");
           while let Some(nt) = iter.next() {
               buffer.push_str(format!("\"{}\"", nt).as_str());
               if iter.peek().is_some() {
                   buffer.push_str(", ");
               }
           }
           buffer.push_str(")");

        write!(f, "\"{}\" → {} {}  # {}", self.head, self.composition, buffer, self.weight)
        // write!(f, "\"{}\" → {}", self.head, self.composition)
    }
}

impl<N: fmt::Display, T: fmt::Display, W: fmt::Display> fmt::Display for PMCFG<N, T, W> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut buffer = "".to_string();

        let mut iter = self.initial.iter().peekable();

        buffer.push_str("initial: [");
        while let Some(nt) = iter.next() {
            buffer.push_str(format!("\"{}\"", nt).as_str());
            if iter.peek().is_some() {
                buffer.push_str(", ");
            }
        }
        buffer.push_str("]\n\n");

        for r in &self.rules {
            buffer.push_str(format!("{}\n", r).as_str());
        }

        write!(f, "{}", buffer)
    }
}

pub fn evaluate<T: Clone + fmt::Debug>(term_map: &GornTree<Composition<T>>)
        -> Composition<T>
{
    evaluate_pos(term_map, vec![])
}

pub fn evaluate_pos<T>(term_map: &GornTree<Composition<T>>, address: Vec<usize>)
        -> Composition<T>
    where T: Clone + fmt::Debug,
{
    let unexpanded_composition = &term_map.get(&address).unwrap().composition;
    let mut expanded_nonterminals: BTreeMap<_, Vec<Vec<VarT<T>>>> = BTreeMap::new();
    let mut expanded_composition = Vec::new();

    for component in unexpanded_composition {
        let mut expanded_component = Vec::new();

        for variable in component {
            match variable {
                &VarT::Var(num_nonter, num_compon) => {
                    if !expanded_nonterminals.contains_key(&num_nonter) {
                        let mut child_address = address.clone();
                        child_address.push(num_nonter);
                        let nonter_compos = evaluate_pos(term_map, child_address).composition;
                        expanded_nonterminals.insert(num_nonter, nonter_compos);
                    }

                    let nonter_compos = expanded_nonterminals.get(&num_nonter).unwrap();

                    if let Some(compon) = nonter_compos.get(num_compon) {
                        for terminal in compon {
                            expanded_component.push(terminal.clone());
                        }
                    } else {
                        panic!("{:?}: use of {}-th component of nonterminal {} that has only {} components!",
                               unexpanded_composition, num_compon, num_nonter, nonter_compos.len());
                    }

                },
                &VarT::T(ref terminal) => {
                    expanded_component.push(VarT::T(terminal.clone()));
                },
            }
        }

        expanded_composition.push(expanded_component);
    }

    Composition::from(expanded_composition)
}

pub fn to_term<H, T, W>(tree_map: &GornTree<PMCFGRule<H, T, W>>)
        -> (GornTree<Composition<T>>, GornTree<H>)
    where H: Clone, T: Clone,
{
    let mut term_map = GornTree::new();
    let mut head_map = GornTree::new();

    for (address, &PMCFGRule { ref head, tail: _, ref composition, weight: _ }) in tree_map {
        term_map.insert(address.clone(), composition.clone());
        head_map.insert(address.clone(), head.clone());
    }

    (term_map, head_map)
}

#[cfg(test)]
mod tests {
    use super::*;
    use self::VarT::{Var, T};
    use std::str::FromStr;

    pub fn example_tree_map() -> GornTree<PMCFGRule<String, char, usize>> {
        let mut tree_map = GornTree::new();

        tree_map.insert(vec![], PMCFGRule::from_str(
            "S -> [[Var 0 0, Var 1 0, Var 0 1, Var 1 1]] (A, B) # 1"
        ).unwrap());
        tree_map.insert(vec![0], PMCFGRule::from_str(
            "A -> [[Var 1 0, Var 0 0], [Var 2 0, Var 0 1]] (A, a, c) # 1"
        ).unwrap());
        tree_map.insert(vec![0, 0], PMCFGRule::from_str(
            "A -> [[Var 1 0, Var 0 0], [Var 2 0, Var 0 1]] (A, a, c) # 1"
        ).unwrap());
        tree_map.insert(vec![0, 0, 0], PMCFGRule::from_str(
            "A -> [[], []] () # 1"
        ).unwrap());
        tree_map.insert(vec![0, 0, 1], PMCFGRule::from_str(
            "a -> [[T a]] () # 1"
        ).unwrap());
        tree_map.insert(vec![0, 0, 2], PMCFGRule::from_str(
            "c -> [[T c]] () # 1"
        ).unwrap());
        tree_map.insert(vec![0, 1], PMCFGRule::from_str(
            "a -> [[T a]] () # 1"
        ).unwrap());
        tree_map.insert(vec![0, 2], PMCFGRule::from_str(
            "c -> [[T c]] () # 1"
        ).unwrap());
        tree_map.insert(vec![1], PMCFGRule::from_str(
            "B -> [[Var 1 0, Var 0 0], [Var 2 0, Var 0 1]] (B, b, d) # 1"
        ).unwrap());
        tree_map.insert(vec![1, 0], PMCFGRule::from_str(
            "B -> [[], []] () # 1"
        ).unwrap());
        tree_map.insert(vec![1, 1], PMCFGRule::from_str(
            "b -> [[T b]] () # 1"
        ).unwrap());
        tree_map.insert(vec![1, 2], PMCFGRule::from_str(
            "d -> [[T d]] () # 1"
        ).unwrap());

        tree_map
    }

    #[test]
    fn test_evaluate() {
        let tree_map = example_tree_map();
        let mut term_map = GornTree::new();

        for (address, PMCFGRule { head: _, tail: _, composition, weight: _ }) in tree_map {
            term_map.insert(address, composition);
        }

        let expanded_compos = Composition::from(vec![
            vec![T('a'), T('a'), T('b'), T('c'), T('c'), T('d')]
        ]);

        assert_eq!(expanded_compos, evaluate(&term_map));
    }

    #[test]
    #[should_panic(expected =
        "[[Var(0, 0), Var(0, 1)]]: use of 1-th component of nonterminal 0 that has only 1 components!"
    )]
    fn test_evaluate_invalid_composition() {
        let mut term_map = GornTree::new();
        term_map.insert(vec![], Composition::from(vec![
            vec![Var(0, 0), Var(0, 1)]
        ]));
        term_map.insert(vec![0], Composition::from(vec![
            vec![T('a')]
        ]));

        evaluate(&term_map);
    }

    #[test]
    fn test_to_term() {
        let mut tree_map: GornTree<PMCFGRule<String, char, usize>> = GornTree::new();

        tree_map.insert(vec![], PMCFGRule::from_str(
            "A -> [[Var 0 0, T a, Var 0 1, T b]] (B) # 1"
        ).unwrap());
        tree_map.insert(vec![0], PMCFGRule::from_str(
            "B -> [[Var 1 0], [T c]] (C) # 1"
        ).unwrap());
        tree_map.insert(vec![0, 1], PMCFGRule::from_str(
            "C -> [[], []] () # 1"
        ).unwrap());

        let mut term_map = GornTree::new();
        term_map.insert(vec![], Composition::from(vec![
            vec![Var(0, 0), T('a'), Var(0, 1), T('b')]
        ]));
        term_map.insert(vec![0], Composition::from(vec![
            vec![Var(1, 0)],
            vec![T('c')]
        ]));
        term_map.insert(vec![0, 1], Composition::from(vec![
            vec![],
            vec![]
        ]));

        let mut head_map = GornTree::new();
        head_map.insert(vec![], String::from("A"));
        head_map.insert(vec![0], String::from("B"));
        head_map.insert(vec![0, 1], String::from("C"));

        assert_eq!((term_map, head_map), to_term(&tree_map));
    }

    #[test]
    fn test_to_term_inverse() {
        let tree_map = example_tree_map();
        let (term_map, head_map) = to_term(&tree_map);
        let mut reconstructed_tree_map = GornTree::new();

        for (address, composition) in term_map {
            let head = head_map.get(&address).unwrap().clone();
            reconstructed_tree_map.insert(address, PMCFGRule {
                head, tail: vec![], composition, weight: 0
            });
        }

        for (address, rule) in tree_map {
            let PMCFGRule { head: ref orig_head, tail: _, composition: ref orig_composition, weight: _ } =
                rule;
            let &PMCFGRule { ref head, tail: _, ref composition, weight: _ } =
                reconstructed_tree_map.get(&address).unwrap();
            assert_eq!(orig_head, head);
            assert_eq!(orig_composition, composition);
        }
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate glium;
extern crate sdl2;

#[macro_use]
extern crate clap;

pub mod gpu;
pub mod controller;
mod sound;

#[allow(dead_code)]
mod debugger;

extern crate gb;

use std::fs::{File, OpenOptions};
use std::io::{Read, Write, Seek, SeekFrom};

use gb::{Emulator, Cpu};

use self::controller::{Event, Controller};
use self::debugger::Debugger;

fn open_save_file(rom_name: &str) -> Result<File, String> {
    let mut v: Vec<&str> = rom_name.split('.').collect();
    // If the file does not end with .gb or .gbc we might be reading garbage,
    // let's bail.
    let ext = v.pop().unwrap();
    if ext != "gb" && ext != "gbc" {
        return Err(format!("Invalid ROM name: '{}' filename must end with '.gb'.", rom_name));
    }

    // Replace extension with "sav"
    v.push("srm");

    let save_name = v.join(".");

    Ok(OpenOptions::new()
        .read(true)
        .write(true)
        .create(true)
        .open(save_name)
        .unwrap())
}

fn open_rom(rom_name: &str) -> Result<File, String> {
    let f = File::open(rom_name);
    f.map_err(|_| format!("Error: ROM '{}' not found.", rom_name))
}

// Similar to try! but for the main function
macro_rules! bail {
    ($expr : expr) => {
        {
            let result = $expr;
            if result.is_err() {
                println!("{}", $expr.unwrap_err());
                return;
            }

            result.unwrap()
        }
    }
}

fn parse_string_at(mut address: u16, cpu: &mut Cpu) -> String {
    let mut result = String::from("");
    let mut c = 0xFF;
    while c != 0 {
        c = cpu.deref(address);
        address += 1;
        if c != 0 {
            result.push(c as char);
        }
    }
    result
}

pub fn main() {
    let matches = clap_app!(gbrust =>
        (version: "0.1b")
        (author: "Agi Sferro <agi.novanta@gmail.com>")
        (about: "Yet another DMG emulator written in Rust.")
        (@arg ROM: +required "Selects the ROM to run.")
        (@arg mag: -m --magnification +takes_value "Number of times the screen should be magnified. Default is '3'.")
        (@arg debug: -d --debug "Starts in debug mode.")
        (@arg headless: -H --headless "Run in headless mode. For integration tests.")
        (@arg timeout: -t --timeout +takes_value "Timeout when running headless, in millions of cycles. Default 100")
        (@arg string_addr: -s --result +takes_value
            "The emulator will print a null-terminated string preset at this address. For integration tests.")
        (@arg commands: -C --commands +takes_value
            "Semicolon separated commands to run after debugger starts. Assumes --debug.")
    ).get_matches();

    let rom_name = matches.value_of("ROM").unwrap();
    let mut save_file = bail!(open_save_file(&rom_name));

    let headless = matches.occurrences_of("headless") > 0;
    let timeout = matches.value_of("timeout")
        .map(|t| t.parse::<usize>())
        .unwrap_or(Ok(100))
        .unwrap();
    let mag = value_t!(matches.value_of("mag"), u32)
        .unwrap_or(3);

    let mut controller = if !headless {
        Some(Controller::new(gb::SCREEN_X as u32 * mag,
                             gb::SCREEN_Y as u32 * mag))
    } else {
        None
    };

    let mut emulator;
    {
        let mut rom_bytes = vec![];
        let mut rom = bail!(open_rom(&rom_name));
        bail!(rom.read_to_end(&mut rom_bytes));

        emulator = Emulator::from_data(&rom_bytes, 44100.0).unwrap();

        // Load save file in ram
        // we don't care if we can't fill the whole buffer, it just
        // means that we don't have a save file
        let _ = save_file.read_exact(emulator.cpu.handler_holder.ram());
    }

    let mut debugger = Debugger::new();
    let commands: Vec<&str> = matches.value_of("commands")
        .map(|cc| cc.split(';').map(|c| c.trim()).collect())
        .unwrap_or(vec![]);

    for c in &commands {
        bail!(debugger.exec(c, &mut emulator));
    }

    if matches.occurrences_of("debug") > 0 || commands.len() > 0 {
        debugger.breakpoint(&mut emulator);
    }

    let mut natural_speed = true;
    let mut counter = timeout * 1000000;

    while !headless || counter > 0 {
        debugger.check_breakpoints(&mut emulator);

        emulator.cpu.next_instruction();

        if let Some(ref mut c) = controller {
            if emulator.cpu.handler_holder.should_refresh() {
                match c.check_events(&mut emulator) {
                    Event::Quit => break,
                    Event::Break => {
                        debugger.breakpoint(&mut emulator);
                    },
                    Event::ToggleSpeed => { natural_speed = !natural_speed },
                    Event::Continue => {},
                }

                c.refresh(&mut emulator);
            }
        }

        debugger.next_instruction(&mut emulator);

        counter -= 1;
    }

    if let Some(string_address) = matches.value_of("string_addr") {
        let address = bail!(u16::from_str_radix(string_address, 16)
            .map_err(|_| format!("Could not parse address '{}'. \
Please use format XXXX, where X is 0-F.", string_address)));
        print!("{}", parse_string_at(address, &mut emulator.cpu));
    }

    bail!(save_file.seek(SeekFrom::Start(0)));
    bail!(save_file.write_all(emulator.cpu.handler_holder.ram()));
}
<commit_msg>Refactor: Move config parsing outside main.<commit_after>#[macro_use]
extern crate glium;
extern crate sdl2;

#[macro_use]
extern crate clap;

pub mod gpu;
pub mod controller;
mod sound;

#[allow(dead_code)]
mod debugger;

extern crate gb;

use std::fs::{File, OpenOptions};
use std::io::{Read, Write, Seek, SeekFrom};

use gb::{Emulator, Cpu};

use self::controller::{Event, Controller};
use self::debugger::Debugger;

fn open_save_file(rom_name: &str) -> Result<File, String> {
    let mut v: Vec<&str> = rom_name.split('.').collect();
    // If the file does not end with .gb or .gbc we might be reading garbage,
    // let's bail.
    let ext = v.pop().unwrap();
    if ext != "gb" && ext != "gbc" {
        return Err(format!("Invalid ROM name: '{}' filename must end with '.gb'.", rom_name));
    }

    // Replace extension with "sav"
    v.push("srm");

    let save_name = v.join(".");

    Ok(OpenOptions::new()
        .read(true)
        .write(true)
        .create(true)
        .open(save_name)
        .unwrap())
}

fn open_rom(rom_name: &str) -> Result<File, String> {
    let f = File::open(rom_name);
    f.map_err(|_| format!("Error: ROM '{}' not found.", rom_name))
}

// Similar to try! but for the main function
macro_rules! bail {
    ($expr : expr) => {
        {
            let result = $expr;
            if result.is_err() {
                println!("{}", result.unwrap_err());
                return;
            }

            result.unwrap()
        }
    }
}

fn parse_string_at(mut address: u16, cpu: &mut Cpu) -> String {
    let mut result = String::from("");
    let mut c = 0xFF;
    while c != 0 {
        c = cpu.deref(address);
        address += 1;
        if c != 0 {
            result.push(c as char);
        }
    }
    result
}

const DEFAULT_MAG: u32 = 3;

#[derive(Debug)]
struct Config {
    rom_name: String,
    is_headless: bool,
    timeout: usize,
    mag: u32,
    commands: Vec<String>,
    integ_tests_string_addr: Option<u16>,
    is_debug: bool,
}

impl Config {
    fn from_clap(matches: clap::ArgMatches) -> Result<Config, String> {
        let timeout = matches.value_of("timeout")
            .map(|t| t.parse::<usize>())
            .unwrap_or(Ok(100))
            .unwrap();
        let mag = value_t!(matches.value_of("mag"), u32)
            .unwrap_or(DEFAULT_MAG);
        let commands: Vec<String> = matches.value_of("commands")
            .map(|cc| cc.split(';').map(|c| c.trim().to_string()).collect())
            .unwrap_or(vec![]);
        let string_addr = if let Some(addr) = matches.value_of("string_addr") {
            Some(u16::from_str_radix(addr, 16)
                .map_err(|_| format!("Could not parse address '{}'. \
    Please use format XXXX, where X is 0-F.", addr))?)
        } else {
            None
        };

        Ok(Config {
            rom_name: matches.value_of("ROM").unwrap().to_string(),
            is_headless: matches.occurrences_of("headless") > 0,
            is_debug: matches.occurrences_of("debug") > 0,
            timeout: timeout,
            mag: mag,
            commands: commands,
            integ_tests_string_addr: string_addr,
        })
    }
}

pub fn main() {
    let matches = clap_app!(gbrust =>
        (version: "0.1b")
        (author: "Agi Sferro <agi.novanta@gmail.com>")
        (about: "Yet another DMG emulator written in Rust.")
        (@arg ROM: +required "Selects the ROM to run.")
        (@arg mag: -m --magnification +takes_value "Number of times the screen should be magnified. Default is '3'.")
        (@arg debug: -d --debug "Starts in debug mode.")
        (@arg headless: -H --headless "Run in headless mode. For integration tests.")
        (@arg timeout: -t --timeout +takes_value "Timeout when running headless, in millions of cycles. Default 100")
        (@arg string_addr: -s --result +takes_value
            "The emulator will print a null-terminated string preset at this address. For integration tests.")
        (@arg commands: -C --commands +takes_value
            "Semicolon separated commands to run after debugger starts. Assumes --debug.")
    ).get_matches();

    let config = bail!(Config::from_clap(matches));

    let mut save_file = bail!(open_save_file(&config.rom_name));

    let mut controller = if !config.is_headless {
        Some(Controller::new(gb::SCREEN_X as u32 * config.mag,
                             gb::SCREEN_Y as u32 * config.mag))
    } else {
        None
    };

    let mut emulator;
    {
        let mut rom_bytes = vec![];
        let mut rom = bail!(open_rom(&config.rom_name));
        bail!(rom.read_to_end(&mut rom_bytes));

        emulator = Emulator::from_data(&rom_bytes, 44100.0).unwrap();

        // Load save file in ram
        // we don't care if we can't fill the whole buffer, it just
        // means that we don't have a save file
        let _ = save_file.read_exact(emulator.cpu.handler_holder.ram());
    }

    let mut debugger = Debugger::new();
    for c in &config.commands {
        bail!(debugger.exec(c, &mut emulator));
    }

    if config.is_debug || config.commands.len() > 0 {
        debugger.breakpoint(&mut emulator);
    }

    let mut natural_speed = true;
    let mut counter = config.timeout * 1000000;

    while !config.is_headless || counter > 0 {
        debugger.check_breakpoints(&mut emulator);

        emulator.cpu.next_instruction();

        if let Some(ref mut c) = controller {
            if emulator.cpu.handler_holder.should_refresh() {
                match c.check_events(&mut emulator) {
                    Event::Quit => break,
                    Event::Break => {
                        debugger.breakpoint(&mut emulator);
                    },
                    Event::ToggleSpeed => { natural_speed = !natural_speed },
                    Event::Continue => {},
                }

                c.refresh(&mut emulator);
            }
        }

        debugger.next_instruction(&mut emulator);

        counter -= 1;
    }

    if let Some(addr) = config.integ_tests_string_addr {
        print!("{}", parse_string_at(addr, &mut emulator.cpu));
    }

    bail!(save_file.seek(SeekFrom::Start(0)));
    bail!(save_file.write_all(emulator.cpu.handler_holder.ram()));
}
<|endoftext|>


<commit_before>//! Support for the IRC protocol using Tokio.

pub mod caps;
pub mod command;
pub mod irc;
pub mod line;
pub mod message;
pub mod response;
<commit_msg>Added reexports to the proto module.<commit_after>//! Support for the IRC protocol using Tokio.

pub mod caps;
pub mod command;
pub mod irc;
pub mod line;
pub mod message;
pub mod response;

pub use self::caps::{Capability, NegotiationVersion};
pub use self::command::{BatchSubCommand, Command};
pub use self::irc::IrcCodec;
pub use self::message::Message;
pub use self::response::Response;
<|endoftext|>


<commit_before>use crate::utils::{if_sequence, parent_node_is_if_expr, span_help_and_lint, SpanlessEq};
use rustc::hir::*;
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
use rustc::{declare_lint_pass, declare_tool_lint};

declare_clippy_lint! {
    /// **What it does:** Checks comparison chains written with `if` that can be
    /// rewritten with `match` and `cmp`.
    ///
    /// **Why is this bad?** `if` is not guaranteed to be exhaustive and conditionals can get
    /// repetitive
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust,ignore
    /// # fn a() {}
    /// # fn b() {}
    /// # fn c() {}
    /// fn f(x: u8, y: u8) {
    ///     if x > y {
    ///         a()
    ///     } else if x < y {
    ///         b()
    ///     } else {
    ///         c()
    ///     }
    /// }
    /// ```
    ///
    /// Could be written:
    ///
    /// ```rust,ignore
    /// use std::cmp::Ordering;
    /// # fn a() {}
    /// # fn b() {}
    /// # fn c() {}
    /// fn f(x: u8, y: u8) {
    ///      match x.cmp(y) {
    ///          Ordering::Greater => a(),
    ///          Ordering::Less => b(),
    ///          Ordering::Equal => c()
    ///      }
    /// }
    /// ```
    pub COMPARISON_CHAIN,
    style,
    "`if`s that can be rewritten with `match` and `cmp`"
}

declare_lint_pass!(ComparisonChain => [COMPARISON_CHAIN]);

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ComparisonChain {
    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
        if expr.span.from_expansion() {
            return;
        }

        // We only care about the top-most `if` in the chain
        if parent_node_is_if_expr(expr, cx) {
            return;
        }

        // Check that there exists at least one explicit else condition
        let (conds, _) = if_sequence(expr);
        if conds.len() < 2 {
            return;
        }

        for cond in conds.windows(2) {
            if let (
                &ExprKind::Binary(ref kind1, ref lhs1, ref rhs1),
                &ExprKind::Binary(ref kind2, ref lhs2, ref rhs2),
            ) = (&cond[0].node, &cond[1].node)
            {
                if !kind_is_cmp(kind1.node) || !kind_is_cmp(kind2.node) {
                    return;
                }

                // Check that both sets of operands are equal
                let mut spanless_eq = SpanlessEq::new(cx);
                if (!spanless_eq.eq_expr(lhs1, lhs2) || !spanless_eq.eq_expr(rhs1, rhs2))
                    && (!spanless_eq.eq_expr(lhs1, rhs2) || !spanless_eq.eq_expr(rhs1, lhs2))
                {
                    return;
                }
            } else {
                // We only care about comparison chains
                return;
            }
        }
        span_help_and_lint(
            cx,
            COMPARISON_CHAIN,
            expr.span,
            "`if` chain can be rewritten with `match`",
            "Consider rewriting the `if` chain to use `cmp` and `match`.",
        )
    }
}

fn kind_is_cmp(kind: BinOpKind) -> bool {
    match kind {
        BinOpKind::Lt | BinOpKind::Gt | BinOpKind::Eq => true,
        _ => false,
    }
}
<commit_msg>Fix example in docs<commit_after>use crate::utils::{if_sequence, parent_node_is_if_expr, span_help_and_lint, SpanlessEq};
use rustc::hir::*;
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
use rustc::{declare_lint_pass, declare_tool_lint};

declare_clippy_lint! {
    /// **What it does:** Checks comparison chains written with `if` that can be
    /// rewritten with `match` and `cmp`.
    ///
    /// **Why is this bad?** `if` is not guaranteed to be exhaustive and conditionals can get
    /// repetitive
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust,ignore
    /// # fn a() {}
    /// # fn b() {}
    /// # fn c() {}
    /// fn f(x: u8, y: u8) {
    ///     if x > y {
    ///         a()
    ///     } else if x < y {
    ///         b()
    ///     } else {
    ///         c()
    ///     }
    /// }
    /// ```
    ///
    /// Could be written:
    ///
    /// ```rust,ignore
    /// use std::cmp::Ordering;
    /// # fn a() {}
    /// # fn b() {}
    /// # fn c() {}
    /// fn f(x: u8, y: u8) {
    ///      match x.cmp(&y) {
    ///          Ordering::Greater => a(),
    ///          Ordering::Less => b(),
    ///          Ordering::Equal => c()
    ///      }
    /// }
    /// ```
    pub COMPARISON_CHAIN,
    style,
    "`if`s that can be rewritten with `match` and `cmp`"
}

declare_lint_pass!(ComparisonChain => [COMPARISON_CHAIN]);

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ComparisonChain {
    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
        if expr.span.from_expansion() {
            return;
        }

        // We only care about the top-most `if` in the chain
        if parent_node_is_if_expr(expr, cx) {
            return;
        }

        // Check that there exists at least one explicit else condition
        let (conds, _) = if_sequence(expr);
        if conds.len() < 2 {
            return;
        }

        for cond in conds.windows(2) {
            if let (
                &ExprKind::Binary(ref kind1, ref lhs1, ref rhs1),
                &ExprKind::Binary(ref kind2, ref lhs2, ref rhs2),
            ) = (&cond[0].node, &cond[1].node)
            {
                if !kind_is_cmp(kind1.node) || !kind_is_cmp(kind2.node) {
                    return;
                }

                // Check that both sets of operands are equal
                let mut spanless_eq = SpanlessEq::new(cx);
                if (!spanless_eq.eq_expr(lhs1, lhs2) || !spanless_eq.eq_expr(rhs1, rhs2))
                    && (!spanless_eq.eq_expr(lhs1, rhs2) || !spanless_eq.eq_expr(rhs1, lhs2))
                {
                    return;
                }
            } else {
                // We only care about comparison chains
                return;
            }
        }
        span_help_and_lint(
            cx,
            COMPARISON_CHAIN,
            expr.span,
            "`if` chain can be rewritten with `match`",
            "Consider rewriting the `if` chain to use `cmp` and `match`.",
        )
    }
}

fn kind_is_cmp(kind: BinOpKind) -> bool {
    match kind {
        BinOpKind::Lt | BinOpKind::Gt | BinOpKind::Eq => true,
        _ => false,
    }
}
<|endoftext|>


<commit_before>use crate::cpp_code_generator;
use crate::cpp_code_generator::apply_moc;
use crate::database::CppCheckerEnv;
use crate::database::CppFfiItem;
use crate::database::CppFfiItemKind;
use crate::processor::ProcessorData;
use log::{debug, trace};
use rayon::iter::ParallelIterator;
use rayon::slice::ParallelSliceMut;
use ritual_common::cpp_build_config::{CppBuildConfigData, CppBuildPaths};
use ritual_common::cpp_lib_builder::{
    c2r_cmake_vars, BuildType, CppLibBuilder, CppLibBuilderOutput,
};
use ritual_common::errors::{bail, err_msg, Result};
use ritual_common::file_utils::{create_dir_all, create_file, path_to_str, remove_dir_all};
use ritual_common::target::current_target;
use ritual_common::utils::MapIfOk;
use ritual_common::utils::ProgressBar;
use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::io::Write;
use std::path::PathBuf;
use std::sync::{Arc, Mutex};
use std::thread::ThreadId;
use std::time::Instant;
use std::{iter, thread};

const PARALLEL_CHUNKS: usize = 64;

fn check_snippets<'a>(
    data: &mut CppCheckerData,
    snippets: impl Iterator<Item = &'a Snippet>,
) -> Result<CppLibBuilderOutput> {
    let mut any_needs_moc = false;
    {
        let mut file = create_file(&data.main_cpp_path)?;
        writeln!(file, "#include \"utils.h\"")?;
        writeln!(file)?;
        let mut main_content = Vec::new();
        for snippet in snippets {
            if snippet.needs_moc {
                any_needs_moc = true;
            }
            match snippet.context {
                SnippetContext::Main => {
                    main_content.push(&snippet.code);
                }
                SnippetContext::Global => {
                    writeln!(file, "{}", snippet.code)?;
                    writeln!(file)?;
                }
            }
        }

        writeln!(file, "int main() {{")?;
        for item in main_content {
            writeln!(file, "{{")?;
            writeln!(file, "{}", item)?;
            writeln!(file, "}}")?;
        }
        writeln!(file, "}}")?;
    }
    if any_needs_moc && !data.crate_name.starts_with("moqt_") {
        apply_moc(&data.main_cpp_path)?;
    }

    let instant = Instant::now();
    let result = data.builder.run();
    trace!("cpp builder time: {:?}", instant.elapsed());
    result
}

fn snippet_for_item(item: &CppFfiItem, all_items: &[CppFfiItem]) -> Result<Snippet> {
    match &item.kind {
        CppFfiItemKind::Function(cpp_ffi_function) => {
            let item_code = cpp_code_generator::function_implementation(cpp_ffi_function)?;
            let mut needs_moc = false;
            let full_code = if let Some(index) = item.source_ffi_item {
                let source_item = all_items
                    .get(index)
                    .ok_or_else(|| err_msg("ffi item references invalid index"))?;
                match &item.kind {
                    CppFfiItemKind::Function(_) => {}
                    CppFfiItemKind::QtSlotWrapper(_) => needs_moc = true,
                }
                let source_item_code = source_item.source_item_cpp_code()?;
                format!("{}\n{}", source_item_code, item_code)
            } else {
                item_code
            };
            Ok(Snippet::new_global(full_code, needs_moc))
        }
        CppFfiItemKind::QtSlotWrapper(_) => {
            Ok(Snippet::new_global(item.source_item_cpp_code()?, true))
        }
    }
}

struct CppCheckerData {
    main_cpp_path: PathBuf,
    crate_name: String,
    builder: CppLibBuilder,
}

struct CppChecker<'b, 'a: 'b> {
    data: &'b mut ProcessorData<'a>,
    instance_provider: InstanceProvider,
}

#[derive(Debug, Clone, Copy)]
enum SnippetContext {
    Main,
    Global,
}

#[derive(Debug, Clone)]
struct Snippet {
    code: String,
    context: SnippetContext,
    needs_moc: bool,
}

#[derive(Debug, Clone)]
struct SnippetWithIndexes {
    snippet: Snippet,
    ffi_item_index: usize,
    output: Option<CppLibBuilderOutput>,
}

impl Snippet {
    fn new_in_main<S: Into<String>>(code: S, needs_moc: bool) -> Self {
        Snippet {
            code: code.into(),
            context: SnippetContext::Main,
            needs_moc,
        }
    }
    fn new_global<S: Into<String>>(code: S, needs_moc: bool) -> Self {
        Snippet {
            code: code.into(),
            context: SnippetContext::Global,
            needs_moc,
        }
    }
}

struct PreliminaryTest {
    name: String,
    snippet: Snippet,
    expected: bool,
}

impl PreliminaryTest {
    fn new(name: &str, expected: bool, snippet: Snippet) -> Self {
        Self {
            name: name.into(),
            expected,
            snippet,
        }
    }
}

fn binary_check(
    snippets: &mut [SnippetWithIndexes],
    data: &mut CppCheckerData,
    progress_bar: &ProgressBar,
) -> Result<()> {
    if snippets.len() < 3 {
        for snippet in &mut *snippets {
            let output = check_snippets(data, iter::once(&snippet.snippet))?;
            snippet.output = Some(output);
            progress_bar.add(1);
        }
        return Ok(());
    }

    let output = check_snippets(data, snippets.iter().map(|s| &s.snippet))?;
    if let CppLibBuilderOutput::Success = output {
        for snippet in &mut *snippets {
            snippet.output = Some(output.clone());
        }
        progress_bar.add(snippets.len() as u64);
    } else {
        let split_point = snippets.len() / 2;
        let (left, right) = snippets.split_at_mut(split_point);
        binary_check(left, data, progress_bar)?;
        binary_check(right, data, progress_bar)?;
    }
    Ok(())
}

fn check_preliminary_test(data: &mut CppCheckerData, test: &PreliminaryTest) -> Result<()> {
    match check_snippets(data, iter::once(&test.snippet))? {
        CppLibBuilderOutput::Success => {
            if !test.expected {
                bail!("Nevative test ({}) succeeded", test.name);
            }
        }
        CppLibBuilderOutput::Fail(output) => {
            if test.expected {
                bail!("Positive test ({}) failed: {}", test.name, output.stderr);
            }
        }
    }
    Ok(())
}

#[derive(Debug, Clone)]
struct InstanceProvider {
    parent_path: PathBuf,
    include_directives: Vec<PathBuf>,
    crate_name: String,
    cpp_build_config: CppBuildConfigData,
    cpp_build_paths: CppBuildPaths,
}

impl InstanceProvider {
    fn get(&self, id: &str) -> Result<CppCheckerData> {
        let root_path = self.parent_path.join(id);
        if root_path.exists() {
            remove_dir_all(&root_path)?;
        }
        let src_path = root_path.join("src");
        create_dir_all(&src_path)?;

        let mut cmake_file = create_file(src_path.join("CMakeLists.txt"))?;
        write!(
            cmake_file,
            "{}",
            include_str!("../templates/cpp_checker/CMakeLists.txt")
        )?;

        let mut utils_file = create_file(src_path.join("utils.h"))?;
        write!(
            utils_file,
            include_str!("../templates/cpp_checker/utils.h"),
            include_directives_code = self
                .include_directives
                .iter()
                .map_if_ok(|d| -> Result<_> { Ok(format!("#include \"{}\"", path_to_str(d)?)) })?
                .join("\n")
        )?;

        let builder = CppLibBuilder {
            cmake_source_dir: src_path.clone(),
            build_dir: root_path.join("build"),
            install_dir: None,
            num_jobs: Some(1),
            build_type: BuildType::Debug,
            cmake_vars: c2r_cmake_vars(&self.cpp_build_config, &self.cpp_build_paths, None)?,
            capture_output: true,
            skip_cmake: false,
            skip_cmake_after_first_run: true,
        };

        Ok(CppCheckerData {
            builder,
            main_cpp_path: src_path.join("main.cpp"),
            crate_name: self.crate_name.clone(),
        })
    }
}

struct InstanceStorage {
    instances: Arc<Mutex<HashMap<ThreadId, Arc<Mutex<CppCheckerData>>>>>,
    provider: InstanceProvider,
}

impl InstanceStorage {
    fn new(provider: InstanceProvider) -> Self {
        Self {
            provider,
            instances: Default::default(),
        }
    }
    fn current(&self) -> Result<Arc<Mutex<CppCheckerData>>> {
        let mut instances = self.instances.lock().unwrap();
        let instances_len = instances.len();
        let instance = match instances.entry(thread::current().id()) {
            Entry::Vacant(entry) => {
                let instance = self.provider.get(&format!("main_{}", instances_len))?;
                entry.insert(Arc::new(Mutex::new(instance)))
            }
            Entry::Occupied(entry) => entry.into_mut(),
        };
        Ok(Arc::clone(instance))
    }
}

impl CppChecker<'_, '_> {
    fn env(&self) -> CppCheckerEnv {
        CppCheckerEnv {
            target: current_target(),
            cpp_library_version: self.data.config.cpp_lib_version().map(|s| s.to_string()),
        }
    }

    fn run(&mut self) -> Result<()> {
        let env = self.env();
        self.data.current_database.add_environment(env.clone());

        let mut snippets = Vec::new();
        for (ffi_item_index, ffi_item) in self.data.current_database.ffi_items().iter().enumerate()
        {
            if ffi_item.checks.has_env(&env) {
                continue;
            }

            match snippet_for_item(ffi_item, self.data.current_database.ffi_items()) {
                Ok(snippet) => {
                    snippets.push(SnippetWithIndexes {
                        ffi_item_index,
                        snippet,
                        output: None,
                    });
                }
                Err(err) => {
                    debug!(
                        "can't create snippet: {}: {:?}",
                        ffi_item.kind.short_text(),
                        err
                    );
                }
            }
        }

        if snippets.is_empty() {
            return Ok(());
        }

        self.run_tests()?;

        let progress_bar = ProgressBar::new(snippets.len() as u64, "Checking items");

        let instances = InstanceStorage::new(self.instance_provider.clone());

        snippets
            .par_chunks_mut(PARALLEL_CHUNKS)
            .map(|chunk| {
                let progress_bar = progress_bar.clone();
                let instance = instances.current()?;
                let mut instance = instance.lock().unwrap();
                binary_check(chunk, &mut instance, &progress_bar)
            })
            .collect::<Result<_>>()?;

        for snippet in snippets {
            let ffi_item = &mut self.data.current_database.ffi_items_mut()[snippet.ffi_item_index];
            let output = snippet.output.unwrap();
            if output.is_success() {
                debug!("success: {}", ffi_item.kind.short_text());
            } else {
                debug!("error: {}: {:?}", ffi_item.kind.short_text(), output);
                trace!("snippet: {:?}", snippet.snippet);
            }
            ffi_item.checks.add(env.clone(), output.is_success());
        }
        Ok(())
    }

    fn run_tests(&mut self) -> Result<()> {
        let positive_tests = &[
            PreliminaryTest::new(
                "hello world",
                true,
                Snippet::new_in_main("std::cout << \"Hello world\\n\";", false),
            ),
            PreliminaryTest::new(
                "correct assertion",
                true,
                Snippet::new_in_main("ritual_assert(2 + 2 == 4);", false),
            ),
            PreliminaryTest::new(
                "type traits",
                true,
                Snippet::new_in_main(
                    "\
                     class C1 {}; \n\
                     enum E1 {};  \n\
                     ritual_assert(std::is_class<C1>::value); \n\
                     ritual_assert(!std::is_class<E1>::value); \n\
                     ritual_assert(!std::is_enum<C1>::value); \n\
                     ritual_assert(std::is_enum<E1>::value); \
                     ritual_assert(sizeof(C1) > 0);\
                     ritual_assert(sizeof(E1) > 0);\n\
                     ",
                    false,
                ),
            ),
            PreliminaryTest::new(
                "incorrect assertion in fn",
                true,
                Snippet::new_global("int f1() { ritual_assert(2 + 2 == 5); return 1; }", false),
            ),
        ];
        assert!(positive_tests.iter().all(|t| t.expected));

        let mut instance = self.instance_provider.get("tests")?;
        let all_positive_output =
            check_snippets(&mut instance, positive_tests.iter().map(|t| &t.snippet))?;
        if all_positive_output != CppLibBuilderOutput::Success {
            for test in positive_tests {
                check_preliminary_test(&mut instance, test)?;
            }
        }

        let negative_tests = &[
            PreliminaryTest::new("syntax error", false, Snippet::new_in_main("}", false)),
            PreliminaryTest::new(
                "incorrect assertion",
                false,
                Snippet::new_in_main("ritual_assert(2 + 2 == 5);", false),
            ),
            PreliminaryTest::new(
                "status code 1",
                false,
                Snippet::new_in_main("return 1;", false),
            ),
        ];
        assert!(negative_tests.iter().all(|t| !t.expected));

        for test in negative_tests {
            check_preliminary_test(&mut instance, test)?;
        }

        Ok(())
    }
}

pub fn run(data: &mut ProcessorData<'_>) -> Result<()> {
    let instance_provider = InstanceProvider {
        parent_path: data.workspace.tmp_path().join("cpp_checker"),
        include_directives: data.config.include_directives().to_vec(),
        crate_name: data.current_database.crate_name().to_string(),
        cpp_build_paths: {
            let mut data = data.config.cpp_build_paths().clone();
            data.apply_env();
            data
        },
        cpp_build_config: data.config.cpp_build_config().eval(&current_target())?,
    };
    let mut checker = CppChecker {
        data,
        instance_provider,
    };

    checker.run()?;

    Ok(())
}
<commit_msg>fix moc running in cpp checker<commit_after>use crate::cpp_code_generator;
use crate::cpp_code_generator::apply_moc;
use crate::database::CppCheckerEnv;
use crate::database::CppFfiItem;
use crate::database::CppFfiItemKind;
use crate::processor::ProcessorData;
use log::{debug, trace};
use rayon::iter::ParallelIterator;
use rayon::slice::ParallelSliceMut;
use ritual_common::cpp_build_config::{CppBuildConfigData, CppBuildPaths};
use ritual_common::cpp_lib_builder::{
    c2r_cmake_vars, BuildType, CppLibBuilder, CppLibBuilderOutput,
};
use ritual_common::errors::{bail, err_msg, Result};
use ritual_common::file_utils::{create_dir_all, create_file, path_to_str, remove_dir_all};
use ritual_common::target::current_target;
use ritual_common::utils::MapIfOk;
use ritual_common::utils::ProgressBar;
use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::io::Write;
use std::path::PathBuf;
use std::sync::{Arc, Mutex};
use std::thread::ThreadId;
use std::time::Instant;
use std::{iter, thread};

const PARALLEL_CHUNKS: usize = 64;

fn check_snippets<'a>(
    data: &mut CppCheckerData,
    snippets: impl Iterator<Item = &'a Snippet>,
) -> Result<CppLibBuilderOutput> {
    let mut any_needs_moc = false;
    {
        let mut file = create_file(&data.main_cpp_path)?;
        writeln!(file, "#include \"utils.h\"")?;
        writeln!(file)?;
        let mut main_content = Vec::new();
        for snippet in snippets {
            if snippet.needs_moc {
                any_needs_moc = true;
            }
            match snippet.context {
                SnippetContext::Main => {
                    main_content.push(&snippet.code);
                }
                SnippetContext::Global => {
                    writeln!(file, "{}", snippet.code)?;
                    writeln!(file)?;
                }
            }
        }

        writeln!(file, "int main() {{")?;
        for item in main_content {
            writeln!(file, "{{")?;
            writeln!(file, "{}", item)?;
            writeln!(file, "}}")?;
        }
        writeln!(file, "}}")?;
    }
    if any_needs_moc && !data.crate_name.starts_with("moqt_") {
        apply_moc(&data.main_cpp_path)?;
    }

    let instant = Instant::now();
    let result = data.builder.run();
    trace!("cpp builder time: {:?}", instant.elapsed());
    result
}

fn snippet_for_item(item: &CppFfiItem, all_items: &[CppFfiItem]) -> Result<Snippet> {
    match &item.kind {
        CppFfiItemKind::Function(cpp_ffi_function) => {
            let item_code = cpp_code_generator::function_implementation(cpp_ffi_function)?;
            let mut needs_moc = false;
            let full_code = if let Some(index) = item.source_ffi_item {
                let source_item = all_items
                    .get(index)
                    .ok_or_else(|| err_msg("ffi item references invalid index"))?;
                match &source_item.kind {
                    CppFfiItemKind::Function(_) => {}
                    CppFfiItemKind::QtSlotWrapper(_) => needs_moc = true,
                }
                let source_item_code = source_item.source_item_cpp_code()?;
                format!("{}\n{}", source_item_code, item_code)
            } else {
                item_code
            };
            Ok(Snippet::new_global(full_code, needs_moc))
        }
        CppFfiItemKind::QtSlotWrapper(_) => {
            Ok(Snippet::new_global(item.source_item_cpp_code()?, true))
        }
    }
}

struct CppCheckerData {
    main_cpp_path: PathBuf,
    crate_name: String,
    builder: CppLibBuilder,
}

struct CppChecker<'b, 'a: 'b> {
    data: &'b mut ProcessorData<'a>,
    instance_provider: InstanceProvider,
}

#[derive(Debug, Clone, Copy)]
enum SnippetContext {
    Main,
    Global,
}

#[derive(Debug, Clone)]
struct Snippet {
    code: String,
    context: SnippetContext,
    needs_moc: bool,
}

#[derive(Debug, Clone)]
struct SnippetWithIndexes {
    snippet: Snippet,
    ffi_item_index: usize,
    output: Option<CppLibBuilderOutput>,
}

impl Snippet {
    fn new_in_main<S: Into<String>>(code: S, needs_moc: bool) -> Self {
        Snippet {
            code: code.into(),
            context: SnippetContext::Main,
            needs_moc,
        }
    }
    fn new_global<S: Into<String>>(code: S, needs_moc: bool) -> Self {
        Snippet {
            code: code.into(),
            context: SnippetContext::Global,
            needs_moc,
        }
    }
}

struct PreliminaryTest {
    name: String,
    snippet: Snippet,
    expected: bool,
}

impl PreliminaryTest {
    fn new(name: &str, expected: bool, snippet: Snippet) -> Self {
        Self {
            name: name.into(),
            expected,
            snippet,
        }
    }
}

fn binary_check(
    snippets: &mut [SnippetWithIndexes],
    data: &mut CppCheckerData,
    progress_bar: &ProgressBar,
) -> Result<()> {
    if snippets.len() < 3 {
        for snippet in &mut *snippets {
            let output = check_snippets(data, iter::once(&snippet.snippet))?;
            snippet.output = Some(output);
            progress_bar.add(1);
        }
        return Ok(());
    }

    let output = check_snippets(data, snippets.iter().map(|s| &s.snippet))?;
    if let CppLibBuilderOutput::Success = output {
        for snippet in &mut *snippets {
            snippet.output = Some(output.clone());
        }
        progress_bar.add(snippets.len() as u64);
    } else {
        let split_point = snippets.len() / 2;
        let (left, right) = snippets.split_at_mut(split_point);
        binary_check(left, data, progress_bar)?;
        binary_check(right, data, progress_bar)?;
    }
    Ok(())
}

fn check_preliminary_test(data: &mut CppCheckerData, test: &PreliminaryTest) -> Result<()> {
    match check_snippets(data, iter::once(&test.snippet))? {
        CppLibBuilderOutput::Success => {
            if !test.expected {
                bail!("Nevative test ({}) succeeded", test.name);
            }
        }
        CppLibBuilderOutput::Fail(output) => {
            if test.expected {
                bail!("Positive test ({}) failed: {}", test.name, output.stderr);
            }
        }
    }
    Ok(())
}

#[derive(Debug, Clone)]
struct InstanceProvider {
    parent_path: PathBuf,
    include_directives: Vec<PathBuf>,
    crate_name: String,
    cpp_build_config: CppBuildConfigData,
    cpp_build_paths: CppBuildPaths,
}

impl InstanceProvider {
    fn get(&self, id: &str) -> Result<CppCheckerData> {
        let root_path = self.parent_path.join(id);
        if root_path.exists() {
            remove_dir_all(&root_path)?;
        }
        let src_path = root_path.join("src");
        create_dir_all(&src_path)?;

        let mut cmake_file = create_file(src_path.join("CMakeLists.txt"))?;
        write!(
            cmake_file,
            "{}",
            include_str!("../templates/cpp_checker/CMakeLists.txt")
        )?;

        let mut utils_file = create_file(src_path.join("utils.h"))?;
        write!(
            utils_file,
            include_str!("../templates/cpp_checker/utils.h"),
            include_directives_code = self
                .include_directives
                .iter()
                .map_if_ok(|d| -> Result<_> { Ok(format!("#include \"{}\"", path_to_str(d)?)) })?
                .join("\n")
        )?;

        let builder = CppLibBuilder {
            cmake_source_dir: src_path.clone(),
            build_dir: root_path.join("build"),
            install_dir: None,
            num_jobs: Some(1),
            build_type: BuildType::Debug,
            cmake_vars: c2r_cmake_vars(&self.cpp_build_config, &self.cpp_build_paths, None)?,
            capture_output: true,
            skip_cmake: false,
            skip_cmake_after_first_run: true,
        };

        Ok(CppCheckerData {
            builder,
            main_cpp_path: src_path.join("main.cpp"),
            crate_name: self.crate_name.clone(),
        })
    }
}

struct InstanceStorage {
    instances: Arc<Mutex<HashMap<ThreadId, Arc<Mutex<CppCheckerData>>>>>,
    provider: InstanceProvider,
}

impl InstanceStorage {
    fn new(provider: InstanceProvider) -> Self {
        Self {
            provider,
            instances: Default::default(),
        }
    }
    fn current(&self) -> Result<Arc<Mutex<CppCheckerData>>> {
        let mut instances = self.instances.lock().unwrap();
        let instances_len = instances.len();
        let instance = match instances.entry(thread::current().id()) {
            Entry::Vacant(entry) => {
                let instance = self.provider.get(&format!("main_{}", instances_len))?;
                entry.insert(Arc::new(Mutex::new(instance)))
            }
            Entry::Occupied(entry) => entry.into_mut(),
        };
        Ok(Arc::clone(instance))
    }
}

impl CppChecker<'_, '_> {
    fn env(&self) -> CppCheckerEnv {
        CppCheckerEnv {
            target: current_target(),
            cpp_library_version: self.data.config.cpp_lib_version().map(|s| s.to_string()),
        }
    }

    fn run(&mut self) -> Result<()> {
        let env = self.env();
        self.data.current_database.add_environment(env.clone());

        let mut snippets = Vec::new();
        for (ffi_item_index, ffi_item) in self.data.current_database.ffi_items().iter().enumerate()
        {
            if ffi_item.checks.has_env(&env) {
                continue;
            }

            match snippet_for_item(ffi_item, self.data.current_database.ffi_items()) {
                Ok(snippet) => {
                    snippets.push(SnippetWithIndexes {
                        ffi_item_index,
                        snippet,
                        output: None,
                    });
                }
                Err(err) => {
                    debug!(
                        "can't create snippet: {}: {:?}",
                        ffi_item.kind.short_text(),
                        err
                    );
                }
            }
        }

        if snippets.is_empty() {
            return Ok(());
        }

        self.run_tests()?;

        let progress_bar = ProgressBar::new(snippets.len() as u64, "Checking items");

        let instances = InstanceStorage::new(self.instance_provider.clone());

        snippets
            .par_chunks_mut(PARALLEL_CHUNKS)
            .map(|chunk| {
                let progress_bar = progress_bar.clone();
                let instance = instances.current()?;
                let mut instance = instance.lock().unwrap();
                binary_check(chunk, &mut instance, &progress_bar)
            })
            .collect::<Result<_>>()?;

        for snippet in snippets {
            let ffi_item = &mut self.data.current_database.ffi_items_mut()[snippet.ffi_item_index];
            let output = snippet.output.unwrap();
            if output.is_success() {
                debug!("success: {}", ffi_item.kind.short_text());
            } else {
                debug!("error: {}: {:?}", ffi_item.kind.short_text(), output);
                trace!("snippet: {:?}", snippet.snippet);
            }
            ffi_item.checks.add(env.clone(), output.is_success());
        }
        Ok(())
    }

    fn run_tests(&mut self) -> Result<()> {
        let positive_tests = &[
            PreliminaryTest::new(
                "hello world",
                true,
                Snippet::new_in_main("std::cout << \"Hello world\\n\";", false),
            ),
            PreliminaryTest::new(
                "correct assertion",
                true,
                Snippet::new_in_main("ritual_assert(2 + 2 == 4);", false),
            ),
            PreliminaryTest::new(
                "type traits",
                true,
                Snippet::new_in_main(
                    "\
                     class C1 {}; \n\
                     enum E1 {};  \n\
                     ritual_assert(std::is_class<C1>::value); \n\
                     ritual_assert(!std::is_class<E1>::value); \n\
                     ritual_assert(!std::is_enum<C1>::value); \n\
                     ritual_assert(std::is_enum<E1>::value); \
                     ritual_assert(sizeof(C1) > 0);\
                     ritual_assert(sizeof(E1) > 0);\n\
                     ",
                    false,
                ),
            ),
            PreliminaryTest::new(
                "incorrect assertion in fn",
                true,
                Snippet::new_global("int f1() { ritual_assert(2 + 2 == 5); return 1; }", false),
            ),
        ];
        assert!(positive_tests.iter().all(|t| t.expected));

        let mut instance = self.instance_provider.get("tests")?;
        let all_positive_output =
            check_snippets(&mut instance, positive_tests.iter().map(|t| &t.snippet))?;
        if all_positive_output != CppLibBuilderOutput::Success {
            for test in positive_tests {
                check_preliminary_test(&mut instance, test)?;
            }
        }

        let negative_tests = &[
            PreliminaryTest::new("syntax error", false, Snippet::new_in_main("}", false)),
            PreliminaryTest::new(
                "incorrect assertion",
                false,
                Snippet::new_in_main("ritual_assert(2 + 2 == 5);", false),
            ),
            PreliminaryTest::new(
                "status code 1",
                false,
                Snippet::new_in_main("return 1;", false),
            ),
        ];
        assert!(negative_tests.iter().all(|t| !t.expected));

        for test in negative_tests {
            check_preliminary_test(&mut instance, test)?;
        }

        Ok(())
    }
}

pub fn run(data: &mut ProcessorData<'_>) -> Result<()> {
    let instance_provider = InstanceProvider {
        parent_path: data.workspace.tmp_path().join("cpp_checker"),
        include_directives: data.config.include_directives().to_vec(),
        crate_name: data.current_database.crate_name().to_string(),
        cpp_build_paths: {
            let mut data = data.config.cpp_build_paths().clone();
            data.apply_env();
            data
        },
        cpp_build_config: data.config.cpp_build_config().eval(&current_target())?,
    };
    let mut checker = CppChecker {
        data,
        instance_provider,
    };

    checker.run()?;

    Ok(())
}
<|endoftext|>