1use std::{fmt::Display, str::FromStr};
2
3use crate::category::Category;
4
5#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, Default)]
6pub enum SortOrder {
7 Asecending,
8 #[default]
9 Descending,
10}
11
12impl FromStr for SortOrder {
13 type Err = String;
14 fn from_str(s: &str) -> Result<Self, Self::Err> {
15 match s {
16 "asc" => Ok(SortOrder::Asecending),
17 "desc" => Ok(SortOrder::Descending),
18 _ => Err("Invalid sort order".into()),
19 }
20 }
21}
22
23impl Display for SortOrder {
24 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25 match self {
26 SortOrder::Asecending => write!(f, "asc"),
27 SortOrder::Descending => write!(f, "desc"),
28 }
29 }
30}
31
32#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, Default)]
33pub enum Sort {
34 Comments,
35 Size,
36 Date,
37 #[default]
38 Seeders,
39 Leechers,
40 Downloads,
41}
42
43impl FromStr for Sort {
44 type Err = String;
45 fn from_str(s: &str) -> Result<Self, Self::Err> {
46 match s {
47 "comments" => Ok(Sort::Comments),
48 "size" => Ok(Sort::Size),
49 "date" => Ok(Sort::Date),
50 "seeders" => Ok(Sort::Seeders),
51 "leechers" => Ok(Sort::Leechers),
52 "downloads" => Ok(Sort::Downloads),
53 _ => Err("Invalid sort".into()),
54 }
55 }
56}
57
58impl Display for Sort {
59 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
60 match self {
61 Sort::Comments => write!(f, "comments"),
62 Sort::Size => write!(f, "size"),
63 Sort::Date => write!(f, "id"),
64 Sort::Seeders => write!(f, "seeders"),
65 Sort::Leechers => write!(f, "leechers"),
66 Sort::Downloads => write!(f, "downloads"),
67 }
68 }
69}
70
71#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, Default)]
72pub enum Filter {
73 #[default]
74 NoFilter,
75 NoRemakes,
76 TrustedOnly,
77}
78
79impl From<u8> for Filter {
80 fn from(i: u8) -> Self {
81 match i {
82 0 => Filter::NoFilter,
83 1 => Filter::NoRemakes,
84 2 => Filter::TrustedOnly,
85 _ => Filter::NoFilter,
86 }
87 }
88}
89
90#[allow(clippy::from_over_into)]
91impl Into<u8> for Filter {
92 fn into(self) -> u8 {
93 match self {
94 Filter::NoFilter => 0,
95 Filter::NoRemakes => 1,
96 Filter::TrustedOnly => 2,
97 }
98 }
99}
100
101impl Display for Filter {
102 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
103 match self {
104 Filter::NoFilter => write!(f, "0"),
105 Filter::NoRemakes => write!(f, "1"),
106 Filter::TrustedOnly => write!(f, "2"),
107 }
108 }
109}
110
111#[derive(Debug, PartialEq, Clone, Hash)]
112pub struct Query<C> {
113 search: String,
114 page: u32,
115 sort: Sort,
116 sort_order: SortOrder,
117 filter: Filter,
118 category: C,
119}
120
121impl<C> Display for Query<C>
122where
123 C: Category,
124{
125 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
126 write!(
127 f,
128 "q={}&p={}&s={}&o={}&f={}&c={}",
129 self.search,
130 self.page,
131 self.sort,
132 self.sort_order,
133 self.filter,
134 self.category
135 )
136 }
137}
138
139impl<C> Default for Query<C>
140where
141 C: Category,
142{
143 fn default() -> Self {
144 Query {
145 search: String::new(),
146 page: 1,
147 sort: Sort::default(),
148 sort_order: SortOrder::default(),
149 filter: Filter::default(),
150 category: C::default(),
151 }
152 }
153}
154
155#[derive(Debug, Clone, PartialEq)]
156pub struct QueryBuilder<C> {
157 search: String,
158 page: u32,
159 sort: Sort,
160 sort_order: SortOrder,
161 filter: Filter,
162 category: C,
163}
164
165impl<C: Category> Default for QueryBuilder<C> {
166 fn default() -> Self {
167 QueryBuilder {
168 search: String::new(),
169 page: 1,
170 sort: Sort::default(),
171 sort_order: SortOrder::default(),
172 filter: Filter::default(),
173 category: C::default(),
174 }
175 }
176}
177
178impl<C: Category> QueryBuilder<C> {
179 pub fn new() -> QueryBuilder<C> {
180 QueryBuilder::default()
181 }
182
183 pub fn build(self) -> Query<C> {
184 Query {
185 search: self.search,
186 page: self.page,
187 sort: self.sort,
188 sort_order: self.sort_order,
189 filter: self.filter,
190 category: self.category,
191 }
192 }
193
194 pub fn search<S: Into<String>>(mut self, search: S) -> QueryBuilder<C> {
195 self.search = search.into();
196 self
197 }
198
199 pub fn page(mut self, page: u32) -> QueryBuilder<C> {
200 self.page = page;
201 self
202 }
203
204 pub fn sort(mut self, sort: Sort) -> QueryBuilder<C> {
205 self.sort = sort;
206 self
207 }
208
209 pub fn sort_order(mut self, sort_order: SortOrder) -> QueryBuilder<C> {
210 self.sort_order = sort_order;
211 self
212 }
213
214 pub fn filter(mut self, filter: Filter) -> QueryBuilder<C> {
215 self.filter = filter;
216 self
217 }
218
219 pub fn category(mut self, category: C) -> QueryBuilder<C> {
220 self.category = category;
221 self
222 }
223}
224
225#[cfg(test)]
226mod tests {
227 use super::*;
228 use crate::NyaaCategory;
229
230 #[test]
231 fn test_build() {
232 let query = QueryBuilder::new()
233 .search("frieren")
234 .sort(Sort::Date)
235 .category(NyaaCategory::Anime)
236 .build();
237 assert_eq!(query.to_string(), format!("q={}&p={}&s={}&o={}&f={}&c={}", "frieren", 1, "id", "desc", 0, "1_0"))
238 }
239}