aidoku/structs/
mod.rs

1//! Serializable data structures that are sent between Aidoku and sources.
2
3use super::alloc::{String, Vec};
4use serde::{Deserialize, Serialize};
5
6pub use hashbrown::HashMap;
7
8pub mod canvas;
9mod filter;
10mod home;
11mod setting;
12
13pub use filter::*;
14pub use home::*;
15pub use setting::*;
16
17#[cfg(feature = "imports")]
18mod source;
19
20#[cfg(feature = "imports")]
21pub use source::*;
22
23/// Context associated with a page.
24pub type PageContext = HashMap<String, String>;
25
26/// The publishing status of a manga.
27#[derive(Default, PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)]
28pub enum MangaStatus {
29	#[default]
30	Unknown = 0,
31	Ongoing,
32	Completed,
33	Cancelled,
34	Hiatus,
35}
36
37/// The content rating of a manga.
38#[derive(Default, PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)]
39pub enum ContentRating {
40	#[default]
41	Unknown = 0,
42	Safe,
43	Suggestive,
44	NSFW,
45}
46
47/// The proper reading viewer for a manga.
48///
49/// This is used for automatic selection of the reader view in Aidoku.
50/// `RightToLeft` is used for manga, `LeftToRight` is for western comics,
51/// and `Webtoon` is used for manhwa and manhua.
52#[derive(Default, PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)]
53pub enum Viewer {
54	#[default]
55	Unknown = 0,
56	LeftToRight,
57	RightToLeft,
58	Vertical,
59	Webtoon,
60}
61
62/// The preferred update strategy for a manga.
63///
64/// Titles marked as `Always` will be included in library refreshes by default,
65/// while `Never` will be excluded. Useful for titles that are known to be fully
66/// completed or have a static chapter list that won't change after the initial fetch.
67#[derive(Default, PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)]
68pub enum UpdateStrategy {
69	#[default]
70	Always,
71	Never,
72}
73
74/// A manga, comic, webtoon, or other type of content for Aidoku to read.
75#[derive(Default, Clone, Debug, PartialEq, Serialize, Deserialize)]
76pub struct Manga {
77	/// Unique identifier for the manga.
78	pub key: String,
79	/// Title of the manga.
80	pub title: String,
81	/// Link to the manga cover image.
82	pub cover: Option<String>,
83	/// Optional list of artists.
84	pub artists: Option<Vec<String>>,
85	/// Optional list of authors.
86	pub authors: Option<Vec<String>>,
87	/// Description of the manga.
88	pub description: Option<String>,
89	/// Link to the manga on the source website.
90	pub url: Option<String>,
91	/// Optional list of genres or tags (max: 255).
92	pub tags: Option<Vec<String>>,
93	/// Publishing status of the manga.
94	pub status: MangaStatus,
95	/// Content rating of the manga.
96	pub content_rating: ContentRating,
97	/// Preferred viewer type of the manga.
98	pub viewer: Viewer,
99	/// Ideal update strategy for the manga.
100	pub update_strategy: UpdateStrategy,
101	/// Optional date for when the manga should next be updated.
102	pub next_update_time: Option<i64>,
103	/// List of chapters.
104	pub chapters: Option<Vec<Chapter>>,
105}
106
107impl Manga {
108	/// Copy the values from another manga into this one.
109	pub fn copy_from(&mut self, manga: Manga) {
110		self.key = manga.key;
111		self.title = manga.title;
112		if let Some(cover) = manga.cover {
113			self.cover = Some(cover);
114		}
115		if let Some(artists) = manga.artists {
116			self.artists = Some(artists);
117		}
118		if let Some(authors) = manga.authors {
119			self.authors = Some(authors);
120		}
121		if let Some(description) = manga.description {
122			self.description = Some(description);
123		}
124		if let Some(url) = manga.url {
125			self.url = Some(url);
126		}
127		if let Some(tags) = manga.tags {
128			self.tags = Some(tags);
129		}
130		self.status = manga.status;
131		self.content_rating = manga.content_rating;
132		self.viewer = manga.viewer;
133		self.update_strategy = manga.update_strategy;
134		if let Some(next_update_time) = manga.next_update_time {
135			self.next_update_time = Some(next_update_time);
136		}
137		if let Some(chapters) = manga.chapters {
138			self.chapters = Some(chapters);
139		}
140	}
141}
142
143/// A page of manga entries.
144#[derive(Default, Clone, Debug, PartialEq, Serialize)]
145pub struct MangaPageResult {
146	/// List of manga entries.
147	pub entries: Vec<Manga>,
148	/// Whether the next page is available or not.
149	pub has_next_page: bool,
150}
151
152/// A chapter of a manga.
153#[derive(Default, Clone, Debug, PartialEq, Serialize, Deserialize)]
154pub struct Chapter {
155	/// Unique identifier for the chapter.
156	pub key: String,
157	/// Title of the chapter (excluding volume and chapter number).
158	pub title: Option<String>,
159	/// Chapter number.
160	pub chapter_number: Option<f32>,
161	/// Volume number.
162	pub volume_number: Option<f32>,
163	/// Date the chapter was uploaded.
164	pub date_uploaded: Option<i64>,
165	/// Optional list of groups that scanlated or published the chapter.
166	pub scanlators: Option<Vec<String>>,
167	/// Link to the chapter on the source website.
168	pub url: Option<String>,
169	/// Language of the chapter.
170	pub language: Option<String>,
171	/// Optional thumbnail image url for the chapter.
172	pub thumbnail: Option<String>,
173	/// Boolean indicating if the chapter is locked.
174	pub locked: bool,
175}
176
177#[cfg(feature = "imports")]
178mod __private {
179	use crate::imports::canvas::ImageRef;
180
181	#[derive(Debug, PartialEq)]
182	pub struct ImageRefPriv(pub(crate) ImageRef);
183
184	impl serde::Serialize for ImageRefPriv {
185		fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
186		where
187			S: serde::Serializer,
188		{
189			self.0.serialize(serializer)
190		}
191	}
192
193	impl<'de> serde::Deserialize<'de> for ImageRefPriv {
194		fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
195		where
196			D: serde::Deserializer<'de>,
197		{
198			ImageRef::deserialize(deserializer).map(ImageRefPriv)
199		}
200	}
201}
202
203/// The content of a page.
204#[derive(Debug, PartialEq, Serialize, Deserialize)]
205pub enum PageContent {
206	/// A url to an image, with associated context.
207	///
208	/// The context is sent to the page processor and/or image request modifier
209	/// if the source implements either.
210	Url(String, Option<PageContext>),
211	/// A markdown text string.
212	Text(String),
213	/// A raw image.
214	#[cfg(feature = "imports")]
215	Image(__private::ImageRefPriv),
216	/// A url to zip archive and a file path to an image inside the archive.
217	Zip(String, String),
218}
219
220impl PageContent {
221	/// Create a new `PageContent` with a url.
222	pub fn url<T: Into<String>>(url: T) -> Self {
223		Self::Url(url.into(), None)
224	}
225
226	/// Create a new `PageContent` with a url and context.
227	pub fn url_context<T: Into<String>>(url: T, context: PageContext) -> Self {
228		Self::Url(url.into(), Some(context))
229	}
230
231	/// Create a new `PageContent` with a markdown text string.
232	pub fn text<T: Into<String>>(text: T) -> Self {
233		Self::Text(text.into())
234	}
235
236	/// Create a new `PageContent` with a raw image.
237	#[cfg(feature = "imports")]
238	pub fn image(image: crate::imports::canvas::ImageRef) -> Self {
239		Self::Image(__private::ImageRefPriv(image))
240	}
241}
242
243/// A page for a chapter.
244#[derive(Debug, PartialEq, Serialize, Deserialize)]
245pub struct Page {
246	/// The page content.
247	pub content: PageContent,
248	/// Optional thumbnail image url for the page.
249	pub thumbnail: Option<String>,
250	/// If the page has a description.
251	pub has_description: bool,
252	/// Optional description for the page.
253	///
254	/// If `has_description` is `true` and this is `None`, [PageDescriptionProvider] will be used.
255	/// If `has_description` is `false`, this field will be ignored.
256	pub description: Option<String>,
257}
258
259impl Page {
260	/// Set the page content to be externally managed if it is an image.
261	///
262	/// This property is exposed for the functions that the [register_source](crate::register_source)
263	/// macro generates and should not be used directly.
264	#[cfg(feature = "imports")]
265	pub fn ensure_externally_managed(&mut self) {
266		if let PageContent::Image(ref mut image) = self.content {
267			image.0.externally_managed = true;
268		}
269	}
270}
271
272impl Default for Page {
273	fn default() -> Self {
274		Self {
275			content: PageContent::Text(String::default()),
276			thumbnail: None,
277			has_description: false,
278			description: None,
279		}
280	}
281}
282
283/// The display type of a listing.
284#[derive(Default, PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize)]
285pub enum ListingKind {
286	#[default]
287	Default,
288	List,
289}
290
291/// A listing of manga.
292#[derive(Default, Clone, Debug, PartialEq, Serialize, Deserialize)]
293pub struct Listing {
294	/// Unique identifier for the listing.
295	pub id: String,
296	/// Title of the listing.
297	pub name: String,
298	/// Type of listing.
299	pub kind: ListingKind,
300}