1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
use std::iter::FromIterator;

use rand::{thread_rng, Rng};

use crate::{Config, Spongemock};

// TODO this implementation is a disgrace; change that.
impl Spongemock for String {
    /// Transform a mutable str reference according to the set parameters in the specified options
    fn mock(&mut self, config: &Config) {
        // Abort on empty string
        if self.is_empty() {
            return;
        }

        // Construct a new RNG
        let mut rng = thread_rng();

        // A vector to store the new chars
        let mut chars = vec![];

        // Boolean flags if the previous character (and the one before it) was uppercase
        let mut prev_upper;
        let mut prev_prev_upper = true;

        // The transformation function
        let mut transform_char = |c: char, prob: f64, inverse: bool| {
            let gen_bool = rng.gen_bool(prob);

            let gen_bool = if inverse { !gen_bool } else { gen_bool };

            if gen_bool {
                chars.extend(c.to_uppercase());
            } else {
                chars.extend(c.to_lowercase());
            };

            gen_bool
        };

        // Transform the first char
        prev_upper = transform_char(self.chars().next().unwrap(), config.first_upper, false);

        // Transform the second char (if present)
        if self.len() != 1 {
            if prev_upper {
                prev_prev_upper = prev_upper;
                prev_upper =
                    transform_char(self.chars().nth(1).unwrap(), config.upper_to_lower, true);
            } else {
                prev_upper =
                    transform_char(self.chars().nth(1).unwrap(), config.lower_to_upper, false);
            }
        }

        for c in self.as_mut().chars().skip(2) {
            // Check if there might be three same-case letters in a row
            if prev_upper == prev_prev_upper {
                // Check if there are two lower- or two uppercase letters
                if prev_upper {
                    prev_prev_upper = prev_upper;
                    prev_upper = transform_char(c, config.upper_upper_to_lower, true);
                } else {
                    prev_prev_upper = prev_upper;
                    prev_upper = transform_char(c, config.lower_lower_to_upper, false);
                }
            } else if prev_upper {
                prev_prev_upper = prev_upper;
                prev_upper = transform_char(c, config.upper_to_lower, true);
            } else {
                prev_prev_upper = prev_upper;
                prev_upper = transform_char(c, config.lower_to_upper, false);
            }
        }

        *self = String::from_iter(chars);
    }
}

// impl<'a> Spongemock {
//     pub fn transform_str<T>(&self, s: T)
//     where
//         T: Into<&'a mut str>,
//     {
//         let s: &'a mut str = s.into();
//     }
// }