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
use serde::{Deserialize, Serialize};
use std::{collections::HashMap, fmt};
use reqwest::header;
use super::{Error, Result};
use crate::{DeepL, Language};
/// A glossary language pair
#[derive(Debug, Deserialize, Serialize)]
pub struct GlossaryLanguagePair {
/// Source language
pub source_lang: String,
/// Target language
pub target_lang: String,
}
/// Defines the set of supported language pairs for a glossary
#[derive(Debug, Deserialize, Serialize)]
pub struct GlossaryLanguagePairsResult {
/// List of supported glossary language pairs
pub supported_languages: Vec<GlossaryLanguagePair>,
}
/// Format in which glossary entries are provided
#[derive(Clone, Copy, Debug)]
pub enum GlossaryEntriesFormat {
/// Tab-separated values
Tsv,
/// Comma-separated values
Csv,
}
/// Information that uniquely identifies a glossary
#[derive(Debug, Deserialize, Serialize)]
pub struct Glossary {
/// A unique ID assigned to a glossary
pub glossary_id: String,
/// Indicates if the newly created glossary can already be used in translate requests.
/// If the created glossary is not yet ready, you have to wait and check the ready status
/// of the glossary before using it in a translate request.
pub ready: bool,
/// Name associated with the glossary
pub name: String,
/// The language in which the source texts in the glossary are specified
pub source_lang: String,
/// The language in which the target texts in the glossary are specified
pub target_lang: String,
/// The creation time of the glossary in ISO 8601-1:2019 format (e.g. 2021-08-03T14:16:18.329Z)
pub creation_time: String,
/// The number of entries in the glossary
pub entry_count: u64,
}
/// The result of getting available glossaries
#[derive(Debug, Deserialize, Serialize)]
pub struct GlossariesResult {
/// List of glossaries
pub glossaries: Vec<Glossary>,
}
impl AsRef<str> for GlossaryEntriesFormat {
fn as_ref(&self) -> &str {
match self {
Self::Tsv => "tsv",
Self::Csv => "csv",
}
}
}
impl fmt::Display for GlossaryEntriesFormat {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_ref())
}
}
impl DeepL {
/// GET /glossary-language-pairs
///
/// Get supported glossary language pairs
pub fn glossary_languages(&self) -> Result<GlossaryLanguagePairsResult> {
let url = format!("{}/glossary-language-pairs", self.url);
let resp = self.get(url).send().map_err(Error::Reqwest)?;
if !resp.status().is_success() {
return super::convert(resp);
}
resp.json().map_err(|_| Error::Deserialize)
}
/// POST /glossaries
///
/// Create a new glossary.
///
/// [`DeepL`] supports creating custom glossaries, i.e. a collection of entries which when
/// used in a translation ensures that a given word from the source language always maps to
/// the same target word in the glossary, giving a user more control in cases where
/// translation might otherwise be unreliable or ambiguous. A given glossary is defined by one
/// source language and one target language where the source word in each entry is unique.
///
/// ## Example
///
/// ```rust,no_run
/// # use deeprl::*;
/// # let dl = DeepL::new(&std::env::var("DEEPL_API_KEY").unwrap());
/// let name = "my_glossary".to_string();
/// let source_lang = Language::EN;
/// let target_lang = Language::IT;
/// let entries = "hello,ciao".to_string();
/// let fmt = GlossaryEntriesFormat::Csv;
///
/// let glossary = dl.glossary_new(
/// name,
/// source_lang,
/// target_lang,
/// entries,
/// fmt
/// )
/// .unwrap();
/// assert!(!glossary.glossary_id.is_empty());
/// ```
pub fn glossary_new(
&self,
name: String,
source_lang: Language,
target_lang: Language,
entries: String,
fmt: GlossaryEntriesFormat,
) -> Result<Glossary> {
let url = format!("{}/glossaries", self.url);
let params = HashMap::from([
("name", name),
("source_lang", source_lang.to_string()),
("target_lang", target_lang.to_string()),
("entries", entries),
("entries_format", fmt.to_string()),
]);
let resp = self
.post(url)
.form(¶ms)
.send()
.map_err(Error::Reqwest)?;
if !resp.status().is_success() {
return super::convert(resp);
}
resp.json().map_err(|_| Error::Deserialize)
}
/// GET /glossaries
///
/// List current active glossaries
pub fn glossaries(&self) -> Result<GlossariesResult> {
let url = format!("{}/glossaries", self.url);
let resp = self.get(url).send().map_err(Error::Reqwest)?;
if !resp.status().is_success() {
return super::convert(resp);
}
resp.json().map_err(|_| Error::Deserialize)
}
/// GET /glossaries/`{glossary_id}`
///
/// Get meta information for a specified glossary (excluding entries)
pub fn glossary_info(&self, glossary_id: &str) -> Result<Glossary> {
let url = format!("{}/glossaries/{}", self.url, glossary_id);
let resp = self.get(url).send().map_err(Error::Reqwest)?;
if !resp.status().is_success() {
return super::convert(resp);
}
resp.json().map_err(|_| Error::Deserialize)
}
/// GET /glossaries/`{glossary_id}`/entries
///
/// Retrieve entries for a specified glossary.
// Currently supports receiving entries in TSV format.
pub fn glossary_entries(&self, glossary_id: &str) -> Result<HashMap<String, String>> {
let url = format!("{}/glossaries/{}/entries", self.url, glossary_id);
let accept = header::HeaderValue::from_static("text/tab-separated-values");
let resp = self
.get(url)
.header(header::ACCEPT, accept)
.send()
.map_err(Error::Reqwest)?;
if !resp.status().is_success() {
return super::convert(resp);
}
let t = resp.text().map_err(|_| Error::InvalidResponse).unwrap();
// The response text contains newline-separated entries
// where each entry contains two strings separated by a tab.
// First we split entries on '\n', then for each entry, split words
// on '\t' and build a map of source to target words
let raw_entries: Vec<&str> = t.split('\n').collect();
let mut map = HashMap::new();
for entry in raw_entries {
let words: Vec<&str> = entry.split('\t').collect();
if words.len() != 2 {
continue;
}
map.insert(words[0].to_string(), words[1].to_string());
}
Ok(map)
}
/// DELETE /glossaries/`{glossary_id}`
///
/// Destroy a glossary
pub fn glossary_delete(&self, glossary_id: &str) -> Result<()> {
let url = format!("{}/glossaries/{}", self.url, glossary_id);
let _ = self.delete(url).send().map_err(Error::Reqwest);
Ok(())
}
}