inflector/string/pluralize/
mod.rs

1#![deny(warnings)]
2use regex::Regex;
3use string::constants::UNACCONTABLE_WORDS;
4
5macro_rules! add_rule{
6    ($r:ident, $rule:expr => $replace:expr) => {
7        $r.push((Regex::new($rule).unwrap(), $replace));
8    }
9}
10
11macro_rules! rules{
12    ($r:ident; $($rule:expr => $replace:expr), *) => {
13        $(
14            add_rule!{$r, $rule => $replace}
15        )*
16    }
17}
18
19
20lazy_static!{
21    static ref RULES: Vec<(Regex, &'static str)> = {
22        let mut r = Vec::with_capacity(24);
23        rules![r;
24               r"(\w*)s$" => "s",
25               r"(\w*([^aeiou]ese))$" => "",
26               r"(\w*(ax|test))is$" => "es",
27               r"(\w*(alias|[^aou]us|tlas|gas|ris))$" => "es",
28               r"(\w*(e[mn]u))s?$" => "s",
29               r"(\w*([^l]ias|[aeiou]las|[emjzr]as|[iu]am))$" => "",
30               r"(\w*(alumn|syllab|octop|vir|radi|nucle|fung|cact|stimul|termin|bacill|foc|uter|loc|strat))(?:us|i)$" => "i",
31               r"(\w*(alumn|alg|vertebr))(?:a|ae)$" => "ae",
32               r"(\w*(seraph|cherub))(?:im)?$" => "im",
33               r"(\w*(her|at|gr))o$" => "oes",
34               r"(\w*(agend|addend|millenni|dat|extrem|bacteri|desiderat|strat|candelabr|errat|ov|symposi|curricul|automat|quor))(?:a|um)$" => "a",
35               r"(\w*(apheli|hyperbat|periheli|asyndet|noumen|phenomen|criteri|organ|prolegomen|hedr|automat))(?:a|on)$" => "a",
36               r"(\w*)sis$" => "ses",
37               r"(\w*(kni|wi|li))fe$" => "ves",
38               r"(\w*(ar|l|ea|eo|oa|hoo))f$" => "ves",
39               r"(\w*([^aeiouy]|qu))y$" => "ies",
40               r"(\w*([^ch][ieo][ln]))ey$" => "ies",
41               r"(\w*(x|ch|ss|sh|zz)es)$" => "",
42               r"(\w*(x|ch|ss|sh|zz))$" => "es",
43               r"(\w*(matr|cod|mur|sil|vert|ind|append))(?:ix|ex)$" => "ices",
44               r"(\w*(m|l)(?:ice|ouse))$" => "ice",
45               r"(\w*(pe)(?:rson|ople))$" => "ople",
46               r"(\w*(child))(?:ren)?$" => "ren",
47               r"(\w*eaux)$" => ""
48        ];
49        r
50    };
51}
52
53macro_rules! special_cases{
54    ($s:ident, $($singular: expr => $plural:expr), *) => {
55        match &$s[..] {
56            $(
57                $singular => {
58                    return $plural.to_owned();
59                },
60            )*
61            _ => ()
62        }
63    }
64}
65
66
67/// Converts a `&str` to pluralized `String`
68///
69/// ```
70///     use inflector::string::pluralize::to_plural;
71///     let mock_string: &str = "foo_bar";
72///     let expected_string: String = "foo_bars".to_owned();
73///     let asserted_string: String = to_plural(mock_string);
74///     assert_eq!(asserted_string, expected_string);
75///
76/// ```
77/// ```
78///     use inflector::string::pluralize::to_plural;
79///     let mock_string: &str = "ox";
80///     let expected_string: String = "oxen".to_owned();
81///     let asserted_string: String = to_plural(mock_string);
82///     assert_eq!(asserted_string, expected_string);
83///
84/// ```
85/// ```
86///     use inflector::string::pluralize::to_plural;
87///     let mock_string: &str = "crate";
88///     let expected_string: String = "crates".to_owned();
89///     let asserted_string: String = to_plural(mock_string);
90///     assert_eq!(asserted_string, expected_string);
91///
92/// ```
93/// ```
94///     use inflector::string::pluralize::to_plural;
95///     let mock_string: &str = "boxes";
96///     let expected_string: String = "boxes".to_owned();
97///     let asserted_string: String = to_plural(mock_string);
98///     assert_eq!(asserted_string, expected_string);
99///
100/// ```
101/// ```
102///     use inflector::string::pluralize::to_plural;
103///     let mock_string: &str = "vengeance";
104///     let expected_string: String = "vengeance".to_owned();
105///     let asserted_string: String = to_plural(mock_string);
106///     assert_eq!(asserted_string, expected_string);
107///
108/// ```
109/// ```
110///     use inflector::string::pluralize::to_plural;
111///     let mock_string: &str = "yoga";
112///     let expected_string: String = "yoga".to_owned();
113///     let asserted_string: String = to_plural(mock_string);
114///     assert_eq!(asserted_string, expected_string);
115///
116/// ```
117/// ```
118///     use inflector::string::pluralize::to_plural;
119///     let mock_string: &str = "geometry";
120///     let expected_string: String = "geometries".to_owned();
121///     let asserted_string: String = to_plural(mock_string);
122///     assert_eq!(asserted_string, expected_string);
123///
124/// ```
125///
126pub fn to_plural(non_plural_string: &str) -> String {
127    if UNACCONTABLE_WORDS.contains(&non_plural_string.as_ref()) {
128        non_plural_string.to_owned()
129    } else {
130        special_cases![non_plural_string,
131            "ox" => "oxen",
132            "man" => "men",
133            "woman" => "women",
134            "die" => "dice",
135            "yes" => "yeses",
136            "foot" => "feet",
137            "eave" => "eaves",
138            "goose" => "geese",
139            "tooth" => "teeth",
140            "quiz" => "quizzes"
141        ];
142        for &(ref rule, replace) in RULES.iter().rev() {
143            if let Some(c) = rule.captures(&non_plural_string) {
144                if let Some(c) = c.get(1) {
145                    return format!("{}{}", c.as_str(), replace);
146                }
147            }
148        }
149
150        format!("{}s", non_plural_string)
151    }
152}
153
154
155#[cfg(test)]
156mod tests {
157
158    macro_rules! as_item {
159        ($i:item) => { $i };
160    }
161
162    macro_rules! make_tests{
163        ($($singular:ident => $plural:ident); *) =>{
164            $(
165                   as_item! {
166                       #[test]
167                       fn $singular(){
168                           assert_eq!(
169                               stringify!($plural),
170                               super::to_plural(stringify!($singular))
171                               );
172                       }
173                   }
174            )*
175        }
176    }
177
178    #[test]
179    fn boxes() {
180        assert_eq!("boxes", super::to_plural("box"));
181    }
182
183    make_tests!{
184        geometry => geometries;
185        ox => oxen;
186        woman => women;
187        test => tests;
188        axis => axes;
189        knife => knives;
190        agendum => agenda;
191        elf => elves;
192        zoology => zoology
193    }
194}