booru_rs/client/
danbooru.rs1use super::{Client, ClientBuilder, shared_client};
4use crate::autocomplete::{Autocomplete, TagSuggestion};
5use crate::error::Result;
6use crate::model::danbooru::*;
7
8use reqwest::header::{self, HeaderMap, HeaderValue};
9use serde::Deserialize;
10
11fn get_headers() -> HeaderMap {
15 let mut headers = HeaderMap::with_capacity(1);
16 headers.insert(
17 header::USER_AGENT,
18 HeaderValue::from_static("booru-rs/0.3.0"),
19 );
20 headers
21}
22
23#[derive(Debug)]
47pub struct DanbooruClient(ClientBuilder<Self>);
48
49impl From<ClientBuilder<Self>> for DanbooruClient {
50 fn from(value: ClientBuilder<Self>) -> Self {
51 Self(value)
52 }
53}
54
55impl Client for DanbooruClient {
56 type Post = DanbooruPost;
57 type Rating = DanbooruRating;
58
59 const URL: &'static str = "https://danbooru.donmai.us";
60 const SORT: &'static str = "order:";
61 const MAX_TAGS: Option<usize> = Some(2);
62
63 async fn get_by_id(&self, id: u32) -> Result<Self::Post> {
69 let builder = &self.0;
70 let url = &builder.url;
71
72 let response = builder
73 .client
74 .get(format!("{url}/posts/{id}.json"))
75 .headers(get_headers())
76 .send()
77 .await?
78 .json::<DanbooruPost>()
79 .await?;
80
81 Ok(response)
82 }
83
84 async fn get(&self) -> Result<Vec<Self::Post>> {
90 let builder = &self.0;
91 let tag_string = builder.tags.join(" ");
92 let url = &builder.url;
93
94 let response = builder
95 .client
96 .get(format!("{url}/posts.json"))
97 .headers(get_headers())
98 .query(&[
99 ("limit", builder.limit.to_string()),
100 ("page", builder.page.to_string()),
101 ("tags", tag_string),
102 ])
103 .send()
104 .await?
105 .json::<Vec<DanbooruPost>>()
106 .await?;
107
108 Ok(response)
109 }
110}
111
112#[derive(Debug, Deserialize)]
114struct DanbooruAutocompleteItem {
115 value: String,
116 label: String,
117 category: Option<u8>,
118 post_count: Option<u32>,
119}
120
121impl Autocomplete for DanbooruClient {
122 async fn autocomplete(query: &str, limit: u32) -> Result<Vec<TagSuggestion>> {
139 let response = shared_client()
140 .get(format!("{}/autocomplete.json", Self::URL))
141 .headers(get_headers())
142 .query(&[
143 ("search[query]", query),
144 ("search[type]", "tag_query"),
145 ("limit", &limit.to_string()),
146 ])
147 .send()
148 .await?
149 .json::<Vec<DanbooruAutocompleteItem>>()
150 .await?;
151
152 Ok(response
153 .into_iter()
154 .map(|item| TagSuggestion {
155 name: item.value,
156 label: item.label,
157 post_count: item.post_count,
158 category: item.category,
159 })
160 .collect())
161 }
162}