use crate::Limit;
use serde::{Deserialize, Serialize};
use std::borrow::Cow;
pub const URL: &str = "https://fanyi-api.baidu.com/api/trans/vip/translate";
#[derive(Debug)]
pub struct Query<'q> {
pub q: &'q str,
pub from: &'q str,
pub to: &'q str,
pub sign: String,
}
#[derive(Debug, Deserialize)]
#[serde(rename = "baidu")] pub struct User {
pub appid: String,
pub key: String,
#[serde(default = "default_salt")]
pub salt: String,
#[serde(default = "default_qps")]
pub qps: u8,
#[serde(default = "default_limit")]
pub limit: Limit,
}
fn default_qps() -> u8 { 1 }
fn default_salt() -> String { String::from("0") }
fn default_limit() -> Limit { Limit::Byte(6000) }
impl Default for User {
fn default() -> Self {
Self { appid: String::new(),
key: String::new(),
salt: default_salt(),
qps: default_qps(),
limit: default_limit(), }
}
}
impl<'q> Query<'q> {
pub fn new(q: &'q str, from: &'q str, to: &'q str) -> Self {
Self { q, from, to, sign: "".into() }
}
pub fn sign<'f>(&'f mut self, user: &'f User) -> Form<'f> {
let data = format!("{}{}{}{}", &user.appid, self.q, &user.salt, &user.key);
self.sign = format!("{:x}", md5::compute(data));
Form::from_user_query(user, self)
}
}
#[derive(Debug, Serialize)]
pub struct Form<'f> {
pub q: &'f str,
pub from: &'f str,
pub to: &'f str,
pub appid: &'f str,
pub salt: &'f str,
pub sign: &'f str,
}
impl<'f> Form<'f> {
pub fn from_user_query(user: &'f User, query: &'f Query) -> Self {
Self { q: query.q,
from: query.from,
to: query.to,
appid: &user.appid,
salt: &user.salt,
sign: &query.sign, }
}
}
#[derive(Debug, Deserialize)]
#[serde(untagged)]
pub enum Response<'r> {
Ok {
from: &'r str,
to: &'r str,
#[serde(rename = "trans_result")]
#[serde(borrow)]
res: Vec<SrcDst<'r>>,
},
Err(Error),
}
impl<'r> Response<'r> {
pub fn dst(&self) -> Result<impl Iterator<Item = &str>, Error> {
match self {
Response::Ok { res, .. } => Ok(res.iter().map(|x| x.dst.as_ref())),
Response::Err(e) => Err(e.clone()),
}
}
pub fn dst_owned(self) -> Result<Vec<String>, Error> {
match self {
Response::Ok { res, .. } => Ok(res.into_iter().map(|x| x.dst.into()).collect()),
Response::Err(e) => Err(e),
}
}
pub fn is_borrowed(&self) -> Option<bool> {
match self {
Response::Ok { res, .. } if !res.is_empty() => {
Some(matches!(res[0].dst, Cow::Borrowed(_)))
}
_ => None,
}
}
}
#[derive(Debug, Deserialize)]
pub struct SrcDst<'r> {
#[serde(borrow)]
pub dst: Cow<'r, str>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct Error {
#[serde(rename = "error_code")]
pub code: String,
#[serde(rename = "error_msg")]
pub msg: String,
}
impl std::error::Error for Error {}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f,
"错误码:`{}`\n错误信息:`{}`\n错误含义:{}\n以上内容由百度翻译 API 返回",
self.code,
self.msg,
self.solution())
}
}
impl Error {
pub fn solution(&self) -> &str {
match self.code.as_bytes() {
b"52000" => "成功。",
b"52001" => "请求超时。\n解决方法:请重试。",
b"52002" => "系统错误。\n解决方法:请重试。",
b"52003" => "未授权用户。\n解决方法:请检查appid是否正确或者服务是否开通。",
b"54000" => "必填参数为空。\n解决方法:请检查是否少传参数。",
b"54001" => "签名错误。\n解决方法:请检查您的签名生成方法。",
b"54003" => {
"访问频率受限。\n解决方法:请降低您的调用频率,或进行身份认证后切换为高级版/\
尊享版。"
}
b"54004" => "账户余额不足。\n解决方法:请前往管理控制台为账户充值。",
b"54005" => "长 query 请求频繁。\n解决方法:请降低长 query 的发送频率,3s后再试。",
b"58000" => {
"客户端 IP 非法。\n解决方法:检查个人资料里填写的 IP \
地址是否正确,可前往开发者信息-基本信息修改。"
}
b"58001" => "译文语言方向不支持。\n解决方法:检查译文语言是否在语言列表里。",
b"58002" => "服务当前已关闭。\n解决方法:请前往管理控制台开启服务。",
b"90107" => "认证未通过或未生效。\n解决方法:请前往我的认证查看认证进度。",
_ => "未知错误。",
}
}
}