<commit_before>use std::sync::Mutex;
use std::collections::HashMap;
use circular_queue::CircularQueue;
use regex::{Regex, RegexBuilder};

use irc::client::prelude::*;

use plugin::*;

use failure::Fail;
use failure::ResultExt;
use error::ErrorKind as FrippyErrorKind;
use error::FrippyError;
use self::error::*;

lazy_static! {
    static ref RE: Regex = Regex::new(r"^s/((?:\\/|[^/])+)/((?:\\/|[^/])*)/(?:(\w+))?\s*$").unwrap();
}

#[derive(PluginName, Debug)]
pub struct Sed {
    per_channel: usize,
    channel_messages: Mutex<HashMap<String, CircularQueue<String>>>,
}

macro_rules! try_lock {
    ( $m:expr ) => {
        match $m.lock() {
            Ok(guard) => guard,
            Err(poisoned) => poisoned.into_inner(),
        }
    }
}

impl Sed {
    pub fn new(per_channel: usize) -> Sed {
        Sed {
            per_channel: per_channel,
            channel_messages: Mutex::new(HashMap::new()),
        }
    }

    fn add_message(&self, channel: String, message: String) {
        let mut channel_messages = try_lock!(self.channel_messages);
        let messages = channel_messages
            .entry(channel)
            .or_insert(CircularQueue::with_capacity(self.per_channel));
        messages.push(message);
    }

    fn run_regex(&self, channel: &str, message: &str) -> Result<String, SedError> {
        let mut global_match = false;
        let mut case_insens = false;
        let mut ign_whitespace = false;
        let mut swap_greed = false;
        let mut disable_unicode = false;

        let captures = RE.captures(message).unwrap();
        debug!("{:?}", captures);

        let first = captures.get(1).unwrap().as_str();
        let second = captures.get(2).unwrap().as_str();

        if let Some(flags) = captures.get(3) {
            let flags = flags.as_str();

            global_match = flags.contains('g');
            case_insens = flags.contains('i');
            ign_whitespace = flags.contains('x');
            disable_unicode = !flags.contains('u');
            swap_greed = flags.contains('U');
        }

        let user_re = RegexBuilder::new(&first.replace(r"\/", "/"))
            .case_insensitive(case_insens)
            .ignore_whitespace(ign_whitespace)
            .unicode(disable_unicode)
            .swap_greed(swap_greed)
            .build().context(ErrorKind::InvalidRegex)?;

        let channel_messages = try_lock!(self.channel_messages);
        let messages = channel_messages.get(channel).ok_or(ErrorKind::NoMessages)?;

        for message in messages.iter() {
            if user_re.is_match(message) {
                let response = if global_match {
                    user_re.replace_all(message, second)
                } else {
                    user_re.replace(message, second)
                };

                return Ok(response.to_string());
            }
        }

        Err(ErrorKind::NoMatch)?
    }
}

