mangadex_api_schema_rust/
lib.rs1#![deny(clippy::exhaustive_enums)]
2#![deny(clippy::exhaustive_structs)]
3
4mod bind;
7pub mod error;
8pub mod v5;
9
10use error::MangaDexErrorResponse_ as MangaDexErrorResponse;
11use mangadex_api_types::{RelationshipType, ResponseType, ResultType};
12use serde::de::DeserializeOwned;
13use serde::{Deserialize, Deserializer};
14use uuid::Uuid;
15
16use crate::v5::Relationship;
17
18#[derive(Deserialize)]
19#[serde(tag = "result", remote = "std::result::Result")]
20#[non_exhaustive]
21enum ApiResultDef<T, E> {
22 #[serde(rename = "ok", alias = "ko")]
25 Ok(T),
26 #[serde(rename = "error")]
27 Err(E),
28}
29
30#[derive(Deserialize)]
31#[serde(bound = "T: DeserializeOwned, E: DeserializeOwned")]
32pub struct ApiResult<T, E = MangaDexErrorResponse>(
33 #[serde(with = "ApiResultDef")] std::result::Result<T, E>,
34);
35
36impl<T, E> ApiResult<T, E> {
37 pub fn into_result(self) -> Result<T, E> {
38 self.0
39 }
40}
41
42#[derive(Debug, Deserialize, Clone)]
44#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
45#[cfg_attr(feature = "specta", derive(specta::Type))]
46#[non_exhaustive]
47pub struct ApiData<T> {
48 #[serde(default)]
49 pub result: ResultType,
50 pub response: ResponseType,
51 pub data: T,
52}
53
54impl<T> ApiData<T> {
55 fn new(data: T) -> ApiData<T> {
56 Self {
57 response: ResponseType::Entity,
58 data,
59 result: ResultType::Ok,
60 }
61 }
62}
63
64impl<T> Default for ApiData<T>
65where
66 T: Default,
67{
68 fn default() -> Self {
69 Self::new(T::default())
70 }
71}
72
73#[derive(Debug, Deserialize, Clone)]
74#[serde(rename_all = "camelCase")]
75#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
76#[cfg_attr(feature = "specta", derive(specta::Type))]
77#[non_exhaustive]
78pub struct ApiObject<A> {
79 pub id: Uuid,
80 pub type_: RelationshipType,
81 pub attributes: A,
82 pub relationships: Vec<Relationship>,
83}
84
85impl Default for ApiObject<()> {
86 fn default() -> Self {
87 Self {
88 id: Uuid::nil(),
89 type_: RelationshipType::Unknown,
90 attributes: (),
91 relationships: Vec::new(),
92 }
93 }
94}
95
96pub trait TypedAttributes {
97 const TYPE_: RelationshipType;
98}
99
100impl<A> Default for ApiObject<A>
101where
102 A: TypedAttributes + Default,
103{
104 fn default() -> Self {
105 Self {
106 id: Uuid::nil(),
107 type_: A::TYPE_,
108 attributes: A::default(),
109 relationships: Vec::new(),
110 }
111 }
112}
113
114impl<A> ApiObject<A> {
115 pub fn new(id: Uuid, type_: RelationshipType, attr: A) -> Self {
116 Self {
117 id,
118 type_,
119 attributes: attr,
120 relationships: Default::default(),
121 }
122 }
123 pub fn find_relationships(&self, type_: RelationshipType) -> Vec<&Relationship> {
124 self.relationships
125 .iter()
126 .filter(|rel| rel.type_ == type_)
127 .collect()
128 }
129 pub fn find_first_relationships(&self, type_: RelationshipType) -> Option<&Relationship> {
130 self.relationships.iter().find(|rel| rel.type_ == type_)
131 }
132}
133
134impl<T> From<ApiObject<T>> for ApiObjectNoRelationships<T> {
135 fn from(value: ApiObject<T>) -> Self {
136 Self {
137 id: value.id,
138 type_: value.type_,
139 attributes: value.attributes,
140 }
141 }
142}
143
144impl<T> ApiObject<T> {
145 pub fn drop_relationships(self) -> ApiObjectNoRelationships<T> {
146 self.into()
147 }
148}
149
150impl<T> ApiObjectNoRelationships<T> {
151 pub fn with_relathionships(self, rel: Option<Vec<Relationship>>) -> ApiObject<T> {
152 let mut res: ApiObject<T> = self.into();
153 let mut rels = rel.unwrap_or_default();
154 res.relationships.append(&mut rels);
155 res
156 }
157}
158
159impl<T> From<ApiObjectNoRelationships<T>> for ApiObject<T> {
160 fn from(value: ApiObjectNoRelationships<T>) -> Self {
161 Self {
162 id: value.id,
163 type_: value.type_,
164 attributes: value.attributes,
165 relationships: Vec::new(),
166 }
167 }
168}
169
170impl<T> PartialEq for ApiObject<T> {
171 fn eq(&self, other: &Self) -> bool {
172 self.id == other.id && self.type_ == other.type_
173 }
174}
175
176#[derive(Debug, Deserialize, Clone)]
177#[serde(rename_all = "camelCase")]
178#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
179#[cfg_attr(feature = "specta", derive(specta::Type))]
180#[non_exhaustive]
181pub struct ApiObjectNoRelationships<A> {
182 pub id: Uuid,
183 pub type_: RelationshipType,
184 pub attributes: A,
185}
186
187impl<A> Default for ApiObjectNoRelationships<A>
188where
189 A: TypedAttributes + Default,
190{
191 fn default() -> Self {
192 Self {
193 id: Uuid::nil(),
194 type_: A::TYPE_,
195 attributes: A::default(),
196 }
197 }
198}
199
200impl<A> ApiObjectNoRelationships<A> {
201 pub fn new(attributes: A) -> Self {
202 Self {
203 id: Uuid::nil(),
204 type_: RelationshipType::Unknown,
205 attributes,
206 }
207 }
208}
209
210#[derive(Debug, Default, Deserialize, Clone, Hash, PartialEq, Eq)]
224#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
225#[cfg_attr(feature = "specta", derive(specta::Type))]
226#[non_exhaustive]
227pub struct NoData {
228 #[serde(default)]
229 result: ResultType,
230}
231
232pub(crate) fn deserialize_null_default<'de, D, T>(deserializer: D) -> Result<T, D::Error>
233where
234 T: Default + Deserialize<'de>,
235 D: Deserializer<'de>,
236{
237 let opt = Option::deserialize(deserializer)?;
238 Ok(opt.unwrap_or_default())
239}
240
241#[derive(Debug, Deserialize, Clone)]
246#[serde(rename_all = "camelCase")]
247#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
248#[cfg_attr(feature = "specta", derive(specta::Type))]
249#[non_exhaustive]
250pub struct ApiObjectStringId<A> {
251 pub id: String,
252 pub type_: RelationshipType,
253 pub attributes: A,
254 pub relationships: Vec<Relationship>,
255}
256
257impl Default for ApiObjectStringId<()> {
258 fn default() -> Self {
259 Self {
260 id: Default::default(),
261 type_: RelationshipType::Unknown,
262 attributes: (),
263 relationships: Vec::new(),
264 }
265 }
266}
267
268impl<A> Default for ApiObjectStringId<A>
269where
270 A: TypedAttributes + Default,
271{
272 fn default() -> Self {
273 Self {
274 id: Default::default(),
275 type_: A::TYPE_,
276 attributes: A::default(),
277 relationships: Vec::new(),
278 }
279 }
280}
281
282impl<A> ApiObjectStringId<A> {
283 pub fn new(id: String, type_: RelationshipType, attr: A) -> Self {
284 Self {
285 id,
286 type_,
287 attributes: attr,
288 relationships: Default::default(),
289 }
290 }
291 pub fn find_relationships(&self, type_: RelationshipType) -> Vec<&Relationship> {
292 self.relationships
293 .iter()
294 .filter(|rel| rel.type_ == type_)
295 .collect()
296 }
297 pub fn find_first_relationships(&self, type_: RelationshipType) -> Option<&Relationship> {
298 self.relationships.iter().find(|rel| rel.type_ == type_)
299 }
300}
301
302impl<T> TryFrom<ApiObjectStringId<T>> for ApiObject<T> {
303 type Error = uuid::Error;
304 fn try_from(value: ApiObjectStringId<T>) -> Result<Self, Self::Error> {
305 Ok(Self {
306 id: value.id.parse()?,
307 type_: value.type_,
308 attributes: value.attributes,
309 relationships: value.relationships,
310 })
311 }
312}
313
314impl<T> From<ApiObject<T>> for ApiObjectStringId<T> {
315 fn from(value: ApiObject<T>) -> Self {
316 Self {
317 id: value.id.to_string(),
318 type_: value.type_,
319 attributes: value.attributes,
320 relationships: value.relationships,
321 }
322 }
323}
324
325pub trait RelationedObject {
327 fn find_relationships(&self, type_: RelationshipType) -> Vec<&Relationship>;
328 fn find_first_relationships(&self, _type_: RelationshipType) -> Option<&Relationship> {
329 None
330 }
331}
332
333impl<T> RelationedObject for Vec<T>
334where
335 T: RelationedObject,
336{
337 fn find_first_relationships(&self, type_: RelationshipType) -> Option<&Relationship> {
338 self.iter().find_map(|d| d.find_first_relationships(type_))
339 }
340 fn find_relationships(&self, type_: RelationshipType) -> Vec<&Relationship> {
341 self.iter()
342 .flat_map(|d| d.find_relationships(type_))
343 .collect()
344 }
345}
346
347impl<T> RelationedObject for v5::Results<T>
348where
349 T: RelationedObject,
350{
351 fn find_relationships(&self, type_: RelationshipType) -> Vec<&Relationship> {
352 self.data.find_relationships(type_)
353 }
354 fn find_first_relationships(&self, type_: RelationshipType) -> Option<&Relationship> {
355 self.data.find_first_relationships(type_)
356 }
357}
358
359macro_rules! impl_api_obj {
360 ($type:ty) => {
361 impl<T> RelationedObject for $type {
362 fn find_relationships(&self, type_: RelationshipType) -> Vec<&Relationship> {
363 self.relationships
364 .iter()
365 .filter(|rel| rel.type_ == type_)
366 .collect()
367 }
368 fn find_first_relationships(&self, type_: RelationshipType) -> Option<&Relationship> {
369 self.relationships.iter().find(|rel| rel.type_ == type_)
370 }
371 }
372 };
373}
374
375impl_api_obj!(ApiObject<T>);
376impl_api_obj!(ApiObjectStringId<T>);