use crate::conf::AppConfig;
use crate::fancy_egg::{EGG_CODE, decrypt};
use crate::fyerrcodes::query_msg;
use md5;
use rand;
use serde::Deserialize;
use serde_json;
use std::collections::HashMap;
use std::str::FromStr;
use ureq;
const HOST: &str = "https://fanyi-api.baidu.com/api/trans/vip/translate";
#[derive(Deserialize, Debug)]
#[allow(dead_code)]
struct TranslationResponse {
from: String,
to: String,
trans_result: Vec<TranslationItem>,
}
#[derive(Deserialize, Debug)]
#[allow(dead_code)]
struct TranslationItem {
src: String,
dst: String,
}
#[derive(Deserialize, Debug)]
#[allow(dead_code)]
struct ErrorResponse {
error_code: serde_json::Value,
error_msg: String,
}
fn calculate_sign(appid: &str, q: &str, salt: &str, key: &str) -> String {
let sign_str = format!("{}{}{}{}", appid, q, salt, key);
format!("{:x}", md5::compute(sign_str.as_bytes()))
}
fn send_response(
appid: &str,
from: &str,
to: &str,
q: &str,
app_config: &AppConfig,
) -> Result<String, Box<dyn std::error::Error>> {
let salt = rand::random::<u32>().to_string();
let sign = calculate_sign(appid, q, &salt, &app_config.key);
let mut params = HashMap::new();
params.insert("appid", appid);
params.insert("from", from);
params.insert("to", to);
params.insert("q", q);
params.insert("salt", &salt);
params.insert("sign", &sign);
let response = ureq::post(HOST)
.header("Content-Type", "application/x-www-form-urlencoded")
.send_form(¶ms)?;
let response_text = response.into_body().read_to_string()?;
Ok(response_text)
}
fn patch_raw(content: String) -> Result<String, String> {
let parsed_response = serde_json::from_str::<TranslationResponse>(&content)
.map_err(|e| format!(":( Failed to parse successful response: {}", e))?;
if !parsed_response.trans_result.is_empty() {
Ok(parsed_response.trans_result[0].dst.clone())
} else {
Err(":( Response contains no translation results".to_string())
}
}
fn patch_error(body_content: String) -> Result<ErrorResponse, String> {
serde_json::from_str::<ErrorResponse>(&body_content)
.map_err(|e| format!(":( Failed to parse error response: {}", e))
}
pub fn translate(
appid: &str,
from: &str,
to: &str,
q: &str,
app_config: AppConfig,
) -> Result<String, String> {
if q.trim().eq_ignore_ascii_case("QAS") {
let egg: String = decrypt(EGG_CODE);
return Ok(egg);
}
std::thread::sleep(std::time::Duration::from_millis(100));
match send_response(appid, from, to, q, &app_config) {
Err(e) => {
return Err(format!(":( error sending request: {}", e.to_string()));
}
Ok(response_body) => {
if response_body.contains("error_msg") {
match patch_error(response_body) {
Ok(err_resp) => {
let errcode = match err_resp.error_code.as_u64() {
Some(code) => code as usize,
None => {
match err_resp.error_code.as_str() {
Some(code_str) => match usize::from_str(code_str) {
Ok(code) => code,
Err(_) => {
return Err(":( failed to parse error code".to_string());
}
},
None => return Err(":( failed to parse error code".to_string()),
}
}
};
return Err(format!(
":( We asked, but server said: {}",
query_msg(errcode)
));
}
Err(e) => return Err(e),
}
} else {
return patch_raw(response_body);
}
}
}
}