1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
#![deny(warnings, missing_docs, missing_copy_implementations, missing_debug_implementations)]

//! This is a client that can be used to search the learngaelic.scot/dictionary Scottish Gaelic
//! Dictionary

/// Make a search using the default options
///
/// # Example
///
/// ```rust,no_run
/// # fn main() -> Result<(), faclair::Error> {
/// let search_result = faclair::search("saor")?;
/// assert_eq!(search_result[0].translation, "1 free, liberate! 2 absolve! 3 exempt!");
/// #   Ok(())
/// # }
/// ```
pub fn search(query: &str) -> Result<Vec<SearchResult>, Error> {
    search_with_options(query, Options::default())
}

/// Search for a gaelic word or phrase, providing additional options for the search
pub fn search_with_options(query: &str, options: Options) -> Result<Vec<SearchResult>, Error> {
    let language = options.language.to_string();
    Ok(ureq::post("https://learngaelic.scot/dictionary/search")
        .send_form(&[
            ("abairt", query),
            ("slang", &language[..]),
            ("wholeword", if options.whole_word { "true" } else { "false" }),
        ])?
        .into_json()?)
}

/// Possible values for the 'language' option
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Language {
    /// en
    English,
    /// gd
    Gaelic,
    /// both
    Both,
}
impl std::fmt::Display for Language {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        use Language::*;
        Ok(match self {
            English => f.write_fmt(format_args!("en"))?,
            Gaelic => f.write_fmt(format_args!("gd"))?,
            Both => f.write_fmt(format_args!("both"))?,
        })
    }
}

/// A single result of a search
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
pub struct SearchResult {
    /// Entry ID
    pub id: usize,
    /// Search result
    pub headword: String,
    /// *unknown*
    pub source: Option<String>,
    /// The translation of the search result
    pub translation: String,
    /// Grammar information
    pub grammar: String,
    /// IPA Pronunciation information
    pub ipa: String,
    /// *unknown*
    pub wordclass: String,
    #[serde(rename = "hasAudio")]
    /// Whether there is audio for the result or not
    pub has_audio: bool,
    #[serde(rename = "audioVersion")]
    /// Version of the audio
    pub audio_version: usize,
    #[serde(rename = "gdKeys")]
    /// *unknown*
    pub gd_keys: Option<String>,
    #[serde(rename = "enKeys")]
    /// *unknown*
    pub en_keys: Option<String>,
    #[serde(rename = "commitMessage")]
    /// *unknown*
    pub commit_message: Option<String>,
}


/// Additional search options
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Options {
    whole_word: bool,
    language: Language,
}
impl Options {
    /// Specify that we should search for the whole word only
    pub fn whole_word(&mut self) -> &mut Options {
        self.whole_word = true;
        self
    }
    /// Specify whether to search both english and gaelic
    pub fn language(&mut self, language: Language) -> &mut Options {
        self.language = language;
        self
    }
}
impl std::default::Default for Options {
    fn default() -> Options {
        Options {
            whole_word: false,
            language: Language::Gaelic,
        }
    }
}

/// Possible errors
#[derive(Debug, Display)]
pub enum Error {
    /// An error from the ureq http client library
    Ureq(ureq::Error),
    /// An error from the standard library's IO module
    Io(std::io::Error),
}
impl std::error::Error for Error {}
impl From<std::io::Error> for Error {
    fn from(e: std::io::Error) -> Error {
        Error::Io(e)
    }
}
impl From<ureq::Error> for Error {
    fn from(e: ureq::Error) -> Error {
        Error::Ureq(e)
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
        let result = super::search("saor").expect("Problem when searching for 'saor'");
        assert_eq!(result[0].translation, "1 free, liberate! 2 absolve! 3 exempt!");
    }
}

use derive_more::Display;
use serde::{Deserialize, Serialize};