harper_core/
irregular_verbs.rs1use serde::Deserialize;
2use std::sync::{Arc, LazyLock};
3
4type Verb = (String, String, String);
5
6#[derive(Debug, Deserialize)]
7pub struct IrregularVerbs {
8 verbs: Vec<Verb>,
9}
10
11fn uncached_inner_new() -> Arc<IrregularVerbs> {
14 IrregularVerbs::from_json_file(include_str!("../irregular_verbs.json"))
15 .map(Arc::new)
16 .unwrap_or_else(|e| panic!("Failed to load irregular verb table: {}", e))
17}
18
19static VERBS: LazyLock<Arc<IrregularVerbs>> = LazyLock::new(uncached_inner_new);
20
21impl IrregularVerbs {
22 pub fn new() -> Self {
23 Self { verbs: 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 verbs JSON");
30
31 let mut verbs = Vec::new();
32
33 for value in values {
34 match value {
35 serde_json::Value::Array(arr) if arr.len() == 3 => {
36 if let (Some(lemma), Some(preterite), Some(past_participle)) =
38 (arr[0].as_str(), arr[1].as_str(), arr[2].as_str())
39 {
40 verbs.push((
41 lemma.to_string(),
42 preterite.to_string(),
43 past_participle.to_string(),
44 ));
45 }
46 }
47 serde_json::Value::String(_) => {}
49 _ => {}
50 }
51 }
52
53 Ok(Self { verbs })
54 }
55
56 pub fn curated() -> Arc<Self> {
57 (*VERBS).clone()
58 }
59
60 pub fn get_past_participle_for_preterite(&self, preterite: &str) -> Option<&str> {
61 self.verbs
62 .iter()
63 .find(|(_, pt, _)| pt.eq_ignore_ascii_case(preterite))
64 .map(|(_, _, pp)| pp.as_str())
65 }
66
67 pub fn get_lemma_for_preterite(&self, preterite: &str) -> Option<&str> {
68 self.verbs
69 .iter()
70 .find(|(_, pt, _)| pt.eq_ignore_ascii_case(preterite))
71 .map(|(lemma, _, _)| lemma.as_str())
72 }
73
74 pub fn get_preterite_for_lemma(&self, lemma: &str) -> Option<&str> {
75 self.verbs
76 .iter()
77 .find(|(l, _, _)| l.eq_ignore_ascii_case(lemma))
78 .map(|(_, pt, _)| pt.as_str())
79 }
80
81 pub fn get_past_participle_for_lemma(&self, lemma: &str) -> Option<&str> {
82 self.verbs
83 .iter()
84 .find(|(l, _, _)| l.eq_ignore_ascii_case(lemma))
85 .map(|(_, _, pp)| pp.as_str())
86 }
87
88 pub fn get_pasts_for_lemma(&self, lemma: &str) -> Option<(&str, &str)> {
89 self.verbs
90 .iter()
91 .find(|(l, _, _)| l.eq_ignore_ascii_case(lemma))
92 .map(|(_, pt, pp)| (pt.as_str(), pp.as_str()))
93 }
94}
95
96impl Default for IrregularVerbs {
97 fn default() -> Self {
98 Self::new()
99 }
100}
101
102#[cfg(test)]
103mod tests {
104 use super::*;
105
106 #[test]
107 fn can_find_irregular_past_participle_for_preterite_lowercase() {
108 assert_eq!(
109 IrregularVerbs::curated().get_past_participle_for_preterite("arose"),
110 Some("arisen")
111 );
112 }
113
114 #[test]
115 fn can_find_irregular_past_participle_for_preterite_uppercase() {
116 assert_eq!(
117 IrregularVerbs::curated().get_past_participle_for_preterite("WENT"),
118 Some("gone")
119 );
120 }
121
122 #[test]
123 fn can_find_irregular_past_participle_same_as_past_tense() {
124 assert_eq!(
125 IrregularVerbs::curated().get_past_participle_for_preterite("taught"),
126 Some("taught")
127 );
128 }
129
130 #[test]
131 fn cant_find_regular_past_participle() {
132 assert_eq!(
133 IrregularVerbs::curated().get_past_participle_for_preterite("walked"),
134 None
135 );
136 }
137
138 #[test]
139 fn cant_find_non_verb() {
140 assert_eq!(
141 IrregularVerbs::curated().get_past_participle_for_preterite("the"),
142 None
143 );
144 }
145}