synapse_rpc/
resource.rs

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/// To increase server->client update efficiency, we
39/// encode common partial updates to resources with
40/// this enum.
41#[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/// Collection of mutable fields that clients
183/// can modify. Due to shared field names, all fields are aggregated
184#[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
806// TODO: Proc macros to remove this shit
807
808impl 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
1023/// Merges json objects according to RFC 7396
1024pub 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}