harper_core/
irregular_nouns.rs1use serde::Deserialize;
2use std::sync::{Arc, LazyLock};
3
4type Noun = (String, String);
5
6#[derive(Debug, Deserialize)]
7pub struct IrregularNouns {
8 nouns: Vec<Noun>,
9}
10
11fn uncached_inner_new() -> Arc<IrregularNouns> {
14 IrregularNouns::from_json_file(include_str!("../irregular_nouns.json"))
15 .map(Arc::new)
16 .unwrap_or_else(|e| panic!("Failed to load irregular noun table: {}", e))
17}
18
19static NOUNS: LazyLock<Arc<IrregularNouns>> = LazyLock::new(uncached_inner_new);
20
21impl IrregularNouns {
22 pub fn new() -> Self {
23 Self { nouns: vec![] }
24 }
25
26 pub fn from_json_file(json: &str) -> Result<Self, serde_json::Error> {
27 let values: Vec<serde_json::Value> =
29 serde_json::from_str(json).expect("Failed to parse irregular nouns JSON");
30
31 let mut nouns = Vec::new();
32
33 for value in values {
34 match value {
35 serde_json::Value::Array(arr) if arr.len() == 2 => {
36 if let (Some(singular), Some(plural)) = (arr[0].as_str(), arr[1].as_str()) {
38 nouns.push((singular.to_string(), plural.to_string()));
39 }
40 }
41 serde_json::Value::String(_) => {}
43 _ => {}
44 }
45 }
46
47 Ok(Self { nouns })
48 }
49
50 pub fn curated() -> Arc<Self> {
51 (*NOUNS).clone()
52 }
53
54 pub fn get_plural_for_singular(&self, singular: &str) -> Option<&str> {
55 self.nouns
56 .iter()
57 .find(|(sg, _)| sg.eq_ignore_ascii_case(singular))
58 .map(|(_, pl)| pl.as_str())
59 }
60
61 pub fn get_singular_for_plural(&self, plural: &str) -> Option<&str> {
62 self.nouns
63 .iter()
64 .find(|(_, pl)| pl.eq_ignore_ascii_case(plural))
65 .map(|(sg, _)| sg.as_str())
66 }
67}
68
69impl Default for IrregularNouns {
70 fn default() -> Self {
71 Self::new()
72 }
73}
74
75#[cfg(test)]
76mod tests {
77 use super::*;
78
79 #[test]
80 fn can_find_irregular_plural_for_singular_lowercase() {
81 assert_eq!(
82 IrregularNouns::curated().get_plural_for_singular("man"),
83 Some("men")
84 );
85 }
86
87 #[test]
88 fn can_find_irregular_plural_for_singular_uppercase() {
89 assert_eq!(
90 IrregularNouns::curated().get_plural_for_singular("WOMAN"),
91 Some("women")
92 );
93 }
94
95 #[test]
96 fn can_find_singular_for_irregular_plural() {
97 assert_eq!(
98 IrregularNouns::curated().get_singular_for_plural("children"),
99 Some("child")
100 );
101 }
102
103 #[test]
104 fn cant_find_regular_plural() {
105 assert_eq!(
106 IrregularNouns::curated().get_plural_for_singular("car"),
107 None
108 );
109 }
110
111 #[test]
112 fn cant_find_non_noun() {
113 assert_eq!(
114 IrregularNouns::curated().get_plural_for_singular("the"),
115 None
116 );
117 }
118}