use crate::Result;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
pub(crate) struct Response {
pub image: Image,
}
#[derive(Serialize, Deserialize)]
pub(crate) struct ResponseList {
pub images: Vec<Image>,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ImageMeta {
pub description: String,
pub tag_input: String,
pub source_url: String,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Image {
pub name: String,
pub faves: i64,
pub format: String,
pub updated_at: String,
pub downvotes: i64,
pub duplicate_of: Option<u64>,
pub tag_count: i64,
pub spoilered: Option<bool>,
pub uploader: Option<String>,
pub deletion_reason: Option<String>,
pub width: i64,
pub processed: bool,
pub created_at: String,
pub orig_sha512_hash: String,
pub view_url: String,
pub uploader_id: Option<i64>,
pub intensities: Option<Intensities>,
pub score: i64,
pub height: i64,
pub mime_type: String,
pub tag_ids: Vec<i64>,
pub wilson_score: f64,
pub first_seen_at: String,
pub tags: Vec<String>,
pub id: i64,
pub upvotes: i64,
pub comment_count: i64,
pub representations: Option<Representations>,
pub thumbnails_generated: bool,
pub aspect_ratio: f64,
pub hidden_from_users: bool,
pub sha512_hash: String,
pub source_url: Option<String>,
pub description: String,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Intensities {
pub ne: f64,
pub nw: f64,
pub se: f64,
pub sw: f64,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Representations {
pub full: String,
pub large: String,
pub medium: String,
pub small: String,
pub tall: String,
pub thumb: String,
pub thumb_small: String,
pub thumb_tiny: String,
}
impl crate::Client {
pub async fn featured_image(&self) -> Result<Image> {
let resp: Response = self
.request(reqwest::Method::GET, "api/v1/json/images/featured")
.send()
.await?
.error_for_status()?
.json()
.await?;
Ok(resp.image)
}
pub async fn image(&self, id: u64) -> Result<Image> {
let resp: Response = self
.request(reqwest::Method::GET, &format!("api/v1/json/images/{}", id))
.send()
.await?
.error_for_status()?
.json()
.await?;
Ok(resp.image)
}
pub async fn image_search<T: Into<String>>(&self, q: T, page: u64) -> Result<Vec<Image>> {
let mut req = self
.request(reqwest::Method::GET, &format!("api/v1/json/search/images"))
.query(&[("q", q.into())]);
if page != 0 {
req = req.query(&[("page", format!("{}", page))]);
}
let resp: ResponseList = req.send().await?.error_for_status()?.json().await?;
Ok(resp.images)
}
pub async fn post_image(&self, image_url: String, im: ImageMeta) -> Result<Image> {
self.cli.get(&image_url).send().await?.error_for_status()?;
#[derive(Serialize)]
struct CreateImage {
url: String,
image: ImageMeta,
}
let resp = self
.request(reqwest::Method::POST, "api/v1/json/images")
.json(&CreateImage {
url: image_url,
image: im,
})
.send()
.await?;
match (&resp).error_for_status_ref() {
Ok(_) => {
let bytes = resp.bytes().await?;
log::debug!("body: {}", std::str::from_utf8(&bytes)?);
Ok(serde_json::from_slice::<Response>(&bytes)?.image)
}
Err(why) => {
log::debug!("error: {:?}", why);
log::debug!("body: {}", resp.text().await?);
Err(why.into())
}
}
}
}
#[cfg(test)]
mod tests {
use httptest::{matchers::*, responders::*, Expectation, Server};
#[tokio::test]
async fn featured_image() {
let _ = pretty_env_logger::try_init();
let data: serde_json::Value =
serde_json::from_slice(include_bytes!("../testdata/featured.json")).unwrap();
let server = Server::run();
server.expect(
Expectation::matching(request::method_path("GET", "/api/v1/json/images/featured"))
.respond_with(json_encoded(data)),
);
let cli =
crate::Client::with_baseurl("test", "42069", &format!("{}", server.url("/"))).unwrap();
cli.featured_image().await.unwrap();
}
#[tokio::test]
async fn image() {
let _ = pretty_env_logger::try_init();
let data: serde_json::Value =
serde_json::from_slice(include_bytes!("../testdata/image_2336.json")).unwrap();
let server = Server::run();
server.expect(
Expectation::matching(request::method_path("GET", "/api/v1/json/images/2336"))
.respond_with(json_encoded(data)),
);
let cli =
crate::Client::with_baseurl("test", "42069", &format!("{}", server.url("/"))).unwrap();
cli.image(2336).await.unwrap();
}
#[tokio::test]
async fn image_search() {
let _ = pretty_env_logger::try_init();
let data: serde_json::Value =
serde_json::from_slice(include_bytes!("../testdata/search_images.json")).unwrap();
let server = Server::run();
server.expect(
Expectation::matching(request::method_path("GET", "/api/v1/json/search/images"))
.respond_with(json_encoded(data)),
);
let cli =
crate::Client::with_baseurl("test", "42069", &format!("{}", server.url("/"))).unwrap();
cli.image_search("orca", 0).await.unwrap();
}
}