1use crate::Result;
2use serde::Deserialize;
3
4#[derive(Debug, PartialEq)]
9pub struct WordElement {
10 pub word: String,
12 pub score: usize,
15 pub num_syllables: Option<usize>,
18 pub parts_of_speech: Option<Vec<PartOfSpeech>>,
21 pub pronunciation: Option<String>,
25 pub frequency: Option<f32>,
29 pub definitions: Option<Vec<Definition>>,
32}
33
34#[derive(Debug, PartialEq)]
36pub struct Definition {
37 pub part_of_speech: Option<PartOfSpeech>,
39 pub definition: String,
41}
42
43#[derive(Debug)]
46pub struct Response {
47 json: String,
48}
49
50#[derive(Clone, Copy, Debug, PartialEq)]
52pub enum PartOfSpeech {
53 Noun, Adjective, Adverb, Verb, }
62
63#[derive(Deserialize, Debug)]
64#[serde(rename_all = "camelCase")]
65struct DatamuseWordObject {
66 word: String,
67 score: usize,
68 num_syllables: Option<usize>,
69 tags: Option<Vec<String>>,
70 defs: Option<Vec<String>>,
71}
72
73impl Response {
74 pub fn list(&self) -> Result<Vec<WordElement>> {
76 parse_response(&self.json)
77 }
78
79 pub(crate) fn new(json: String) -> Response {
80 Response { json }
81 }
82}
83
84impl PartOfSpeech {
85 fn from_str(pos: &str) -> Option<Self> {
86 match pos {
87 "n" => Some(Self::Noun),
88 "adj" => Some(Self::Adjective),
89 "adv" => Some(Self::Adverb),
90 "v" => Some(Self::Verb),
91 _ => None, }
93 }
94}
95
96fn parse_response(response: &str) -> Result<Vec<WordElement>> {
97 let word_list: Vec<DatamuseWordObject> = serde_json::from_str(response)?;
98 let mut converted_word_list: Vec<WordElement> = Vec::new();
99
100 for word in word_list {
101 converted_word_list.push(word_obj_to_word_elem(word));
102 }
103
104 Ok(converted_word_list)
105}
106
107fn word_obj_to_word_elem(word_obj: DatamuseWordObject) -> WordElement {
108 let word = word_obj.word;
109 let score = word_obj.score;
110 let num_syllables = word_obj.num_syllables;
111
112 let mut parts_of_speech: Vec<PartOfSpeech> = Vec::new();
113 let mut pronunciation = None;
114 let mut frequency = None;
115
116 if let Some(tags) = word_obj.tags {
117 for tag in tags {
118 let parts: Vec<&str> = tag.split(':').collect();
119
120 match parts[0] {
121 "f" => {
122 if parts.len() == 2 {
123 frequency = match parts[1].parse() {
124 Ok(val) => Some(val),
125 Err(_) => None,
126 }
127 }
128 }
129 "pron" => {
130 if let None = pronunciation {
131 if parts.len() == 2 {
133 pronunciation = Some(parts[1].to_string());
134 }
135 }
136 }
137 "ipa_pron" => {
138 if parts.len() == 2 {
139 pronunciation = Some(parts[1].to_string());
140 }
141 }
142 val => match PartOfSpeech::from_str(&val) {
143 Some(val) => parts_of_speech.push(val),
144 None => continue,
145 },
146 }
147 }
148 }
149
150 let pos;
151 if parts_of_speech.len() > 0 {
152 pos = Some(parts_of_speech);
153 } else {
154 pos = None;
155 }
156 let parts_of_speech = pos;
157
158 let mut definitions = None;
159 if let Some(defs) = word_obj.defs {
160 if defs.len() > 0 {
161 let mut def_list: Vec<Definition> = Vec::new();
162
163 for def in defs {
164 let parts: Vec<&str> = def.split('\t').collect();
165
166 if parts.len() == 2 {
167 let pos = PartOfSpeech::from_str(&parts[0]);
168 def_list.push(Definition {
169 part_of_speech: pos,
170 definition: parts[1].to_string(),
171 });
172 }
173 }
174
175 definitions = Some(def_list);
176 }
177 }
178
179 WordElement {
180 word,
181 score,
182 num_syllables,
183 parts_of_speech,
184 pronunciation,
185 frequency,
186 definitions,
187 }
188}
189
190#[cfg(test)]
191mod tests {
192 use super::DatamuseWordObject;
193 use crate::{Definition, PartOfSpeech, WordElement};
194
195 #[test]
196 fn word_obj_to_word_elem() {
197 let word_obj = DatamuseWordObject {
198 word: String::from("cow"),
199 score: 2168,
200 num_syllables: Some(1),
201 tags: Some(vec![
202 String::from("n"),
203 String::from("pron:K AW1 "),
204 String::from("f:16.567268"),
205 ]),
206 defs: Some(vec![
207 String::from("n\tmature female of mammals of which the male is called `bull'"),
208 String::from("n\tfemale of domestic cattle"),
209 ]),
210 };
211
212 let actual = super::word_obj_to_word_elem(word_obj);
213
214 let expected = WordElement {
215 word: String::from("cow"),
216 score: 2168,
217 num_syllables: Some(1),
218 parts_of_speech: Some(vec![PartOfSpeech::Noun]),
219 pronunciation: Some(String::from("K AW1 ")),
220 frequency: Some(16.567268),
221 definitions: Some(vec![
222 Definition {
223 part_of_speech: Some(PartOfSpeech::Noun),
224 definition: String::from(
225 "mature female of mammals of which the male is called `bull'",
226 ),
227 },
228 Definition {
229 part_of_speech: Some(PartOfSpeech::Noun),
230 definition: String::from("female of domestic cattle"),
231 },
232 ]),
233 };
234
235 assert_eq!(expected, actual);
236 }
237
238 #[test]
239 fn json_to_word_elem() {
240 let json = r#"
241 [
242 {
243 "word":"milk",
244 "score":2168,
245 "numSyllables":1,
246 "tags": [],
247 "defs": []
248 },
249 {
250 "word":"cow",
251 "score":2168,
252 "numSyllables":1,
253 "tags": [
254 "n",
255 "pron:K AW1 ",
256 "f:16.567268"
257 ],
258 "defs": [
259 "n\tmature female of mammals of which the male is called `bull'",
260 "n\tfemale of domestic cattle"
261 ]
262 }
263 ]
264 "#;
265
266 let actual = super::parse_response(json).unwrap();
267
268 let expected1 = WordElement {
269 word: String::from("milk"),
270 score: 2168,
271 num_syllables: Some(1),
272 parts_of_speech: None,
273 pronunciation: None,
274 frequency: None,
275 definitions: None,
276 };
277
278 let expected2 = WordElement {
279 word: String::from("cow"),
280 score: 2168,
281 num_syllables: Some(1),
282 parts_of_speech: Some(vec![PartOfSpeech::Noun]),
283 pronunciation: Some(String::from("K AW1 ")),
284 frequency: Some(16.567268),
285 definitions: Some(vec![
286 Definition {
287 part_of_speech: Some(PartOfSpeech::Noun),
288 definition: String::from(
289 "mature female of mammals of which the male is called `bull'",
290 ),
291 },
292 Definition {
293 part_of_speech: Some(PartOfSpeech::Noun),
294 definition: String::from("female of domestic cattle"),
295 },
296 ]),
297 };
298
299 assert_eq!(expected1, actual[0]);
300 assert_eq!(expected2, actual[1]);
301 }
302}