1use std::mem;
2use std::fmt;
3use std::borrow::Cow;
4
5use chrono::prelude::{DateTime, Utc};
6use serde;
7use serde_json as json;
8use url::Url;
9use url_serde;
10
11use super::criterion::{Field, Queryable, FNULL};
12
13#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
14#[serde(deny_unknown_fields)]
15#[serde(tag = "type")]
16#[serde(rename_all = "lowercase")]
17pub enum Resource {
18 Server(Server),
19 Torrent(Torrent),
20 Piece(Piece),
21 File(File),
22 Peer(Peer),
23 Tracker(Tracker),
24}
25
26#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq)]
27#[serde(deny_unknown_fields)]
28#[serde(rename_all = "lowercase")]
29pub enum ResourceKind {
30 Server = 0,
31 Torrent,
32 Peer,
33 File,
34 Piece,
35 Tracker,
36}
37
38#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
42#[serde(untagged)]
43#[serde(deny_unknown_fields)]
44pub enum SResourceUpdate<'a> {
45 Resource(Cow<'a, Resource>),
46 Throttle {
47 id: String,
48 #[serde(rename = "type")]
49 kind: ResourceKind,
50 throttle_up: Option<i64>,
51 throttle_down: Option<i64>,
52 },
53 Rate {
54 id: String,
55 #[serde(rename = "type")]
56 kind: ResourceKind,
57 rate_up: u64,
58 rate_down: u64,
59 },
60 UserData {
61 id: String,
62 #[serde(rename = "type")]
63 kind: ResourceKind,
64 user_data: json::Value,
65 },
66
67 ServerTransfer {
68 id: String,
69 #[serde(rename = "type")]
70 kind: ResourceKind,
71 rate_up: u64,
72 rate_down: u64,
73 transferred_up: u64,
74 transferred_down: u64,
75 ses_transferred_up: u64,
76 ses_transferred_down: u64,
77 },
78 ServerSpace {
79 id: String,
80 #[serde(rename = "type")]
81 kind: ResourceKind,
82 free_space: u64,
83 },
84 ServerToken {
85 id: String,
86 #[serde(rename = "type")]
87 kind: ResourceKind,
88 download_token: String,
89 },
90
91 TorrentStatus {
92 id: String,
93 #[serde(rename = "type")]
94 kind: ResourceKind,
95 error: Option<String>,
96 status: Status,
97 },
98 TorrentTransfer {
99 id: String,
100 #[serde(rename = "type")]
101 kind: ResourceKind,
102 rate_up: u64,
103 rate_down: u64,
104 transferred_up: u64,
105 transferred_down: u64,
106 progress: f32,
107 },
108 TorrentPeers {
109 id: String,
110 #[serde(rename = "type")]
111 kind: ResourceKind,
112 peers: u16,
113 availability: f32,
114 },
115 TorrentPicker {
116 id: String,
117 #[serde(rename = "type")]
118 kind: ResourceKind,
119 strategy: Strategy,
120 },
121 TorrentPriority {
122 id: String,
123 #[serde(rename = "type")]
124 kind: ResourceKind,
125 priority: u8,
126 },
127 TorrentPath {
128 id: String,
129 #[serde(rename = "type")]
130 kind: ResourceKind,
131 path: String,
132 },
133 TorrentPieces {
134 id: String,
135 #[serde(rename = "type")]
136 kind: ResourceKind,
137 piece_field: String,
138 },
139
140 TrackerStatus {
141 id: String,
142 #[serde(rename = "type")]
143 kind: ResourceKind,
144 last_report: DateTime<Utc>,
145 error: Option<String>,
146 },
147
148 FilePriority {
149 id: String,
150 #[serde(rename = "type")]
151 kind: ResourceKind,
152 priority: u8,
153 },
154 FileProgress {
155 id: String,
156 #[serde(rename = "type")]
157 kind: ResourceKind,
158 progress: f32,
159 },
160
161 PieceAvailable {
162 id: String,
163 #[serde(rename = "type")]
164 kind: ResourceKind,
165 available: bool,
166 },
167 PieceDownloaded {
168 id: String,
169 #[serde(rename = "type")]
170 kind: ResourceKind,
171 downloaded: bool,
172 },
173
174 PeerAvailability {
175 id: String,
176 #[serde(rename = "type")]
177 kind: ResourceKind,
178 availability: f32,
179 },
180}
181
182#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
185#[serde(deny_unknown_fields)]
186pub struct CResourceUpdate {
187 pub id: String,
188 pub path: Option<String>,
189 pub priority: Option<u8>,
190 pub strategy: Option<Strategy>,
191 #[serde(deserialize_with = "deserialize_throttle")]
192 #[serde(default)]
193 pub throttle_up: Option<Option<i64>>,
194 #[serde(deserialize_with = "deserialize_throttle")]
195 #[serde(default)]
196 pub throttle_down: Option<Option<i64>>,
197 pub user_data: Option<json::Value>,
198}
199
200#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
201#[serde(deny_unknown_fields)]
202pub struct Server {
203 pub id: String,
204 pub download_token: String,
205 pub rate_up: u64,
206 pub rate_down: u64,
207 pub throttle_up: Option<i64>,
208 pub throttle_down: Option<i64>,
209 pub transferred_up: u64,
210 pub transferred_down: u64,
211 pub ses_transferred_up: u64,
212 pub ses_transferred_down: u64,
213 pub free_space: u64,
214 pub started: DateTime<Utc>,
215 pub user_data: json::Value,
216}
217
218impl Server {
219 pub fn update(&mut self, update: SResourceUpdate) {
220 match update {
221 SResourceUpdate::Throttle {
222 throttle_up,
223 throttle_down,
224 ..
225 } => {
226 self.throttle_up = throttle_up;
227 self.throttle_down = throttle_down;
228 }
229 SResourceUpdate::ServerTransfer {
230 rate_up,
231 rate_down,
232 transferred_up,
233 transferred_down,
234 ses_transferred_up,
235 ses_transferred_down,
236 ..
237 } => {
238 self.rate_up = rate_up;
239 self.rate_down = rate_down;
240 self.transferred_up = transferred_up;
241 self.transferred_down = transferred_down;
242 self.ses_transferred_up = ses_transferred_up;
243 self.ses_transferred_down = ses_transferred_down;
244 }
245 SResourceUpdate::ServerToken { download_token, .. } => {
246 self.download_token = download_token;
247 }
248 SResourceUpdate::ServerSpace { free_space, .. } => {
249 self.free_space = free_space;
250 }
251 SResourceUpdate::Rate {
252 rate_up, rate_down, ..
253 } => {
254 self.rate_up = rate_up;
255 self.rate_down = rate_down;
256 }
257 _ => {}
258 }
259 }
260}
261
262#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
263#[serde(deny_unknown_fields)]
264pub struct Torrent {
265 pub id: String,
266 pub name: Option<String>,
267 pub creator: Option<String>,
268 pub comment: Option<String>,
269 pub private: bool,
270 pub path: String,
271 pub created: DateTime<Utc>,
272 pub modified: DateTime<Utc>,
273 pub status: Status,
274 pub error: Option<String>,
275 pub priority: u8,
276 pub progress: f32,
277 pub availability: f32,
278 pub strategy: Strategy,
279 pub rate_up: u64,
280 pub rate_down: u64,
281 pub throttle_up: Option<i64>,
282 pub throttle_down: Option<i64>,
283 pub transferred_up: u64,
284 pub transferred_down: u64,
285 pub peers: u16,
286 pub trackers: u8,
287 pub tracker_urls: Vec<String>,
288 pub size: Option<u64>,
289 pub pieces: Option<u64>,
290 pub piece_size: Option<u32>,
291 pub piece_field: String,
292 pub files: Option<u32>,
293 pub user_data: json::Value,
294}
295
296impl Torrent {
297 pub fn update(&mut self, update: SResourceUpdate) {
298 self.modified = Utc::now();
299 match update {
300 SResourceUpdate::Throttle {
301 throttle_up,
302 throttle_down,
303 ..
304 } => {
305 self.throttle_up = throttle_up;
306 self.throttle_down = throttle_down;
307 }
308 SResourceUpdate::TorrentStatus { error, status, .. } => {
309 self.error = error;
310 self.status = status;
311 }
312 SResourceUpdate::TorrentTransfer {
313 rate_up,
314 rate_down,
315 transferred_up,
316 transferred_down,
317 progress,
318 ..
319 } => {
320 self.rate_up = rate_up;
321 self.rate_down = rate_down;
322 self.transferred_up = transferred_up;
323 self.transferred_down = transferred_down;
324 self.progress = progress;
325 }
326 SResourceUpdate::TorrentPeers {
327 peers,
328 availability,
329 ..
330 } => {
331 self.peers = peers;
332 self.availability = availability;
333 }
334 SResourceUpdate::TorrentPicker { strategy, .. } => {
335 self.strategy = strategy;
336 }
337 SResourceUpdate::TorrentPriority { priority, .. } => {
338 self.priority = priority;
339 }
340 SResourceUpdate::TorrentPieces { piece_field, .. } => {
341 self.piece_field = piece_field;
342 }
343 _ => {}
344 }
345 }
346}
347
348#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq)]
349#[serde(rename_all = "lowercase")]
350#[serde(deny_unknown_fields)]
351pub enum Status {
352 Pending,
353 Magnet,
354 Paused,
355 Leeching,
356 Idle,
357 Seeding,
358 Hashing,
359 Error,
360}
361
362#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq)]
363#[serde(rename_all = "lowercase")]
364#[serde(deny_unknown_fields)]
365pub enum Strategy {
366 Rarest,
367 Sequential,
368}
369
370impl Strategy {
371 pub fn as_str(&self) -> &'static str {
372 match self {
373 &Strategy::Rarest => "rarest",
374 &Strategy::Sequential => "sequential",
375 }
376 }
377}
378
379#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
380#[serde(deny_unknown_fields)]
381pub struct Piece {
382 pub id: String,
383 pub torrent_id: String,
384 pub available: bool,
385 pub downloaded: bool,
386 pub index: u32,
387 pub user_data: json::Value,
388}
389
390impl Piece {
391 pub fn update(&mut self, update: SResourceUpdate) {
392 match update {
393 SResourceUpdate::PieceAvailable { available, .. } => {
394 self.available = available;
395 }
396 SResourceUpdate::PieceDownloaded { downloaded, .. } => {
397 self.downloaded = downloaded;
398 }
399 _ => {}
400 }
401 }
402}
403
404#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
405#[serde(deny_unknown_fields)]
406pub struct File {
407 pub id: String,
408 pub torrent_id: String,
409 pub path: String,
410 pub progress: f32,
411 pub availability: f32,
412 pub priority: u8,
413 pub size: u64,
414 pub user_data: json::Value,
415}
416
417impl File {
418 pub fn update(&mut self, update: SResourceUpdate) {
419 match update {
420 SResourceUpdate::FilePriority { priority, .. } => {
421 self.priority = priority;
422 }
423 SResourceUpdate::FileProgress { progress, .. } => {
424 self.progress = progress;
425 }
426 _ => {}
427 }
428 }
429}
430
431#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
432#[serde(deny_unknown_fields)]
433pub struct Peer {
434 pub id: String,
435 pub torrent_id: String,
436 pub client_id: String,
437 pub ip: String,
438 pub rate_up: u64,
439 pub rate_down: u64,
440 pub availability: f32,
441 pub user_data: json::Value,
442}
443
444impl Peer {
445 pub fn update(&mut self, update: SResourceUpdate) {
446 match update {
447 SResourceUpdate::Rate {
448 rate_up, rate_down, ..
449 } => {
450 self.rate_up = rate_up;
451 self.rate_down = rate_down;
452 }
453 SResourceUpdate::PeerAvailability { availability, .. } => {
454 self.availability = availability;
455 }
456 _ => {}
457 }
458 }
459}
460
461#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
462#[serde(deny_unknown_fields)]
463pub struct Tracker {
464 pub id: String,
465 pub torrent_id: String,
466 #[serde(with = "url_serde")]
467 pub url: Url,
468 pub last_report: DateTime<Utc>,
469 pub error: Option<String>,
470 pub user_data: json::Value,
471}
472
473impl Tracker {
474 pub fn update(&mut self, update: SResourceUpdate) {
475 match update {
476 SResourceUpdate::TrackerStatus {
477 last_report, error, ..
478 } => {
479 self.last_report = last_report;
480 self.error = error;
481 }
482 _ => {}
483 }
484 }
485}
486
487impl<'a> SResourceUpdate<'a> {
488 pub fn id(&self) -> &str {
489 match self {
490 &SResourceUpdate::Resource(ref r) => r.id(),
491 &SResourceUpdate::Throttle { ref id, .. }
492 | &SResourceUpdate::Rate { ref id, .. }
493 | &SResourceUpdate::UserData { ref id, .. }
494 | &SResourceUpdate::ServerTransfer { ref id, .. }
495 | &SResourceUpdate::ServerToken { ref id, .. }
496 | &SResourceUpdate::ServerSpace { ref id, .. }
497 | &SResourceUpdate::TorrentStatus { ref id, .. }
498 | &SResourceUpdate::TorrentTransfer { ref id, .. }
499 | &SResourceUpdate::TorrentPeers { ref id, .. }
500 | &SResourceUpdate::TorrentPicker { ref id, .. }
501 | &SResourceUpdate::TorrentPriority { ref id, .. }
502 | &SResourceUpdate::TorrentPath { ref id, .. }
503 | &SResourceUpdate::TorrentPieces { ref id, .. }
504 | &SResourceUpdate::FilePriority { ref id, .. }
505 | &SResourceUpdate::FileProgress { ref id, .. }
506 | &SResourceUpdate::TrackerStatus { ref id, .. }
507 | &SResourceUpdate::PeerAvailability { ref id, .. }
508 | &SResourceUpdate::PieceAvailable { ref id, .. }
509 | &SResourceUpdate::PieceDownloaded { ref id, .. } => id,
510 }
511 }
512}
513
514impl Resource {
515 pub fn id(&self) -> &str {
516 match self {
517 &Resource::Server(ref t) => &t.id,
518 &Resource::Torrent(ref t) => &t.id,
519 &Resource::File(ref t) => &t.id,
520 &Resource::Piece(ref t) => &t.id,
521 &Resource::Peer(ref t) => &t.id,
522 &Resource::Tracker(ref t) => &t.id,
523 }
524 }
525
526 pub fn torrent_id(&self) -> Option<&str> {
527 match self {
528 &Resource::File(ref t) => Some(&t.torrent_id),
529 &Resource::Piece(ref t) => Some(&t.torrent_id),
530 &Resource::Peer(ref t) => Some(&t.torrent_id),
531 &Resource::Tracker(ref t) => Some(&t.torrent_id),
532 _ => None,
533 }
534 }
535
536 pub fn kind(&self) -> ResourceKind {
537 match self {
538 &Resource::Server(_) => ResourceKind::Server,
539 &Resource::Torrent(_) => ResourceKind::Torrent,
540 &Resource::File(_) => ResourceKind::File,
541 &Resource::Piece(_) => ResourceKind::Piece,
542 &Resource::Peer(_) => ResourceKind::Peer,
543 &Resource::Tracker(_) => ResourceKind::Tracker,
544 }
545 }
546
547 pub fn user_data(&mut self) -> &mut json::Value {
548 match self {
549 &mut Resource::Server(ref mut r) => &mut r.user_data,
550 &mut Resource::Torrent(ref mut r) => &mut r.user_data,
551 &mut Resource::File(ref mut r) => &mut r.user_data,
552 &mut Resource::Piece(ref mut r) => &mut r.user_data,
553 &mut Resource::Peer(ref mut r) => &mut r.user_data,
554 &mut Resource::Tracker(ref mut r) => &mut r.user_data,
555 }
556 }
557
558 pub fn as_server(&self) -> &Server {
559 match self {
560 &Resource::Server(ref s) => s,
561 _ => panic!(),
562 }
563 }
564
565 pub fn as_torrent(&self) -> &Torrent {
566 match self {
567 &Resource::Torrent(ref t) => t,
568 _ => panic!(),
569 }
570 }
571
572 pub fn as_torrent_mut(&mut self) -> &mut Torrent {
573 match self {
574 &mut Resource::Torrent(ref mut t) => t,
575 _ => panic!(),
576 }
577 }
578
579 pub fn as_file(&self) -> &File {
580 match self {
581 &Resource::File(ref f) => f,
582 _ => panic!(),
583 }
584 }
585
586 pub fn as_piece(&self) -> &Piece {
587 match self {
588 &Resource::Piece(ref p) => p,
589 _ => panic!(),
590 }
591 }
592
593 pub fn as_peer(&self) -> &Peer {
594 match self {
595 &Resource::Peer(ref p) => p,
596 _ => panic!(),
597 }
598 }
599
600 pub fn as_tracker(&self) -> &Tracker {
601 match self {
602 &Resource::Tracker(ref t) => t,
603 _ => panic!(),
604 }
605 }
606
607 pub fn update(&mut self, update: SResourceUpdate) {
608 match self {
609 &mut Resource::Server(ref mut s) => {
610 s.update(update);
611 }
612 &mut Resource::Torrent(ref mut t) => {
613 t.update(update);
614 }
615 &mut Resource::Piece(ref mut p) => {
616 p.update(update);
617 }
618 &mut Resource::File(ref mut f) => {
619 f.update(update);
620 }
621 &mut Resource::Peer(ref mut p) => {
622 p.update(update);
623 }
624 &mut Resource::Tracker(ref mut t) => {
625 t.update(update);
626 }
627 }
628 }
629}
630
631impl fmt::Display for Resource {
632 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
633 match self {
634 &Resource::Server(ref t) => {
635 write!(f, "Server {{")?;
636 write!(f, "\n")?;
637 write!(f, " id: {}", t.id)?;
638 write!(f, "\n")?;
639 write!(f, " upload: {} B/s", t.rate_up)?;
640 write!(f, "\n")?;
641 write!(f, " download: {} B/s", t.rate_down)?;
642 write!(f, "\n")?;
643 match t.throttle_up {
644 Some(u) if u >= 0 => {
645 write!(f, " throttle up: {} B/s", u)?;
646 }
647 Some(u) => {
648 write!(f, " throttle up: invalid({})", u)?;
649 }
650 None => {
651 write!(f, " throttle up: unlimited")?;
652 }
653 }
654 write!(f, "\n")?;
655 match t.throttle_down {
656 Some(u) if u >= 0 => {
657 write!(f, " throttle down: {} B/s", u)?;
658 }
659 Some(u) => {
660 write!(f, " throttle down: invalid({})", u)?;
661 }
662 None => {
663 write!(f, " throttle down: unlimited")?;
664 }
665 }
666 write!(f, "\n")?;
667 write!(f, " uploaded: {} B", t.transferred_up)?;
668 write!(f, "\n")?;
669 write!(f, " downloaded: {} B", t.transferred_down)?;
670 write!(f, "\n")?;
671 write!(f, " session upload: {} B", t.ses_transferred_up)?;
672 write!(f, "\n")?;
673 write!(f, " session download: {} B", t.ses_transferred_down)?;
674 write!(f, "\n")?;
675 write!(f, " started at: {}", t.started)?;
676 write!(f, "\n")?;
677 write!(f, "}}")?;
678 }
679 &Resource::Torrent(ref t) => {
680 write!(f, "Torrent {{")?;
681 write!(f, "\n")?;
682 write!(f, " id: {}", t.id)?;
683 write!(f, "\n")?;
684 write!(
685 f,
686 " name: {}",
687 if let Some(ref n) = t.name {
688 n.as_str()
689 } else {
690 "Unknown (magnet)"
691 }
692 )?;
693 write!(f, "\n")?;
694 write!(f, " path: {}", t.path)?;
695 write!(f, "\n")?;
696 write!(f, " created at: {}", t.created)?;
697 write!(f, "\n")?;
698 write!(f, " modified at: {}", t.modified)?;
699 write!(f, "\n")?;
700 write!(f, " status: {}", t.status.as_str())?;
701 write!(f, "\n")?;
702 if let Some(ref e) = t.error {
703 write!(f, " error: {}", e)?;
704 write!(f, "\n")?;
705 }
706 write!(f, " priority: {}", t.priority)?;
707 write!(f, "\n")?;
708 write!(f, " progress: {}", t.progress)?;
709 write!(f, "\n")?;
710 write!(f, " availability: {}", t.availability)?;
711 write!(f, "\n")?;
712 write!(f, " strategy: {:?}", t.strategy)?;
713 write!(f, "\n")?;
714 write!(f, " upload: {} B/s", t.rate_up)?;
715 write!(f, "\n")?;
716 write!(f, " download: {} B/s", t.rate_down)?;
717 write!(f, "\n")?;
718 match t.throttle_up {
719 Some(u) if u >= 0 => {
720 write!(f, " throttle up: {} B/s", u)?;
721 }
722 Some(_) => {
723 write!(f, " throttle up: unlimited")?;
724 }
725 None => {
726 write!(f, " throttle up: server")?;
727 }
728 }
729 write!(f, "\n")?;
730 match t.throttle_down {
731 Some(u) if u >= 0 => {
732 write!(f, " throttle down: {} B/s", u)?;
733 }
734 Some(_) => {
735 write!(f, " throttle down: unlimited")?;
736 }
737 None => {
738 write!(f, " throttle down: server")?;
739 }
740 }
741 write!(f, "\n")?;
742 write!(f, " uploaded: {} B", t.transferred_up)?;
743 write!(f, "\n")?;
744 write!(f, " downloaded: {} B", t.transferred_down)?;
745 write!(f, "\n")?;
746 write!(f, " peers: {}", t.peers)?;
747 write!(f, "\n")?;
748 write!(f, " trackers: {}", t.trackers)?;
749 write!(f, "\n")?;
750 if let Some(s) = t.size {
751 write!(f, " size: {} B", s)?;
752 } else {
753 write!(f, " size: Unknown (magnet)")?;
754 }
755 write!(f, "\n")?;
756 if let Some(p) = t.pieces {
757 write!(f, " pieces: {}", p)?;
758 } else {
759 write!(f, " pieces: Unknown (magnet)")?;
760 }
761 write!(f, "\n")?;
762 if let Some(p) = t.piece_size {
763 write!(f, " piece size: {} B", p)?;
764 } else {
765 write!(f, " piece size: Unknown (magnet)")?;
766 }
767 write!(f, "\n")?;
768 if let Some(files) = t.files {
769 write!(f, " files: {}", files)?;
770 } else {
771 write!(f, " files: Unknown (magnet)")?;
772 }
773 write!(f, "\n")?;
774 write!(f, "}}")?;
775 }
776 &Resource::File(ref t) => {
777 write!(f, "{:#?}", t)?;
778 }
779 &Resource::Piece(ref t) => {
780 write!(f, "{:#?}", t)?;
781 }
782 &Resource::Peer(ref t) => {
783 write!(f, "{:#?}", t)?;
784 }
785 &Resource::Tracker(ref t) => {
786 write!(f, "{:#?}", t)?;
787 }
788 }
789 Ok(())
790 }
791}
792
793fn deserialize_throttle<'de, D>(de: D) -> Result<Option<Option<i64>>, D::Error>
794where
795 D: serde::Deserializer<'de>,
796{
797 let deser_result = serde::Deserialize::deserialize(de)?;
798 match deser_result {
799 json::Value::Null => Ok(Some(None)),
800 json::Value::Number(ref i) if i.is_i64() => Ok(Some(Some(i.as_i64().unwrap()))),
801 json::Value::Number(_) => Err(serde::de::Error::custom("Throttle must not be a float")),
802 _ => Err(serde::de::Error::custom("Throttle must be number or null")),
803 }
804}
805
806impl Queryable for Resource {
809 fn field(&self, f: &str) -> Option<Field> {
810 match self {
811 &Resource::Server(ref t) => t.field(f),
812 &Resource::Torrent(ref t) => t.field(f),
813 &Resource::File(ref t) => t.field(f),
814 &Resource::Piece(ref t) => t.field(f),
815 &Resource::Peer(ref t) => t.field(f),
816 &Resource::Tracker(ref t) => t.field(f),
817 }
818 }
819}
820
821impl Queryable for json::Value {
822 fn field(&self, f: &str) -> Option<Field> {
823 match self.pointer(f) {
824 Some(&json::Value::Null) => Some(FNULL),
825 Some(&json::Value::Bool(b)) => Some(Field::B(b)),
826 Some(&json::Value::Number(ref n)) => {
827 if n.is_f64() {
828 Some(Field::F(n.as_f64().unwrap() as f32))
829 } else {
830 Some(Field::N(n.as_i64().unwrap()))
831 }
832 }
833 Some(&json::Value::String(ref s)) => Some(Field::S(s)),
834 Some(&json::Value::Array(ref a)) => {
835 Some(Field::V(a.iter().filter_map(|v| v.field("")).collect()))
836 }
837 Some(&json::Value::Object(_)) => None,
838 None => None,
839 }
840 }
841}
842
843impl Queryable for Server {
844 fn field(&self, f: &str) -> Option<Field> {
845 match f {
846 "id" => Some(Field::S(&self.id)),
847
848 "rate_up" => Some(Field::N(self.rate_up as i64)),
849 "rate_down" => Some(Field::N(self.rate_down as i64)),
850 "throttle_up" => Some(self.throttle_up.map(|v| Field::N(v)).unwrap_or(FNULL)),
851 "throttle_down" => Some(self.throttle_down.map(|v| Field::N(v)).unwrap_or(FNULL)),
852 "transferred_up" => Some(Field::N(self.transferred_up as i64)),
853 "transferred_down" => Some(Field::N(self.transferred_down as i64)),
854 "ses_transferred_up" => Some(Field::N(self.ses_transferred_up as i64)),
855 "ses_transferred_down" => Some(Field::N(self.ses_transferred_down as i64)),
856 "free_space" => Some(Field::N(self.free_space as i64)),
857
858 "started" => Some(Field::D(self.started)),
859
860 _ if f.starts_with("user_data") => self.user_data.field(&f[9..]),
861
862 _ => None,
863 }
864 }
865}
866
867impl Queryable for Torrent {
868 fn field(&self, f: &str) -> Option<Field> {
869 match f {
870 "id" => Some(Field::S(&self.id)),
871 "name" => Some(
872 self.name
873 .as_ref()
874 .map(|v| Field::S(v.as_str()))
875 .unwrap_or(FNULL),
876 ),
877 "private" => Some(Field::B(self.private)),
878 "creator" => Some(
879 self.creator
880 .as_ref()
881 .map(|v| Field::S(v.as_str()))
882 .unwrap_or(FNULL),
883 ),
884 "comment" => Some(
885 self.comment
886 .as_ref()
887 .map(|v| Field::S(v.as_str()))
888 .unwrap_or(FNULL),
889 ),
890 "path" => Some(Field::S(&self.path)),
891 "status" => Some(Field::S(self.status.as_str())),
892 "error" => Some(
893 self.error
894 .as_ref()
895 .map(|v| Field::S(v.as_str()))
896 .unwrap_or(FNULL),
897 ),
898
899 "priority" => Some(Field::N(self.priority as i64)),
900 "rate_up" => Some(Field::N(self.rate_up as i64)),
901 "rate_down" => Some(Field::N(self.rate_down as i64)),
902 "throttle_up" => Some(self.throttle_up.map(|v| Field::N(v)).unwrap_or(FNULL)),
903 "throttle_down" => Some(self.throttle_down.map(|v| Field::N(v)).unwrap_or(FNULL)),
904 "transferred_up" => Some(Field::N(self.transferred_up as i64)),
905 "transferred_down" => Some(Field::N(self.transferred_down as i64)),
906 "peers" => Some(Field::N(self.peers as i64)),
907 "trackers" => Some(Field::N(self.trackers as i64)),
908 "tracker_urls" => Some(Field::V(
909 self.tracker_urls.iter().map(|url| Field::S(url)).collect(),
910 )),
911 "size" => Some(self.size.map(|v| Field::N(v as i64)).unwrap_or(FNULL)),
912 "pieces" => Some(self.pieces.map(|v| Field::N(v as i64)).unwrap_or(FNULL)),
913 "piece_size" => Some(self.piece_size.map(|v| Field::N(v as i64)).unwrap_or(FNULL)),
914 "files" => Some(self.files.map(|v| Field::N(v as i64)).unwrap_or(FNULL)),
915
916 "created" => Some(Field::D(self.created)),
917 "modified" => Some(Field::D(self.modified)),
918
919 "progress" => Some(Field::F(self.progress)),
920 "availability" => Some(Field::F(self.availability)),
921
922 "strategy" => Some(Field::S(self.strategy.as_str())),
923
924 _ if f.starts_with("user_data") => self.user_data.field(&f[9..]),
925
926 _ => None,
927 }
928 }
929}
930
931impl Queryable for Piece {
932 fn field(&self, f: &str) -> Option<Field> {
933 match f {
934 "id" => Some(Field::S(&self.id)),
935 "torrent_id" => Some(Field::S(&self.torrent_id)),
936
937 "available" => Some(Field::B(self.available)),
938 "downloaded" => Some(Field::B(self.downloaded)),
939
940 _ if f.starts_with("user_data") => self.user_data.field(&f[9..]),
941
942 _ => None,
943 }
944 }
945}
946
947impl Queryable for File {
948 fn field(&self, f: &str) -> Option<Field> {
949 match f {
950 "id" => Some(Field::S(&self.id)),
951 "torrent_id" => Some(Field::S(&self.torrent_id)),
952 "path" => Some(Field::S(&self.path)),
953
954 "priority" => Some(Field::N(self.priority as i64)),
955
956 "progress" => Some(Field::F(self.progress)),
957
958 _ if f.starts_with("user_data") => self.user_data.field(&f[9..]),
959
960 _ => None,
961 }
962 }
963}
964
965impl Queryable for Peer {
966 fn field(&self, f: &str) -> Option<Field> {
967 match f {
968 "id" => Some(Field::S(&self.id)),
969 "torrent_id" => Some(Field::S(&self.torrent_id)),
970 "ip" => Some(Field::S(&self.ip)),
971
972 "rate_up" => Some(Field::N(self.rate_up as i64)),
973 "rate_down" => Some(Field::N(self.rate_down as i64)),
974
975 "availability" => Some(Field::F(self.availability)),
976
977 "client_id" => Some(Field::S(&self.client_id)),
978
979 _ if f.starts_with("user_data") => self.user_data.field(&f[9..]),
980
981 _ => None,
982 }
983 }
984}
985
986impl Queryable for Tracker {
987 fn field(&self, f: &str) -> Option<Field> {
988 match f {
989 "id" => Some(Field::S(&self.id)),
990 "torrent_id" => Some(Field::S(&self.torrent_id)),
991 "url" => Some(Field::S(self.url.as_str())),
992 "error" => Some(
993 self.error
994 .as_ref()
995 .map(|v| Field::S(v.as_str()))
996 .unwrap_or(FNULL),
997 ),
998
999 "last_report" => Some(Field::D(self.last_report)),
1000
1001 _ if f.starts_with("user_data") => self.user_data.field(&f[9..]),
1002
1003 _ => None,
1004 }
1005 }
1006}
1007
1008impl Status {
1009 pub fn as_str(&self) -> &'static str {
1010 match *self {
1011 Status::Pending => "pending",
1012 Status::Paused => "paused",
1013 Status::Leeching => "leeching",
1014 Status::Idle => "idle",
1015 Status::Seeding => "seeding",
1016 Status::Hashing => "hashing",
1017 Status::Magnet => "magnet",
1018 Status::Error => "error",
1019 }
1020 }
1021}
1022
1023pub fn merge_json(original: &mut json::Value, update: &mut json::Value) {
1025 match (original, update) {
1026 (&mut json::Value::Object(ref mut o), &mut json::Value::Object(ref mut u)) => {
1027 for (k, v) in u.iter_mut() {
1028 if v.is_null() {
1029 o.remove(k);
1030 } else if o.contains_key(k) {
1031 merge_json(o.get_mut(k).unwrap(), v);
1032 } else {
1033 o.insert(k.to_owned(), mem::replace(v, json::Value::Null));
1034 }
1035 }
1036 }
1037 (o, u) => {
1038 mem::swap(o, u);
1039 }
1040 }
1041}
1042
1043impl Default for Status {
1044 fn default() -> Self {
1045 Status::Pending
1046 }
1047}
1048
1049impl Default for Server {
1050 fn default() -> Self {
1051 Server {
1052 id: "".to_owned(),
1053 rate_up: 0,
1054 rate_down: 0,
1055 throttle_up: None,
1056 throttle_down: None,
1057 transferred_up: 0,
1058 transferred_down: 0,
1059 ses_transferred_up: 0,
1060 ses_transferred_down: 0,
1061 free_space: 0,
1062 download_token: "".to_owned(),
1063 started: Utc::now(),
1064 user_data: json::Value::Null,
1065 }
1066 }
1067}
1068
1069impl Default for Torrent {
1070 fn default() -> Self {
1071 Torrent {
1072 id: "".to_owned(),
1073 name: None,
1074 comment: None,
1075 creator: None,
1076 private: false,
1077 path: "".to_owned(),
1078 created: Utc::now(),
1079 modified: Utc::now(),
1080 status: Default::default(),
1081 error: None,
1082 priority: 0,
1083 progress: 0.,
1084 availability: 0.,
1085 strategy: Strategy::Rarest,
1086 rate_up: 0,
1087 rate_down: 0,
1088 throttle_up: None,
1089 throttle_down: None,
1090 transferred_up: 0,
1091 transferred_down: 0,
1092 peers: 0,
1093 trackers: 0,
1094 tracker_urls: vec![],
1095 size: None,
1096 pieces: None,
1097 piece_size: None,
1098 piece_field: "".to_owned(),
1099 files: None,
1100 user_data: json::Value::Null,
1101 }
1102 }
1103}
1104
1105impl Default for Tracker {
1106 fn default() -> Self {
1107 Tracker {
1108 id: "".to_owned(),
1109 torrent_id: "".to_owned(),
1110 url: Url::parse("http://my.tracker/announce").unwrap(),
1111 last_report: Utc::now(),
1112 error: None,
1113 user_data: json::Value::Null,
1114 }
1115 }
1116}