Skip to main content

alembic_engine/
inflect.rs

1//! string inflection shared by the django/drf adapters.
2
3/// pluralize a django/drf endpoint segment, the way django's url router does.
4/// shared by the netbox, nautobot, and django adapters.
5pub fn pluralize(value: &str) -> String {
6    if value.ends_with("address") {
7        return format!("{value}es");
8    }
9    if let Some(stripped) = value.strip_suffix('y') {
10        // consonant + y -> ies (city -> cities); vowel + y -> +s (bay -> bays)
11        if !stripped.ends_with(['a', 'e', 'i', 'o', 'u']) {
12            return format!("{stripped}ies");
13        }
14    }
15    if value.ends_with('s')
16        || value.ends_with('x')
17        || value.ends_with('z')
18        || value.ends_with("ch")
19        || value.ends_with("sh")
20    {
21        return format!("{value}es");
22    }
23    format!("{value}s")
24}
25
26#[cfg(test)]
27mod tests {
28    use super::*;
29
30    #[test]
31    fn pluralize_covers_every_branch() {
32        // x/z/ch/sh -> +es (the cases django's stale copy got wrong)
33        assert_eq!(pluralize("prefix"), "prefixes");
34        assert_eq!(pluralize("box"), "boxes");
35        assert_eq!(pluralize("buzz"), "buzzes");
36        assert_eq!(pluralize("branch"), "branches");
37        assert_eq!(pluralize("dish"), "dishes");
38        // consonant + y -> ies
39        assert_eq!(pluralize("city"), "cities");
40        // vowel + y -> +s (django's stale copy got this wrong too)
41        assert_eq!(pluralize("gateway"), "gateways");
42        assert_eq!(pluralize("bay"), "bays");
43        // address -> addresses, special-cased ahead of the +es rule
44        assert_eq!(pluralize("address"), "addresses");
45        assert_eq!(pluralize("ip-address"), "ip-addresses");
46        // already ends in s -> +es
47        assert_eq!(pluralize("status"), "statuses");
48        // default -> +s
49        assert_eq!(pluralize("device"), "devices");
50        assert_eq!(pluralize("device-bay"), "device-bays");
51    }
52}