1use std::collections::HashMap;
6
7use serde::{Deserialize, Serialize};
8use tosho_macros::AutoGetter;
9
10use super::{MangaNode, Volume};
11
12#[derive(Debug, Clone, AutoGetter, Serialize, Deserialize)]
16pub struct ChapterListNode {
17 uuid: String,
19 #[serde(rename = "label")]
21 chapter: String,
22 #[serde(rename = "is_new")]
24 new: bool,
25 #[serde(rename = "is_upcoming")]
27 upcoming: bool,
28 #[serde(rename = "is_premium")]
30 premium: bool,
31}
32
33#[derive(Debug, Clone, AutoGetter, Serialize, Deserialize)]
35pub struct Chapter {
36 uuid: String,
38 #[serde(rename = "label")]
40 chapter: String,
41 title: Option<String>,
43 #[serde(
45 rename = "release_date",
46 serialize_with = "super::datetime::serialize_opt",
47 deserialize_with = "super::datetime::deserialize_opt"
48 )]
49 #[copyable]
50 published: Option<chrono::DateTime<chrono::FixedOffset>>,
51 #[serde(
53 rename = "free_release_date",
54 serialize_with = "super::datetime::serialize_opt",
55 deserialize_with = "super::datetime::deserialize_opt"
56 )]
57 #[copyable]
58 free_published: Option<chrono::DateTime<chrono::FixedOffset>>,
59 #[serde(
61 rename = "original_published_date",
62 serialize_with = "super::datetime::serialize_opt",
63 deserialize_with = "super::datetime::deserialize_opt"
64 )]
65 original_published: Option<chrono::DateTime<chrono::FixedOffset>>,
66 #[serde(rename = "is_new")]
68 new: bool,
69 #[serde(rename = "is_upcoming")]
71 upcoming: bool,
72 #[serde(rename = "is_premium")]
74 premium: bool,
75 #[serde(
77 rename = "last_updated_at",
78 serialize_with = "super::datetime::serialize_opt",
79 deserialize_with = "super::datetime::deserialize_opt"
80 )]
81 #[copyable]
82 last_updated: Option<chrono::DateTime<chrono::FixedOffset>>,
83 volume_uuid: Option<String>,
85}
86
87impl Chapter {
88 pub fn formatted_title(&self) -> String {
90 let title = self.title.as_deref().unwrap_or("");
91 if title.is_empty() {
92 format!("Chapter {}", self.chapter)
93 } else {
94 format!("Chapter {} - {}", self.chapter, title)
95 }
96 }
97}
98
99#[derive(Debug, Clone, AutoGetter, Serialize, Deserialize)]
101pub struct ChapterDetailsResponse {
102 chapter: Chapter,
104 manga: MangaNode,
106}
107
108#[derive(Debug, Clone, AutoGetter, Serialize, Deserialize)]
110pub struct ChapterListResponse {
111 chapters: Vec<Chapter>,
113 #[serde(rename = "volume_uuid_to_volume")]
117 volumes: HashMap<String, Volume>,
118 separators: Vec<super::common::Separator>,
120 #[serde(rename = "volume_uuid_order")]
122 volume_order: Vec<String>,
123}
124
125pub type Spread = (Option<i32>, Option<i32>);
129
130#[derive(Debug, Clone, AutoGetter, Serialize, Deserialize)]
132pub struct ChapterPage {
133 uuid: String,
135 image: super::Image,
137 #[serde(rename = "image_wm")]
139 watermarked_image: super::Image,
140 #[serde(rename = "is_double_page")]
142 double_page: bool,
143 #[serde(rename = "spread_index")]
147 spread: i32,
148 side: String,
153}
154
155#[derive(Debug, Clone, AutoGetter, Serialize, Deserialize)]
157pub struct ChapterPageDetails {
158 spreads: Vec<Spread>,
160 pages: Vec<ChapterPage>,
162}
163
164#[derive(Debug, Clone, AutoGetter, Serialize, Deserialize)]
166pub struct ChapterPageDetailsResponse {
167 data: ChapterPageDetails,
169}
170
171#[cfg(test)]
172mod tests {
173 #[test]
174 fn test_spreads_unpack() {
175 let json_test = r#"{
176 "spreads": [
177 [null, 0],
178 [1, 2],
179 [3, 4],
180 [5, null]
181 ],
182 "pages": []
183 }"#;
184
185 let spreads: super::ChapterPageDetails = serde_json::from_str(json_test).unwrap();
186 assert_eq!(spreads.spreads.len(), 4);
187 assert_eq!(spreads.spreads[0], (None, Some(0)));
188 assert_eq!(spreads.spreads[1], (Some(1), Some(2)));
189 assert_eq!(spreads.spreads[2], (Some(3), Some(4)));
190 assert_eq!(spreads.spreads[3], (Some(5), None));
191 }
192}