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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
use serde::{Deserialize, Serialize};
use std::fmt;
use std::str::FromStr;

use super::{Error, Result};
use crate::DeepL;

/// Language type. Note: this is currently only used when fetching language meta information.
#[derive(Copy, Clone, Debug)]
pub enum LanguageType {
    /// Source language
    Source,
    /// Target language
    Target,
}

/// Information about a supported language
#[derive(Debug, Deserialize, Serialize)]
pub struct LanguageInfo {
    /// Language code (EN, DE, etc.)
    pub language: String,
    /// Name of the language in English
    pub name: String,
    /// Denotes formality support in case of target language
    #[serde(skip_serializing_if = "Option::is_none")]
    pub supports_formality: Option<bool>,
}

impl Default for LanguageInfo {
    /// Provides serde with a default value for `supports_formality`, since
    /// the field is only returned for target lang (not source).
    /// [deepl-openapi docs](https://docs.rs/deepl-openapi/2.7.1/src/deepl_openapi/models/get_languages_200_response_inner.rs.html)
    //
    // note: can we just derive Default?
    fn default() -> Self {
        Self {
            language: String::default(),
            name: String::default(),
            supports_formality: None,
        }
    }
}

/// Language variants.
///
/// Please note that while many [`Language`] variants are interchangeable as both source and
/// target languages, there are exceptions. For example when translating text and documents,
/// the following may only be used as source languages:
/// - `EN`
/// - `PT`
///
/// and the following may only be used as target languages (representing regional variants):
/// - `ENUS`
/// - `ENGB`
/// - `PTBR`
/// - `PTPT`
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum Language {
    /// Bulgarian
    BG,
    /// Czech
    CS,
    /// Danish
    DA,
    /// German
    DE,
    /// Greek
    EL,
    /// English (source language)
    EN,
    /// English British (target language)
    ENGB,
    /// English American (target language)
    ENUS,
    /// Spanish
    ES,
    /// Estonian
    ET,
    /// Finish
    FI,
    /// French
    FR,
    /// Hungarian
    HU,
    /// Indonesian
    ID,
    /// Italian
    IT,
    /// Japanese
    JA,
    /// Korean
    KO,
    /// Lithuanian
    LT,
    /// Latvian
    LV,
    /// Norwegian
    NB,
    /// Dutch
    NL,
    /// Polish
    PL,
    /// Portuguese (source language)
    PT,
    /// Portuguese Brazilian (target language)
    PTBR,
    /// Portuguese European (target language)
    PTPT,
    /// Romanian
    RO,
    /// Russian
    RU,
    /// Slovak
    SK,
    /// Slovenian
    SL,
    /// Swedish
    SV,
    /// Turkish
    TR,
    /// Ukranian
    UK,
    /// Chinese simplified
    ZH,
}

impl FromStr for Language {
    type Err = Error;

    /// # Errors
    ///
    /// If a [`Language`] cannot be parsed from the input `s`
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let lang = match s.to_uppercase().as_str() {
            "BG" => Language::BG,
            "CS" => Language::CS,
            "DA" => Language::DA,
            "DE" => Language::DE,
            "EL" => Language::EL,
            "EN" => Language::EN,
            "EN-GB" => Language::ENGB,
            "EN-US" => Language::ENUS,
            "ES" => Language::ES,
            "ET" => Language::ET,
            "FI" => Language::FI,
            "FR" => Language::FR,
            "HU" => Language::HU,
            "ID" => Language::ID,
            "IT" => Language::IT,
            "JA" => Language::JA,
            "KO" => Language::KO,
            "LT" => Language::LT,
            "LV" => Language::LV,
            "NB" => Language::NB,
            "NL" => Language::NL,
            "PL" => Language::PL,
            "PT" => Language::PT,
            "PT-BR" => Language::PTBR,
            "PT-PT" => Language::PTPT,
            "RO" => Language::RO,
            "RU" => Language::RU,
            "SK" => Language::SK,
            "SL" => Language::SL,
            "SV" => Language::SV,
            "TR" => Language::TR,
            "UK" => Language::UK,
            "ZH" => Language::ZH,
            _ => return Err(Error::InvalidLanguage),
        };

        Ok(lang)
    }
}

impl AsRef<str> for Language {
    fn as_ref(&self) -> &str {
        match self {
            Self::BG => "BG",
            Self::CS => "CS",
            Self::DA => "DA",
            Self::DE => "DE",
            Self::EL => "EL",
            Self::EN => "EN",
            Self::ENGB => "EN-GB",
            Self::ENUS => "EN-US",
            Self::ES => "ES",
            Self::ET => "ET",
            Self::FI => "FI",
            Self::FR => "FR",
            Self::HU => "HU",
            Self::ID => "ID",
            Self::IT => "IT",
            Self::JA => "JA",
            Self::KO => "KO",
            Self::LT => "LT",
            Self::LV => "LV",
            Self::NB => "NB",
            Self::NL => "NL",
            Self::PL => "PL",
            Self::PT => "PT",
            Self::PTBR => "PT-BR",
            Self::PTPT => "PT-PT",
            Self::RO => "RO",
            Self::RU => "RU",
            Self::SK => "SK",
            Self::SL => "SL",
            Self::SV => "SV",
            Self::TR => "TR",
            Self::UK => "UK",
            Self::ZH => "ZH",
        }
    }
}

impl fmt::Display for Language {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.as_ref())
    }
}

impl DeepL {
    /// GET /languages
    ///
    /// Get information on supported languages.
    ///
    /// ## Example
    ///
    /// ```rust,no_run
    /// # use deeprl::{DeepL, LanguageType};
    /// # let dl = DeepL::new(&std::env::var("DEEPL_API_KEY").unwrap());
    /// let source_langs = dl.languages(LanguageType::Source).unwrap();
    /// assert!(!source_langs.is_empty());
    ///
    /// let language = &source_langs[0];
    /// println!("{}", language.language); // BG
    /// println!("{}", language.name); // Bulgarian
    ///```
    pub fn languages(&self, lang_type: LanguageType) -> Result<Vec<LanguageInfo>> {
        let url = format!("{}/languages", self.url);

        let kind = match lang_type {
            LanguageType::Source => "source",
            LanguageType::Target => "target",
        };

        // get, query "type"
        let q = vec![("type", kind)];

        let resp = self.get(url).query(&q).send().map_err(Error::Reqwest)?;

        if !resp.status().is_success() {
            return super::convert(resp);
        }

        resp.json().map_err(|_| Error::Deserialize)
    }
}