owoify_rs 2.0.0

This Rust port of mohan-cao's owoify-js is released under MIT license, which is also the same license of owoify-js. This simple library will turn any string into nonsensical babyspeak similar to what LeafySweet's infamous Chrome extension did.
Documentation
use regex::Regex;
use std::collections::HashSet;

#[derive(Debug, Clone)]
pub(crate) struct Word {
    pub(crate) word: String,
    pub(crate) replaced_words: HashSet<String>,
}

impl ToString for Word {
    fn to_string(&self) -> String {
        self.word.clone()
    }
}

impl Word {
    pub(crate) fn new() -> Self {
        Word {
            word: String::new(),
            replaced_words: HashSet::new(),
        }
    }

    pub(crate) fn replace(
        mut self,
        search_value: &Regex,
        replace_value: &str,
        replace_replaced_words: bool,
    ) -> Self {
        if !replace_replaced_words
            && self.search_value_contains_replaced_words(&search_value, &replace_value)
        {
            return self;
        }
        let mut replacing_word = self.word.clone();
        if search_value.is_match(&self.word) {
            replacing_word = search_value
                .replace_all(&self.word, replace_value)
                .parse()
                .expect("Failed to replace string.");
        }
        let collection = search_value.captures_iter(&self.word).collect::<Vec<_>>();
        let replaced_words: Vec<String> = if collection.len() > 1 {
            collection
                .into_iter()
                .map(|c| {
                    c.get(0)
                        .expect("Cannot get matched item at index 0 of the capture.")
                        .as_str()
                        .replace(
                            c.get(0)
                                .expect("Cannot get matched item at index 0 of the capture.")
                                .as_str(),
                            replace_value,
                        )
                })
                .collect()
        } else {
            vec![]
        };

        if replacing_word != self.word {
            for word in replaced_words.into_iter() {
                self.replaced_words.insert(word);
            }
            self.word = replacing_word
        }
        self
    }

    pub(crate) fn replace_with_func_single<F>(
        mut self,
        search_value: &Regex,
        func: F,
        replace_replaced_words: bool,
    ) -> Self
    where
        F: FnOnce() -> String,
    {
        let replace_value = func();

        if !replace_replaced_words
            && self.search_value_contains_replaced_words(&search_value, &replace_value)
        {
            return self;
        }

        let mut replacing_word = self.word.clone();
        if search_value.is_match(&self.word) {
            let match_item = search_value
                .captures(&self.word)
                .expect("Cannot get captures.")
                .get(0)
                .expect("Cannot get matched item at index 0 of the capture.")
                .as_str();

            replacing_word = self.word.replace(match_item, &replace_value);
        }

        let collection = search_value.captures_iter(&self.word).collect::<Vec<_>>();
        let replaced_words: Vec<String> = if collection.len() > 1 {
            collection
                .into_iter()
                .map(|c| {
                    c.get(0)
                        .expect("Cannot get matched item at index 0 of the capture.")
                        .as_str()
                        .replace(
                            c.get(0)
                                .expect("Cannot get matched item at index 0 of the capture.")
                                .as_str(),
                            &replace_value,
                        )
                })
                .collect()
        } else {
            vec![]
        };

        if replacing_word != self.word {
            for word in replaced_words.into_iter() {
                self.replaced_words.insert(word);
            }
            self.word = replacing_word
        }
        self
    }

    pub(crate) fn replace_with_func_multiple<F>(
        mut self,
        search_value: &Regex,
        func: F,
        replace_replaced_words: bool,
    ) -> Self
    where
        F: FnOnce(&str, &str) -> String,
    {
        if !search_value.is_match(&self.word) {
            return self;
        }

        let word = self.word.clone();
        let captures = search_value.captures(&word).expect("Cannot get captures.");
        let replace_value = func(
            captures
                .get(1)
                .expect("Cannot get matched item at index 1 of the capture.")
                .as_str(),
            captures
                .get(2)
                .expect("Cannot get matched item at index 2 of the capture.")
                .as_str(),
        );

        if !replace_replaced_words
            && self.search_value_contains_replaced_words(&search_value, &replace_value)
        {
            return self;
        }

        let replacing_word = self.word.replace(
            captures
                .get(0)
                .expect("Cannot get matched item at index 0 of the capture.")
                .as_str(),
            &replace_value,
        );
        let collection = search_value.captures_iter(&self.word).collect::<Vec<_>>();
        let replaced_words = if collection.len() > 1 {
            collection
                .into_iter()
                .map(|c| {
                    c.get(0)
                        .expect("Cannot get matched item at index 0 of the capture.")
                        .as_str()
                        .replace(
                            c.get(0)
                                .expect("Cannot get matched item at index 0 of the capture.")
                                .as_str(),
                            &replace_value,
                        )
                })
                .collect::<Vec<_>>()
        } else {
            vec![]
        };

        if replacing_word != self.word {
            for word in replaced_words.into_iter() {
                self.replaced_words.insert(word);
            }
            self.word = replacing_word;
        }
        self
    }

    fn search_value_contains_replaced_words<'a>(
        &self,
        search_value: &'a Regex,
        replace_value: &'a str,
    ) -> bool {
        self.replaced_words.iter().any(|s| {
            if search_value.is_match(s.as_str()) {
                let match_result = search_value
                    .captures(s.as_str())
                    .expect("Cannot get captures.")
                    .get(0)
                    .expect("Cannot get matched item at index 0 of the capture.")
                    .as_str();
                return s.replace(match_result, replace_value) == *s;
            }
            false
        })
    }
}