use crate::poly_translator::async_translator::{AsyncTranslator, Language, TranslationListOutput, TranslationOutput};
use crate::poly_translator::translator_error::TranslatorError;
use reqwest::{Client, header::REFERER};
use serde_json::Value;
pub struct MyMemoryTranslator {
input_limit: u32,
host: String,
client: Client,
}
impl Default for MyMemoryTranslator {
fn default() -> Self {
MyMemoryTranslator::new()
}
}
pub fn input_limit_checker(query: &str, input_limit: u32) -> Result<(), TranslatorError> {
if query.len() > input_limit as usize {
return Err(TranslatorError::RequestToLong(query.len() as u32, input_limit));
}
Ok(())
}
#[async_trait::async_trait]
impl AsyncTranslator for MyMemoryTranslator {
fn local(&self) -> bool {
false
}
async fn translate(
&self,
query: &str,
from: Option<Language>,
to: &Language,
) -> anyhow::Result<TranslationOutput> {
input_limit_checker(query, self.input_limit)?;
let _from_orig = from;
let _from = match _from_orig {
Some(lang) => lang.to_mymemory().ok_or(TranslatorError::UnknownLanguage(lang))?,
None => "Autodetect",
};
let url = format!(
"{}?q={}&langpair={}|{}",
self.host,
query,
_from,
to.to_mymemory().ok_or(TranslatorError::UnknownLanguage(*to))?
);
let response = self
.client
.get(&url)
.header(REFERER, "https://mymemory.translated.net")
.send()
.await?;
if !response.status().is_success() {
return Err(TranslatorError::RequestFailed(response.status().as_u16()).into());
}
let resp: Value = response.json().await?;
let resp = &resp["responseData"];
let _lang = resp["detectedLanguage"].to_string();
let mut text = resp["translatedText"].to_string();
if text == "null" {
return Err(TranslatorError::NoResponse.into());
}
if text.starts_with('"') && text.ends_with('"') {
text = text[1..text.len() - 1].to_string();
}
Ok(TranslationOutput {
text,
lang: Some(*to),
})
}
async fn translate_vec(
&self,
query: &[String],
from: Option<Language>,
to: &Language,
) -> anyhow::Result<TranslationListOutput> {
let t = self.translate(&query.join("_._._"), from, to).await?;
Ok(TranslationListOutput {
text: t
.text
.split("_._._")
.map(|s| s.to_string())
.collect::<Vec<_>>(),
lang: t.lang,
})
}
}
impl MyMemoryTranslator {
#[allow(dead_code)]
pub fn new() -> Self {
MyMemoryTranslator {
client: Client::new(),
input_limit: 500,
host: "https://api.mymemory.translated.net/get".to_string(),
}
}
}
#[cfg(test)]
mod tests {
use crate::poly_translator::async_translator::AsyncTranslator;
use crate::poly_translator::mymemory_translator::MyMemoryTranslator;
#[tokio::test]
async fn test_create_translator() {
let translator = MyMemoryTranslator::new();
assert!(!translator.local());
}
#[test]
fn test_input_limit_checker() {
use crate::poly_translator::mymemory_translator::input_limit_checker;
let result = input_limit_checker("short text", 500);
assert!(result.is_ok());
let long_text = "a".repeat(600);
let result = input_limit_checker(&long_text, 500);
assert!(result.is_err());
}
#[test]
fn test_default() {
let translator = MyMemoryTranslator::default();
assert!(!translator.local());
}
#[test]
fn test_translator_fields() {
let translator = MyMemoryTranslator::new();
assert_eq!(translator.input_limit, 500);
assert!(translator.host.contains("mymemory.translated.net"));
}
}