Skip to main content

aidoku/structs/
source.rs

1use super::{
2	Chapter, Filter, FilterValue, HashMap, HomeLayout, Listing, Manga, MangaPageResult, Page,
3	PageContext, Setting,
4};
5use crate::alloc::{String, Vec};
6use crate::imports::{canvas::ImageRef, net::Request};
7use serde::{Deserialize, Serialize, ser::SerializeStruct};
8
9pub use crate::imports::error::{AidokuError, Result};
10
11/// The required functions an Aidoku source must implement.
12pub trait Source {
13	/// Called to initialize a source.
14	///
15	/// If a source requires any setup before other functions are called, it should happen here.
16	fn new() -> Self;
17
18	/// Returns the manga for a search query with filters.
19	fn get_search_manga_list(
20		&self,
21		query: Option<String>,
22		page: i32,
23		filters: Vec<FilterValue>,
24	) -> Result<MangaPageResult>;
25
26	/// Updates a given manga with new details and chapters, as requested.
27	fn get_manga_update(
28		&self,
29		manga: Manga,
30		needs_details: bool,
31		needs_chapters: bool,
32	) -> Result<Manga>;
33
34	/// Returns the pages for a given manga chapter.
35	fn get_page_list(&self, manga: Manga, chapter: Chapter) -> Result<Vec<Page>>;
36}
37
38/// A source that provides listings.
39pub trait ListingProvider: Source {
40	/// Returns the manga for the provided listing.
41	fn get_manga_list(&self, listing: Listing, page: i32) -> Result<MangaPageResult>;
42}
43
44/// A source that provides a home layout.
45pub trait Home: Source {
46	fn get_home(&self) -> Result<HomeLayout>;
47}
48
49/// A source that provides dynamic listings.
50pub trait DynamicListings: Source {
51	fn get_dynamic_listings(&self) -> Result<Vec<Listing>>;
52}
53
54/// A source that provides dynamic filters.
55pub trait DynamicFilters: Source {
56	fn get_dynamic_filters(&self) -> Result<Vec<Filter>>;
57}
58
59/// A source that provides dynamic settings.
60pub trait DynamicSettings: Source {
61	fn get_dynamic_settings(&self) -> Result<Vec<Setting>>;
62}
63
64/// A source that processes page image data after being fetched.
65pub trait PageImageProcessor: Source {
66	fn process_page_image(
67		&self,
68		response: ImageResponse,
69		context: Option<PageContext>,
70	) -> Result<ImageRef>;
71}
72
73/// A source that processes cover image data after being fetched.
74pub trait CoverImageProcessor: Source {
75	fn process_cover_image(&self, response: ImageResponse) -> Result<ImageRef>;
76}
77
78/// A source that provides requests for images.
79///
80/// By default, Aidoku will request covers, thumbnails, and pages without headers.
81/// This trait can be used to override the requests for source images.
82pub trait ImageRequestProvider: Source {
83	fn get_image_request(&self, url: String, context: Option<PageContext>) -> Result<Request>;
84}
85
86/// A source that provides dynamic descriptions for pages.
87pub trait PageDescriptionProvider: Source {
88	fn get_page_description(&self, page: Page) -> Result<String>;
89}
90
91/// A source that provides multiple cover images.
92pub trait AlternateCoverProvider: Source {
93	fn get_alternate_covers(&self, manga: Manga) -> Result<Vec<String>>;
94}
95
96/// A source that provides a programmatic base url.
97///
98/// The use of this trait is discouraged in favor of providing the source url statically.
99pub trait BaseUrlProvider: Source {
100	fn get_base_url(&self) -> Result<String>;
101}
102
103/// A source that handles notification callbacks.
104///
105/// Notifications can be sent on source setting changes.
106pub trait NotificationHandler: Source {
107	fn handle_notification(&self, notification: String);
108}
109
110/// A source that handles deep links.
111///
112/// If a url that is contained in one of the source's provided base urls is opened
113/// in Aidoku, it will be sent to the given source to handle.
114pub trait DeepLinkHandler: Source {
115	fn handle_deep_link(&self, url: String) -> Result<Option<DeepLinkResult>>;
116}
117
118/// A source that handles basic login with username and password.
119///
120/// This function should return true if the login was successful.
121pub trait BasicLoginHandler: Source {
122	fn handle_basic_login(&self, key: String, username: String, password: String) -> Result<bool>;
123}
124
125/// A source that handles custom webview login.
126///
127/// This function will be called whenever cookies are updated, and should return true if the login was successful.
128pub trait WebLoginHandler: Source {
129	fn handle_web_login(&self, key: String, cookies: HashMap<String, String>) -> Result<bool>;
130}
131
132/// A source that handles key migration.
133///
134/// If a source provides a "breakingChangeVersion" in its configuration, these functions will be
135/// called with all of a user's local manga and chapter keys to migrate them after updating.
136/// These functions should return the new key to replace the old one.
137pub trait MigrationHandler: Source {
138	fn handle_manga_migration(&self, key: String) -> Result<String>;
139	fn handle_chapter_migration(&self, manga_key: String, chapter_key: String) -> Result<String>;
140}
141
142/// A result of a deep link handling.
143#[derive(Debug, Clone, PartialEq)]
144pub enum DeepLinkResult {
145	Manga { key: String },
146	Chapter { manga_key: String, key: String },
147	Listing(Listing),
148}
149
150impl Serialize for DeepLinkResult {
151	fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error>
152	where
153		S: serde::Serializer,
154	{
155		let mut state = serializer.serialize_struct("DeepLinkResult", 3)?;
156		match self {
157			DeepLinkResult::Manga { key } => {
158				state.serialize_field("manga_key", &Some(key))?;
159				state.serialize_field("chapter_key", &Option::<String>::None)?;
160				state.serialize_field("listing", &Option::<Listing>::None)?;
161			}
162			DeepLinkResult::Chapter { manga_key, key } => {
163				state.serialize_field("manga_key", &Some(manga_key))?;
164				state.serialize_field("chapter_key", &Some(key))?;
165				state.serialize_field("listing", &Option::<Listing>::None)?;
166			}
167			DeepLinkResult::Listing(listing) => {
168				state.serialize_field("manga_key", &Option::<String>::None)?;
169				state.serialize_field("chapter_key", &Option::<String>::None)?;
170				state.serialize_field("listing", &Some(listing))?;
171			}
172		}
173		state.end()
174	}
175}
176
177/// The details of a HTTP request.
178#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
179pub struct ImageRequest {
180	pub url: Option<String>,
181	pub headers: HashMap<String, String>,
182}
183
184/// A response from a network image request.
185#[derive(Debug, Serialize, Deserialize)]
186pub struct ImageResponse {
187	/// The HTTP status code.
188	pub code: u16,
189	/// The HTTP response headers.
190	pub headers: HashMap<String, String>,
191	/// The HTTP request details.
192	pub request: ImageRequest,
193	/// A reference to image data.
194	pub image: ImageRef,
195}