1use reqwest::Client;
2
3use crate::format::error::VndbApiError;
4use crate::request::query::{
5 CharacterQuery, ProducerQuery, Query, ReleaseQuery, StaffQuery, TagQuery, TraitQuery,
6 UListQuery, VnQuery,
7};
8use crate::request::response::Response;
9
10use crate::format::auth::AuthInfo;
11use crate::format::stats::VndbStats;
12use crate::format::user::{UserSearch, UserSearchFields};
13
14use crate::format::character::Character;
15use crate::format::producer::Producer;
16use crate::format::release::Release;
17use crate::format::rlist::RListPatch;
18use crate::format::staff::Staff;
19use crate::format::tag::Tag;
20use crate::format::traits::Trait;
21use crate::format::ulist::{UList, UListLabels, UListLabelsFieldChoices, UListPatch};
22use crate::format::vn::VisualNovel;
23
24const API_ENDPOINT: &str = "https://api.vndb.org/kana";
25
26pub struct VndbApiClient {
27 client: Client,
28 access_token: String,
29}
30
31impl VndbApiClient {
32 pub fn new(token: &String) -> Self {
33 VndbApiClient {
34 client: Client::new(),
35 access_token: token.clone(),
36 }
37 }
38
39 pub async fn get_stats(&self) -> Result<VndbStats, VndbApiError> {
40 let url = format!("{}/stats", API_ENDPOINT);
41 let response = self.client.get(&url).send().await?;
42
43 if response.status().is_success() {
44 let result = response.json::<VndbStats>().await?;
45 Ok(result)
46 } else {
47 Err(VndbApiError::new(response.status(), response.text().await?))
48 }
49 }
50
51 pub async fn get_auth_info(&self) -> Result<AuthInfo, VndbApiError> {
52 let url = format!("{}/authinfo", API_ENDPOINT);
53 let response = self
54 .client
55 .get(&url)
56 .header("Authorization", format!("token {}", self.access_token))
57 .send()
58 .await?;
59
60 if response.status().is_success() {
61 let result = response.json::<AuthInfo>().await?;
62 Ok(result)
63 } else {
64 Err(VndbApiError::new(response.status(), response.text().await?))
65 }
66 }
67
68 pub async fn get_user(
69 &self,
70 q: &Vec<String>,
71 fields: &UserSearchFields,
72 ) -> Result<UserSearch, VndbApiError> {
73 let url = format!(
74 "{}/user?q={}&fields={}",
75 API_ENDPOINT,
76 q.join("&q="),
77 fields.to_csv()
78 );
79 let response = self.client.get(&url).json(q).send().await?;
80
81 if response.status().is_success() {
82 let result = response.json::<UserSearch>().await?;
83 Ok(result)
84 } else {
85 Err(VndbApiError::new(response.status(), response.text().await?))
86 }
87 }
88
89 pub async fn vn_search(
90 &self,
91 q: &Query<VnQuery>,
92 ) -> Result<Response<VisualNovel>, VndbApiError> {
93 let url = format!("{}/vn", API_ENDPOINT);
94 let response = self.client.post(&url).json(q).send().await?;
95
96 if response.status().is_success() {
97 let result = response.json::<Response<VisualNovel>>().await?;
98 Ok(result)
99 } else {
100 Err(VndbApiError::new(response.status(), response.text().await?))
101 }
102 }
103
104 pub async fn release_search(
105 &self,
106 q: &Query<ReleaseQuery>,
107 ) -> Result<Response<Release>, VndbApiError> {
108 let url = format!("{}/release", API_ENDPOINT);
109 let response = self.client.post(&url).json(q).send().await?;
110
111 if response.status().is_success() {
112 let result = response.json::<Response<Release>>().await?;
113 Ok(result)
114 } else {
115 Err(VndbApiError::new(response.status(), response.text().await?))
116 }
117 }
118
119 pub async fn producer_search(
120 &self,
121 q: &Query<ProducerQuery>,
122 ) -> Result<Response<Producer>, VndbApiError> {
123 let url = format!("{}/producer", API_ENDPOINT);
124 let response = self.client.post(&url).json(q).send().await?;
125
126 if response.status().is_success() {
127 let result = response.json::<Response<Producer>>().await?;
128 Ok(result)
129 } else {
130 Err(VndbApiError::new(response.status(), response.text().await?))
131 }
132 }
133
134 pub async fn character_search(
135 &self,
136 q: &Query<CharacterQuery>,
137 ) -> Result<Response<Character>, VndbApiError> {
138 let url = format!("{}/character", API_ENDPOINT);
139 let response = self.client.post(&url).json(q).send().await?;
140
141 if response.status().is_success() {
142 let result = response.json::<Response<Character>>().await?;
143 Ok(result)
144 } else {
145 Err(VndbApiError::new(response.status(), response.text().await?))
146 }
147 }
148
149 pub async fn staff_search(
150 &self,
151 q: &Query<StaffQuery>,
152 ) -> Result<Response<Staff>, VndbApiError> {
153 let url = format!("{}/staff", API_ENDPOINT);
154 let response = self.client.post(&url).json(q).send().await?;
155
156 if response.status().is_success() {
157 let result = response.json::<Response<Staff>>().await?;
158 Ok(result)
159 } else {
160 Err(VndbApiError::new(response.status(), response.text().await?))
161 }
162 }
163
164 pub async fn tag_search(&self, q: &Query<TagQuery>) -> Result<Response<Tag>, VndbApiError> {
165 let url = format!("{}/tag", API_ENDPOINT);
166 let response = self.client.post(&url).json(q).send().await?;
167
168 if response.status().is_success() {
169 let result = response.json::<Response<Tag>>().await?;
170 Ok(result)
171 } else {
172 Err(VndbApiError::new(response.status(), response.text().await?))
173 }
174 }
175
176 pub async fn trait_search(
177 &self,
178 q: &Query<TraitQuery>,
179 ) -> Result<Response<Trait>, VndbApiError> {
180 let url = format!("{}/trait", API_ENDPOINT);
181 let response = self.client.post(&url).json(q).send().await?;
182
183 if response.status().is_success() {
184 let result = response.json::<Response<Trait>>().await?;
185 Ok(result)
186 } else {
187 Err(VndbApiError::new(response.status(), response.text().await?))
188 }
189 }
190
191 pub async fn ulist(&self, q: &Query<UListQuery>) -> Result<Response<UList>, VndbApiError> {
192 let url = format!("{}/ulist", API_ENDPOINT);
193 let response = self.client.post(&url).json(q).send().await?;
194
195 if response.status().is_success() {
196 let result = response.json::<Response<UList>>().await?;
197 Ok(result)
198 } else {
199 Err(VndbApiError::new(response.status(), response.text().await?))
200 }
201 }
202
203 pub async fn get_ulist_labels(
204 &self,
205 user: &String,
206 fields: &UListLabelsFieldChoices,
207 ) -> Result<UListLabels, VndbApiError> {
208 let url = format!(
209 "{}/ulist_labels?user={}&fields={}",
210 API_ENDPOINT,
211 user.clone(),
212 fields.to_csv()
213 );
214 let response = self.client.get(&url).send().await?;
215
216 if response.status().is_success() {
217 let result = response.json::<UListLabels>().await?;
218 Ok(result)
219 } else {
220 Err(VndbApiError::new(response.status(), response.text().await?))
221 }
222 }
223
224 pub async fn ulist_patch(
225 &self,
226 vn_id: &String,
227 patch: &UListPatch,
228 ) -> Result<(), VndbApiError> {
229 let url = format!("{}/ulist/{}", API_ENDPOINT, vn_id);
230 let response = self
231 .client
232 .patch(&url)
233 .header("Authorization", format!("token {}", self.access_token))
234 .header("Content-Type", "application/json")
235 .json(patch)
236 .send()
237 .await?;
238
239 if response.status().is_success() {
240 Ok(())
241 } else {
242 Err(VndbApiError::new(response.status(), response.text().await?))
243 }
244 }
245
246 pub async fn rlist_patch(&self, r_id: &String, patch: &RListPatch) -> Result<(), VndbApiError> {
247 let url = format!("{}/rlist/{}", API_ENDPOINT, r_id);
248 let response = self
249 .client
250 .patch(&url)
251 .header("Authorization", format!("token {}", self.access_token))
252 .header("Content-Type", "application/json")
253 .json(patch)
254 .send()
255 .await?;
256
257 if response.status().is_success() {
258 Ok(())
259 } else {
260 Err(VndbApiError::new(response.status(), response.text().await?))
261 }
262 }
263
264 pub async fn ulist_remove(&self, vn_id: &String) -> Result<(), VndbApiError> {
265 let url = format!("{}/ulist/{}", API_ENDPOINT, vn_id);
266 let response = self
267 .client
268 .delete(&url)
269 .header("Authorization", format!("token {}", self.access_token))
270 .send()
271 .await?;
272
273 if response.status().is_success() {
274 Ok(())
275 } else {
276 Err(VndbApiError::new(response.status(), response.text().await?))
277 }
278 }
279
280 pub async fn rlist_remove(&self, r_id: &String) -> Result<(), VndbApiError> {
281 let url = format!("{}/rlist/{}", API_ENDPOINT, r_id);
282 let response = self
283 .client
284 .delete(&url)
285 .header("Authorization", format!("token {}", self.access_token))
286 .send()
287 .await?;
288
289 if response.status().is_success() {
290 Ok(())
291 } else {
292 Err(VndbApiError::new(response.status(), response.text().await?))
293 }
294 }
295}