datamuse_api_wrapper/
response.rs

1use crate::Result;
2use serde::Deserialize;
3
4/// This struct represents each word and its associated data in the response.
5/// It is constructed when parsing a [Response](Response) with the method list().
6/// Note that all optional values can still be None even if the proper flag
7/// is set
8#[derive(Debug, PartialEq)]
9pub struct WordElement {
10    /// The word returned based on the search parameters
11    pub word: String,
12    /// A score which ranks the word based on how well it fit the provided parameters.
13    /// Note that by default the words are ranked by score from highest to lowest
14    pub score: usize,
15    /// The number of syllables the word has. This will only have a value if
16    /// the meta data flag [SyllableCount](crate::MetaDataFlag::SyllableCount) is set
17    pub num_syllables: Option<usize>,
18    /// The part(s) of speech a word can be. This will only have a value if
19    /// the meta data flag [PartsOfSpeech](crate::MetaDataFlag::PartsOfSpeech) is set
20    pub parts_of_speech: Option<Vec<PartOfSpeech>>,
21    /// The pronunciation of the word. This will only have a value if
22    /// the meta data flag [Pronunciation](crate::MetaDataFlag::Pronunciation) is set.
23    /// If an IPA pronuncation is available, it takes precedence as it is optional
24    pub pronunciation: Option<String>,
25    /// The frequency of a word based on how many times the word is used per 1,000,000
26    /// words of text. This will only have a value if the meta data flag
27    /// [WordFrequency](crate::MetaDataFlag::WordFrequency) is set
28    pub frequency: Option<f32>,
29    /// Definitions of a word and the associated part of speech with its use. This will only
30    /// have a value if the meta data flag [Definitions](crate::MetaDataFlag::Definitions) is set
31    pub definitions: Option<Vec<Definition>>,
32}
33
34/// A struct representing a word definition
35#[derive(Debug, PartialEq)]
36pub struct Definition {
37    /// The part of speech associated with the definition
38    pub part_of_speech: Option<PartOfSpeech>,
39    /// The definition itself
40    pub definition: String,
41}
42
43/// A struct representing a response from a request.
44/// This can be parsed into a word list using the list() method
45#[derive(Debug)]
46pub struct Response {
47    json: String,
48}
49
50/// An enum representing all possible parts of speech returned from the api
51#[derive(Clone, Copy, Debug, PartialEq)]
52pub enum PartOfSpeech {
53    /// Noun
54    Noun, //n
55    /// Adjective
56    Adjective, //adj
57    /// Adverb
58    Adverb, //adv
59    /// Verb
60    Verb, //v
61}
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    /// Parses the response into a list of word elements
75    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, //Also catches undefined option "u"
92        }
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 pronunciation already has a value ignore b/c of ipa
132                        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}