1use crate::utils::{format_datetime, format_duration};
2use crate::wrapper::service::ServiceName;
3use crate::wrapper::tag::Tag;
4use chrono::{Datelike, Duration};
5use mime::Mime;
6use std::fmt::{Display, Formatter};
7
8#[derive(Clone, Debug)]
9pub struct TagBuilder {
10 negated: bool,
11 name: String,
12 namespace: Option<String>,
13}
14
15impl TagBuilder {
16 pub fn new<S: ToString>(name: S) -> Self {
17 Self {
18 negated: false,
19 name: name.to_string(),
20 namespace: None,
21 }
22 }
23
24 pub fn namespace<S: ToString>(mut self, namespace: S) -> Self {
26 self.namespace = Some(namespace.to_string());
27
28 self
29 }
30
31 pub fn system(self) -> SystemTagBuilder {
33 SystemTagBuilder {
34 negated: false,
35 name: self.name,
36 }
37 }
38
39 pub fn negate(mut self) -> Self {
42 self.negated = !self.negated;
43
44 self
45 }
46
47 pub fn build(self) -> Tag {
48 Tag {
49 negated: self.negated,
50 name: self.name,
51 namespace: self.namespace,
52 }
53 }
54}
55
56#[derive(Clone, Debug)]
57pub struct SystemTagBuilder {
58 name: String,
59 negated: bool,
60}
61
62impl SystemTagBuilder {
63 pub fn new() -> SystemTagBuilder {
64 SystemTagBuilder {
65 name: String::new(),
66 negated: false,
67 }
68 }
69
70 pub fn build(self) -> Tag {
71 Tag {
72 negated: self.negated,
73 name: self.name,
74 namespace: Some(String::from("system")),
75 }
76 }
77
78 pub fn negate(mut self) -> Self {
81 self.negated = !self.negated;
82
83 self
84 }
85
86 pub fn everything(self) -> Self {
88 self.change_name("everything")
89 }
90
91 pub fn inbox(self) -> Self {
93 self.change_name("inbox")
94 }
95
96 pub fn archive(self) -> Self {
98 self.change_name("archive")
99 }
100
101 pub fn has_duration(self) -> Self {
103 self.change_name("has duration")
104 }
105
106 pub fn no_duration(self) -> Self {
108 self.change_name("no duration")
109 }
110
111 pub fn duration(self, comparator: Comparator, value: u64, unit: DurationUnit) -> Self {
113 self.change_name(format!("duration {} {} {}", comparator, value, unit))
114 }
115
116 pub fn best_duplicate_quality(self) -> Self {
118 self.change_name("best quality of group")
119 }
120
121 pub fn not_best_duplicate_quality(self) -> Self {
123 self.change_name("isn't best quality of group")
124 }
125
126 pub fn has_audio(self) -> Self {
128 self.change_name("has audio")
129 }
130
131 pub fn no_audio(self) -> Self {
133 self.change_name("no audio")
134 }
135
136 pub fn has_tags(self) -> Self {
138 self.change_name("has tags")
139 }
140
141 pub fn no_tags(self) -> Self {
143 self.change_name("no tags")
144 }
145
146 pub fn untagged(self) -> Self {
148 self.change_name("untagged")
149 }
150
151 pub fn number_of_tags(self, comparator: Comparator, value: u64) -> Self {
153 self.change_name(format!("number of tags {} {}", comparator, value))
154 }
155
156 pub fn height(self, comparator: Comparator, value: u64) -> Self {
158 self.change_name(format!("height {} {}", comparator, value))
159 }
160
161 pub fn width(self, comparator: Comparator, value: u64) -> Self {
163 self.change_name(format!("width {} {}", comparator, value))
164 }
165
166 pub fn filesize(self, comparator: Comparator, value: u64, unit: FileSizeUnit) -> Self {
168 self.change_name(format!("filesize {} {} {}", comparator, value, unit))
169 }
170
171 pub fn similar_to(self, hashes: Vec<String>, distance: u32) -> Self {
173 self.change_name(format!(
174 "similar to {} with distance {}",
175 hashes.join(", "),
176 distance
177 ))
178 }
179
180 pub fn limit(self, value: u64) -> Self {
182 self.change_name(format!("limit = {}", value))
183 }
184
185 pub fn filetype(self, mimes: Vec<Mime>) -> Self {
187 self.change_name(format!(
188 "filetype = {}",
189 mimes
190 .into_iter()
191 .map(|m| m.to_string())
192 .collect::<Vec<String>>()
193 .join(", ")
194 ))
195 }
196
197 pub fn hash(self, hashes: Vec<String>) -> Self {
199 self.change_name(format!("hash = {}", hashes.join(" ")))
200 }
201
202 pub fn date_modified<D: Datelike>(self, comparator: Comparator, datetime: D) -> Self {
204 self.change_name(format!(
205 "modified date {} {}",
206 comparator,
207 format_datetime(datetime)
208 ))
209 }
210
211 pub fn time_imported<D: Datelike>(self, comparator: Comparator, datetime: D) -> Self {
213 self.change_name(format!(
214 "time imported {} {}",
215 comparator,
216 format_datetime(datetime)
217 ))
218 }
219
220 pub fn file_service(
222 self,
223 comparator: IsComparator,
224 cur_pen: CurrentlyOrPending,
225 service: ServiceName,
226 ) -> Self {
227 self.change_name(format!(
228 "file service {} {} {}",
229 comparator, cur_pen, service
230 ))
231 }
232
233 pub fn number_of_relationships(
235 self,
236 comparator: Comparator,
237 value: u64,
238 relationship: FileRelationshipType,
239 ) -> Self {
240 self.change_name(format!(
241 "num file relationships {} {} {}",
242 comparator, value, relationship
243 ))
244 }
245
246 pub fn ratio(self, wte: WiderTallerEqual, value: (u64, u64)) -> Self {
248 self.change_name(format!("ratio {} {}:{}", wte, value.0, value.1))
249 }
250
251 pub fn number_of_pixels(self, comparator: Comparator, value: u64, unit: PixelUnit) -> Self {
253 self.change_name(format!("num pixels {} {} {}", comparator, value, unit))
254 }
255
256 pub fn views(self, view_type: ViewType, comparator: Comparator, value: u64) -> Self {
258 self.change_name(format!("{} views {} {}", view_type, comparator, value))
259 }
260
261 pub fn viewtime(self, view_type: ViewType, comparator: Comparator, duration: Duration) -> Self {
263 self.change_name(format!(
264 "{} viewtime {} {}",
265 view_type,
266 comparator,
267 format_duration(duration)
268 ))
269 }
270
271 pub fn has_url_matching_regex<S: Display>(self, regex: S) -> Self {
273 self.change_name(format!("has url matching regex {}", regex))
274 }
275
276 pub fn does_not_have_url_matching_regex<S: Display>(self, regex: S) -> Self {
278 self.change_name(format!("does not have url matching regex {}", regex))
279 }
280
281 pub fn has_url_with_class<S: Display>(self, class: S) -> Self {
283 self.change_name(format!("has url with class {}", class))
284 }
285
286 pub fn does_not_have_url_with_class<S: Display>(self, class: S) -> Self {
288 self.change_name(format!("does not have url with class {}", class))
289 }
290
291 pub fn tag_namespace_as_number<S: Display>(
293 self,
294 namespace: S,
295 comparator: Comparator,
296 value: u64,
297 ) -> Self {
298 self.change_name(format!(
299 "tag as number {} {} {}",
300 namespace, comparator, value
301 ))
302 }
303
304 fn change_name<S: ToString>(mut self, value: S) -> Self {
305 self.name = value.to_string();
306
307 self
308 }
309}
310
311#[derive(Clone, Debug, PartialOrd, PartialEq)]
312pub enum Comparator {
313 Greater,
315 Less,
317 Equal,
319 Approximate,
321}
322
323impl Display for Comparator {
324 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
325 let symbol = match self {
326 Comparator::Greater => ">",
327 Comparator::Less => "<",
328 Comparator::Equal => "=",
329 Comparator::Approximate => "~=",
330 };
331 symbol.fmt(f)
332 }
333}
334
335#[derive(Clone, Debug, PartialOrd, PartialEq)]
336pub enum FileSizeUnit {
337 Bytes,
338 Kilobytes,
339 Megabytes,
340 Gigabytes,
341}
342
343impl Display for FileSizeUnit {
344 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
345 let name = match self {
346 FileSizeUnit::Bytes => "B",
347 FileSizeUnit::Kilobytes => "KB",
348 FileSizeUnit::Megabytes => "MB",
349 FileSizeUnit::Gigabytes => "GB",
350 };
351 name.fmt(f)
352 }
353}
354
355#[derive(Clone, Debug, PartialOrd, PartialEq)]
356pub enum DurationUnit {
357 Hours,
358 Minutes,
359 Seconds,
360 Milliseconds,
361}
362
363impl Display for DurationUnit {
364 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
365 let name = match self {
366 DurationUnit::Hours => "hours",
367 DurationUnit::Minutes => "minutes",
368 DurationUnit::Seconds => "seconds",
369 DurationUnit::Milliseconds => "milliseconds",
370 };
371 name.fmt(f)
372 }
373}
374
375#[derive(Clone, Debug, PartialOrd, PartialEq)]
376pub enum IsComparator {
377 Is,
378 IsNot,
379}
380
381impl Display for IsComparator {
382 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
383 let name = match self {
384 IsComparator::Is => "is",
385 IsComparator::IsNot => "is not",
386 };
387 name.fmt(f)
388 }
389}
390
391#[derive(Clone, Debug, PartialOrd, PartialEq)]
392pub enum CurrentlyOrPending {
393 CurrentlyIn,
394 PendingTo,
395}
396
397impl Display for CurrentlyOrPending {
398 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
399 let name = match self {
400 CurrentlyOrPending::CurrentlyIn => "currently in",
401 CurrentlyOrPending::PendingTo => "pending to",
402 };
403 name.fmt(f)
404 }
405}
406
407#[derive(Clone, Debug, PartialOrd, PartialEq)]
408pub enum WiderTallerEqual {
409 Wider,
410 Taller,
411 Equal,
412}
413
414impl Display for WiderTallerEqual {
415 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
416 let name = match self {
417 WiderTallerEqual::Wider => "wider than",
418 WiderTallerEqual::Taller => "taller than",
419 WiderTallerEqual::Equal => "is",
420 };
421 name.fmt(f)
422 }
423}
424
425#[derive(Clone, Debug, PartialOrd, PartialEq)]
426pub enum PixelUnit {
427 Pixels,
428 Kilopixels,
429 Megapixels,
430}
431
432impl Display for PixelUnit {
433 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
434 let name = match self {
435 PixelUnit::Pixels => "pixels",
436 PixelUnit::Kilopixels => "kilopixels",
437 PixelUnit::Megapixels => "megapixels",
438 };
439 name.fmt(f)
440 }
441}
442
443#[derive(Clone, Debug, PartialOrd, PartialEq)]
444pub enum ViewType {
445 Media,
446 Preview,
447 All,
448}
449
450impl Display for ViewType {
451 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
452 let name = match self {
453 ViewType::Media => "media",
454 ViewType::Preview => "preview",
455 ViewType::All => "all",
456 };
457 name.fmt(f)
458 }
459}
460
461#[derive(Clone, Debug, PartialOrd, PartialEq)]
462pub enum FileRelationshipType {
463 Alternates,
464 FalsePositives,
465 Duplicates,
466 PotentialDuplicates,
467}
468
469impl Display for FileRelationshipType {
470 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
471 let name = match self {
472 FileRelationshipType::Alternates => "alternates",
473 FileRelationshipType::FalsePositives => "false positives",
474 FileRelationshipType::Duplicates => "duplicates",
475 FileRelationshipType::PotentialDuplicates => "potential duplicates",
476 };
477 name.fmt(f)
478 }
479}