1use crate::conf::AppConfig;
8use crate::fancy_egg::{EGG_CODE, decrypt};
9use crate::fyerrcodes::query_msg;
10use md5;
11use rand;
12use serde::Deserialize;
13use serde_json;
14use std::collections::HashMap;
15use std::str::FromStr;
16use ureq;
17
18const HOST: &str = "https://fanyi-api.baidu.com/api/trans/vip/translate";
20
21#[derive(Deserialize, Debug)]
24#[allow(dead_code)]
25struct TranslationResponse {
26 from: String,
27 to: String,
28 trans_result: Vec<TranslationItem>,
29}
30
31#[derive(Deserialize, Debug)]
32#[allow(dead_code)]
33struct TranslationItem {
34 src: String,
35 dst: String,
36}
37
38#[derive(Deserialize, Debug)]
40#[allow(dead_code)]
41struct ErrorResponse {
42 error_code: serde_json::Value,
43 error_msg: String,
44}
45
46fn calculate_sign(appid: &str, q: &str, salt: &str, key: &str) -> String {
47 let sign_str = format!("{}{}{}{}", appid, q, salt, key);
48 format!("{:x}", md5::compute(sign_str.as_bytes()))
49}
50
51fn send_response(
52 appid: &str,
53 from: &str,
54 to: &str,
55 q: &str,
56 app_config: &AppConfig,
57) -> Result<String, Box<dyn std::error::Error>> {
58 let salt = rand::random::<u32>().to_string();
59 let sign = calculate_sign(appid, q, &salt, &app_config.key);
60
61 let mut params = HashMap::new();
62 params.insert("appid", appid);
63 params.insert("from", from);
64 params.insert("to", to);
65 params.insert("q", q);
66 params.insert("salt", &salt);
67 params.insert("sign", &sign);
68
69 let response = ureq::post(HOST)
70 .header("Content-Type", "application/x-www-form-urlencoded")
71 .send_form(¶ms)?;
72
73 let response_text = response.into_body().read_to_string()?;
74 Ok(response_text)
75}
76
77fn patch_raw(content: String) -> Result<String, String> {
79 let parsed_response = serde_json::from_str::<TranslationResponse>(&content)
80 .map_err(|e| format!(":( Failed to parse successful response: {}", e))?;
81
82 if !parsed_response.trans_result.is_empty() {
83 Ok(parsed_response.trans_result[0].dst.clone())
84 } else {
85 Err(":( Response contains no translation results".to_string())
86 }
87}
88
89fn patch_error(body_content: String) -> Result<ErrorResponse, String> {
91 serde_json::from_str::<ErrorResponse>(&body_content)
92 .map_err(|e| format!(":( Failed to parse error response: {}", e))
93}
94
95pub fn translate(
97 appid: &str,
98 from: &str,
99 to: &str,
100 q: &str,
101 app_config: AppConfig,
102) -> Result<String, String> {
103 if q.trim().eq_ignore_ascii_case("QAS") {
105 let egg: String = decrypt(EGG_CODE);
106 return Ok(egg);
107 }
108
109 std::thread::sleep(std::time::Duration::from_millis(100));
111
112 match send_response(appid, from, to, q, &app_config) {
114 Err(e) => {
115 return Err(format!(":( error sending request: {}", e.to_string()));
116 }
117 Ok(response_body) => {
118 if response_body.contains("error_msg") {
120 match patch_error(response_body) {
122 Ok(err_resp) => {
123 let errcode = match err_resp.error_code.as_u64() {
125 Some(code) => code as usize,
126 None => {
127 match err_resp.error_code.as_str() {
129 Some(code_str) => match usize::from_str(code_str) {
130 Ok(code) => code,
131 Err(_) => {
132 return Err(":( failed to parse error code".to_string());
133 }
134 },
135 None => return Err(":( failed to parse error code".to_string()),
136 }
137 }
138 };
139
140 return Err(format!(
142 ":( We asked, but server said: {}",
143 query_msg(errcode)
144 ));
145 }
146 Err(e) => return Err(e),
147 }
148 } else {
149 return patch_raw(response_body);
151 }
152 }
153 }
154}