rusty_booru/generic/
client.rs

1use 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}