rusty_booru/generic/
client.rs1use strum::EnumIter;
2
3use crate::{
4 danbooru::client::DanbooruClient,
5 gelbooru::client::GelbooruClient,
6 generic::AutoCompleteItem,
7 safebooru::client::SafebooruClient,
8 shared::{
9 self,
10 client::{
11 ClientInformation, ClientQueryBuilder, ClientQueryDispatcher, ClientTypes,
12 QueryDispatcher, WithClientBuilder,
13 },
14 Tag,
15 },
16};
17
18use super::{BooruPost, Rating};
19
20#[derive(Debug, Clone)]
21pub struct GenericClient();
22
23impl ClientTypes for GenericClient {
24 type Rating = Rating;
25 type Post = BooruPost;
26}
27
28#[derive(EnumIter)]
29pub enum BooruOption {
30 Gelbooru,
31 Safebooru,
32 Danbooru,
33}
34
35impl<T: ClientTypes> From<&Tag<GenericClient>> for Tag<T> {
36 fn from(val: &Tag<GenericClient>) -> Self {
37 match val {
38 Tag::Plain(s) => Tag::Plain(s.clone()),
39 Tag::Blacklist(s) => Tag::Blacklist(s.clone()),
40 Tag::Rating(s) => Tag::Rating(T::Rating::from(s.clone())),
41 Tag::Sort(s) => Tag::Sort(s.clone()),
42 }
43 }
44}
45
46macro_rules! handle_request {
47 (@ $t:ident, ($($args:expr),*), ($($gen:ty),*)) => {
48 request::<$t, $($gen,)*>($($args,)*).await
49 };
50
51 ($booru_option:expr, ($($args:expr),*), ($($gen:ty),*)) => {
52 match $booru_option {
53 BooruOption::Gelbooru => handle_request!(@ GelbooruClient, ($($args),*), ($($gen),*)),
54 BooruOption::Safebooru => handle_request!(@ SafebooruClient, ($($args),*), ($($gen),*)),
55 BooruOption::Danbooru => handle_request!(@ DanbooruClient, ($($args),*), ($($gen),*)),
56 }
57 };
58
59 ($booru_option:expr, ($($args:expr),*)) => {
60 handle_request!($booru_option, ($($args),*), ())
61 }
62}
63
64impl ClientQueryBuilder<GenericClient> {
65 fn convert<T: ClientTypes + ClientInformation + Clone>(&self) -> ClientQueryBuilder<T> {
66 let mut query = ClientQueryBuilder::new();
67
68 for tag in self.tags.0.iter() {
69 query.tag::<Tag<T>>(tag.into());
70 }
71
72 query
73 }
74
75 pub async fn get_autocomplete<In: Into<String> + Send>(
76 &self,
77 booru: BooruOption,
78 input: In,
79 ) -> Result<Vec<AutoCompleteItem>, reqwest::Error> {
80 async fn request<
81 T: ClientTypes + ClientInformation + WithClientBuilder<T> + Clone,
82 In: Into<String> + Send,
83 >(
84 query: &ClientQueryBuilder<GenericClient>,
85 input: In,
86 ) -> Result<Vec<AutoCompleteItem>, reqwest::Error>
87 where
88 ClientQueryDispatcher<T>: QueryDispatcher<T>,
89 {
90 T::builder()
91 .query_raw(&mut query.convert())
92 .get_autocomplete(input)
93 .await
94 .map_err(Into::into)
95 }
96
97 handle_request!(booru, (self, input), (In))
98 }
99
100 pub async fn get_by_id(
101 &self,
102 id: u32,
103 booru: BooruOption,
104 ) -> Result<Option<BooruPost>, shared::Error> {
105 async fn request<T: ClientTypes + ClientInformation + WithClientBuilder<T> + Clone>(
106 query: &ClientQueryBuilder<GenericClient>,
107 id: u32,
108 ) -> Result<Option<BooruPost>, shared::Error>
109 where
110 ClientQueryDispatcher<T>: QueryDispatcher<T>,
111 {
112 T::builder()
113 .query_raw(&mut query.convert())
114 .get_by_id(id)
115 .await
116 .map(|v| v.map(Into::into))
117 .map_err(Into::into)
118 }
119
120 handle_request!(booru, (self, id))
121 }
122
123 pub async fn get(&self, booru: BooruOption) -> Result<Vec<BooruPost>, shared::Error> {
124 async fn request<T: ClientTypes + ClientInformation + WithClientBuilder<T> + Clone>(
125 query: &ClientQueryBuilder<GenericClient>,
126 ) -> Result<Vec<BooruPost>, shared::Error>
127 where
128 ClientQueryDispatcher<T>: QueryDispatcher<T>,
129 {
130 T::builder()
131 .query_raw(&mut query.convert())
132 .get()
133 .await
134 .map_err(Into::into)
135 .map(|v| v.into_iter().map(Into::into).collect())
136 }
137
138 handle_request!(booru, (self))
139 }
140}
141
142impl GenericClient {
143 pub fn query() -> ClientQueryBuilder<GenericClient> {
144 ClientQueryBuilder::new()
145 }
146}