impl Plugin for Sed {
    fn execute(&self, client: &IrcClient, message: &Message) -> ExecutionStatus {
        match message.command {
            Command::PRIVMSG(_, ref content) => {
                let channel = message.response_target().unwrap();
                if channel == message.source_nickname().unwrap() {
                    return ExecutionStatus::Done;
                }

                if RE.is_match(content) {
                    let result = match self.run_regex(channel, content) {
                        Ok(msg) => client.send_privmsg(channel, &msg),
                        Err(e) => client.send_notice(channel, &e.to_string()),
                    };

                    match result {
                        Err(e) => ExecutionStatus::Err(e.context(FrippyErrorKind::Connection).into()),
                        Ok(_) => ExecutionStatus::Done,
                    }
                } else {
                    self.add_message(channel.to_string(), content.to_string());<commit_msg>Fix escaped slashes in sed replacement texts<commit_after>use std::sync::Mutex;
use std::collections::HashMap;
use circular_queue::CircularQueue;
use regex::{Regex, RegexBuilder};

use irc::client::prelude::*;

use plugin::*;

use failure::Fail;
use failure::ResultExt;
use error::ErrorKind as FrippyErrorKind;
use error::FrippyError;
use self::error::*;

lazy_static! {
    static ref RE: Regex = Regex::new(r"^s/((?:\\/|[^/])+)/((?:\\/|[^/])*)/(?:(\w+))?\s*$").unwrap();
}

#[derive(PluginName, Debug)]
pub struct Sed {
    per_channel: usize,
    channel_messages: Mutex<HashMap<String, CircularQueue<String>>>,
}

macro_rules! try_lock {
    ( $m:expr ) => {
        match $m.lock() {
            Ok(guard) => guard,
            Err(poisoned) => poisoned.into_inner(),
        }
    }
}

impl Sed {
    pub fn new(per_channel: usize) -> Sed {
        Sed {
            per_channel: per_channel,
            channel_messages: Mutex::new(HashMap::new()),
        }
    }

    fn add_message(&self, channel: String, message: String) {
        let mut channel_messages = try_lock!(self.channel_messages);
        let messages = channel_messages
            .entry(channel)
            .or_insert(CircularQueue::with_capacity(self.per_channel));
        messages.push(message);
    }

    fn run_regex(&self, channel: &str, message: &str) -> Result<String, SedError> {
        let mut global_match = false;
        let mut case_insens = false;
        let mut ign_whitespace = false;
        let mut swap_greed = false;
        let mut disable_unicode = false;

        let captures = RE.captures(message).unwrap();
        debug!("{:?}", captures);

        let first = captures.get(1).unwrap().as_str().replace(r"\/", "/");
        let second = captures.get(2).unwrap().as_str().replace(r"\/", "/");

        if let Some(flags) = captures.get(3) {
            let flags = flags.as_str();

            global_match = flags.contains('g');
            case_insens = flags.contains('i');
            ign_whitespace = flags.contains('x');
            swap_greed = flags.contains('U');
            disable_unicode = !flags.contains('u');
        }

        let user_re = RegexBuilder::new(&first)
            .case_insensitive(case_insens)
            .ignore_whitespace(ign_whitespace)
            .unicode(disable_unicode)
            .swap_greed(swap_greed)
            .build()
            .context(ErrorKind::InvalidRegex)?;

        let channel_messages = try_lock!(self.channel_messages);
        let messages = channel_messages.get(channel).ok_or(ErrorKind::NoMessages)?;

        for message in messages.iter() {
            if user_re.is_match(message) {
                let response = if global_match {
                    user_re.replace_all(message, &second[..])
                } else {
                    user_re.replace(message, &second[..])
                };

                return Ok(response.to_string());
            }
        }

        Err(ErrorKind::NoMatch)?
    }
}

impl Plugin for Sed {
    fn execute(&self, client: &IrcClient, message: &Message) -> ExecutionStatus {
        match message.command {
            Command::PRIVMSG(_, ref content) => {
                let channel = message.response_target().unwrap();
                if channel == message.source_nickname().unwrap() {
                    return ExecutionStatus::Done;
                }

                if RE.is_match(content) {
                    let result = match self.run_regex(channel, content) {
                        Ok(msg) => client.send_privmsg(channel, &msg),
                        Err(e) => client.send_notice(channel, &e.to_string()),
                    };

                    match result {
                        Err(e) => {
                            ExecutionStatus::Err(e.context(FrippyErrorKind::Connection).into())
                        }
                        Ok(_) => ExecutionStatus::Done,
                    }
                } else {
                    self.add_message(channel.to_string(), content.to_string());
{'fraction_non_alphanumeric': 0.1389971094633656, 'fraction_numerical': 0.0007540530350634661, 'mean_word_length': 2.024705435195743, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 33, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3469201', 'n_tokens_mistral': 2355, 'n_tokens_neox': 2276, 'n_words': 430}