#[deny(missing_docs)]
extern crate base64;
extern crate reqwest;
extern crate serde;
extern crate snafu;
use serde::Serialize;
use snafu::{ResultExt, Snafu};
pub mod types;
pub use types::Doc;
use types::{Me, SearchRequest, SearchResponse};
pub struct Client {
pub base_uri: String,
pub token: Option<String>,
client: reqwest::Client,
}
impl Client {
pub fn new() -> Self {
Self {
base_uri: "https://trace.moe/api".into(),
client: reqwest::Client::new(),
token: None,
}
}
pub fn with_token(token: String) -> Self {
Self {
token: Some(token),
..Self::new()
}
}
pub fn search(&self, image: Vec<u8>) -> Result<SearchResponse> {
let body = SearchRequest::new(image);
let mut response = self.request(reqwest::Method::POST, "search", &body)?;
match response.status().as_u16() {
400 => Err(Error::ImageEmpty),
403 => Err(Error::InvalidToken),
413 => Err(Error::ImageTooLarge),
429 => Err(Error::RateLimit {
message: response.text().context(ResponseEmpty)?,
}),
500 | 503 => Err(Error::InternalServerError),
_ => response.json().context(JsonFailed),
}
}
pub fn me(&self) -> Result<Me> {
self.request(reqwest::Method::GET, "me", "")?
.json()
.context(JsonFailed)
}
fn request<T>(&self, method: reqwest::Method, path: &str, body: &T) -> Result<reqwest::Response>
where
T: Serialize + ?Sized,
{
let url = format!("{}/{}", self.base_uri, path);
let mut request = self.client.request(method, &url);
if let Some(token) = &self.token {
request = request.query(&[("token", token)])
}
request.json(body).send().context(RequestFailed)
}
}
#[derive(Debug, Snafu)]
pub enum Error {
RequestFailed { source: reqwest::Error },
JsonFailed { source: reqwest::Error },
ResponseEmpty { source: reqwest::Error },
ImageEmpty,
InvalidToken,
ImageTooLarge,
RateLimit { message: String },
InternalServerError,
}
pub type Result<T, E = Error> = std::result::Result<T, E>;