rusty_booru/danbooru/
client.rs1use derive_more::From;
2use reqwest::{header, header::HeaderMap, Response};
3
4use super::*;
5use crate::{
6 generic::AutoCompleteItem,
7 shared::{self, client::*},
8};
9
10pub fn get_headers() -> HeaderMap {
12 let mut headers = header::HeaderMap::new();
13 headers.insert(
14 header::USER_AGENT,
15 header::HeaderValue::from_static("PostmanRuntime/7.30.0"),
16 );
17 headers
18}
19
20#[derive(From, Debug, Clone)]
22pub struct DanbooruClient(pub ClientBuilder<Self>);
23
24impl ClientInformation for DanbooruClient {
25 const URL: &'static str = "https://danbooru.donmai.us";
26 const SORT: &'static str = "order:";
27}
28
29impl ClientTypes for DanbooruClient {
30 type Rating = DanbooruRating;
31 type Post = DanbooruPost;
32}
33
34#[derive(Deserialize, Debug, thiserror::Error, Display)]
35pub enum DanbooruError {
36 #[serde(rename = "PostQuery::TagLimitError")]
37 TagLimitError,
38}
39
40#[derive(Deserialize)]
41struct DanbooruErrorStruct {
42 pub error: DanbooruError,
43}
44
45impl From<DanbooruErrorStruct> for shared::Error {
46 fn from(value: DanbooruErrorStruct) -> Self {
47 value.error.into()
48 }
49}
50
51async fn send_error<T>(response: Response) -> Result<T, shared::Error> {
52 Err(response
53 .json::<DanbooruErrorStruct>()
54 .await
55 .map(Into::into)?)
56}
57
58impl QueryDispatcher<DanbooruClient> for ClientQueryDispatcher<DanbooruClient> {
59 async fn get_autocomplete<In: Into<String> + Send>(
60 &self,
61 input: In,
62 ) -> Result<Vec<AutoCompleteItem>, reqwest::Error> {
63 self.builder
64 .client
65 .get(format!("{}/autocomplete.json", self.builder.url))
66 .headers(get_headers())
67 .query(&[
68 ("limit", self.query.limit.to_string().as_str()),
69 ("search[type]", "tag_query"),
70 ("search[query]", &input.into()),
71 ("version", "1"),
72 ])
73 .send()
74 .await?
75 .json::<Vec<AutoCompleteItem>>()
76 .await
77 }
78
79 async fn get_by_id(&self, id: u32) -> Result<Option<DanbooruPost>, shared::Error> {
80 let response = self
81 .builder
82 .client
83 .get(format!("{}/posts/{id}.json", self.builder.url))
84 .headers(get_headers())
85 .send()
86 .await?;
87
88 if response.status().is_success() {
89 response
90 .json::<DanbooruPost>()
91 .await
92 .map(Into::into)
93 .map_err(Into::into)
94 } else {
95 send_error(response).await?
96 }
97 }
98
99 async fn get(&self) -> Result<Vec<DanbooruPost>, shared::Error> {
100 let response = self
101 .builder
102 .client
103 .get(format!("{}/posts.json", self.builder.url))
104 .headers(get_headers())
105 .query(&[
106 ("limit", &self.query.limit.to_string()),
107 ("tags", &self.query.tags.unpack()),
108 ])
109 .send()
110 .await?;
111
112 if response.status().is_success() {
113 Ok(response.json::<Vec<DanbooruPost>>().await?)
114 } else {
115 send_error(response).await?
116 }
117 }
118}