bgpkit_broker/
item.rs

1//! BrokerItem module define the broker search results
2use serde::{Deserialize, Serialize};
3use std::cmp::Ordering;
4use std::fmt::{Display, Formatter};
5
6/// BGPKIT Broker data item.
7///
8/// The fields are:
9/// - [ts_start][BrokerItem::ts_start]: the starting timestamp of the data file
10/// - [ts_end][BrokerItem::ts_end]: the ending timestamp of the data file
11/// - [collector_id][BrokerItem::collector_id]: the collector id of the item: e.g. `rrc00`
12/// - [data_type][BrokerItem::data_type]: type of the data item: `rib` or `updates`
13/// - [url][BrokerItem::url]: the URL to the data item file
14/// - [rough_size][BrokerItem::rough_size]: rough file size extracted from the collector webpage
15/// - [exact_size][BrokerItem::exact_size]: exact file size extracted by crawling the file
16///
17/// An array of [BrokerItem]s can be sorted with the following order:
18/// 1. smaller timestamp before larger timestamp
19/// 2. RIB before updates
20/// 3. then alphabetical order on collector ID (route-views before rrc)
21#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, Hash)]
22#[cfg_attr(feature = "cli", derive(tabled::Tabled))]
23pub struct BrokerItem {
24    /// start timestamp
25    pub ts_start: chrono::NaiveDateTime,
26    /// end timestamps
27    pub ts_end: chrono::NaiveDateTime,
28    /// the collector id of the item: e.g. `rrc00`
29    pub collector_id: String,
30    /// type of the data item: `rib` or `updates`
31    pub data_type: String,
32    /// the URL to the data item file
33    pub url: String,
34    /// rough file size extracted from the hosting site page
35    pub rough_size: i64,
36    /// exact file size extracted by crawling the file
37    pub exact_size: i64,
38}
39
40impl BrokerItem {
41    /// Checks if the data type is "rib" (i.e. RIB dump).
42    ///
43    /// # Return
44    /// Returns `true` if the data type is "rib", otherwise `false`.
45    pub fn is_rib(&self) -> bool {
46        self.data_type.as_str() == "rib"
47    }
48}
49
50#[allow(clippy::unwrap_used)]
51impl Display for BrokerItem {
52    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
53        write!(f, "{}", serde_json::to_string(self).unwrap())
54    }
55}
56
57impl PartialOrd for BrokerItem {
58    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
59        Some(self.cmp(other))
60    }
61}
62
63impl Ord for BrokerItem {
64    fn cmp(&self, other: &Self) -> Ordering {
65        // compare BrokerItems with the following sequence
66        // 1. ts_start
67        // 2. data_type
68        // 3. collector_id
69        self.ts_start
70            .cmp(&other.ts_start) // smaller timestamp comes earlier
71            .then(self.data_type.cmp(&other.data_type)) // RIB before updates on the same timestamp
72            .then(self.collector_id.cmp(&other.collector_id)) // route-viewsX before rrcX
73    }
74}
75
76#[cfg(test)]
77mod tests {
78    use super::*;
79    use chrono::DateTime;
80
81    #[test]
82    fn test_sorting() {
83        let mut items = vec![
84            BrokerItem {
85                ts_start: DateTime::from_timestamp(10, 0).unwrap().naive_utc(),
86                ts_end: Default::default(),
87                collector_id: "rrc00".to_string(),
88                data_type: "updates".to_string(),
89                url: "".to_string(),
90                rough_size: 0,
91                exact_size: 0,
92            },
93            BrokerItem {
94                ts_start: DateTime::from_timestamp(9, 0).unwrap().naive_utc(),
95                ts_end: Default::default(),
96                collector_id: "rrc00".to_string(),
97                data_type: "updates".to_string(),
98                url: "".to_string(),
99                rough_size: 0,
100                exact_size: 0,
101            },
102            BrokerItem {
103                ts_start: DateTime::from_timestamp(10, 0).unwrap().naive_utc(),
104                ts_end: Default::default(),
105                collector_id: "rrc00".to_string(),
106                data_type: "rib".to_string(),
107                url: "".to_string(),
108                rough_size: 0,
109                exact_size: 0,
110            },
111            BrokerItem {
112                ts_start: DateTime::from_timestamp(10, 0).unwrap().naive_utc(),
113                ts_end: Default::default(),
114                collector_id: "route-views2".to_string(),
115                data_type: "rib".to_string(),
116                url: "".to_string(),
117                rough_size: 0,
118                exact_size: 0,
119            },
120        ];
121        let correct_items = vec![
122            BrokerItem {
123                ts_start: DateTime::from_timestamp(9, 0).unwrap().naive_utc(),
124                ts_end: Default::default(),
125                collector_id: "rrc00".to_string(),
126                data_type: "updates".to_string(),
127                url: "".to_string(),
128                rough_size: 0,
129                exact_size: 0,
130            },
131            BrokerItem {
132                ts_start: DateTime::from_timestamp(10, 0).unwrap().naive_utc(),
133                ts_end: Default::default(),
134                collector_id: "route-views2".to_string(),
135                data_type: "rib".to_string(),
136                url: "".to_string(),
137                rough_size: 0,
138                exact_size: 0,
139            },
140            BrokerItem {
141                ts_start: DateTime::from_timestamp(10, 0).unwrap().naive_utc(),
142                ts_end: Default::default(),
143                collector_id: "rrc00".to_string(),
144                data_type: "rib".to_string(),
145                url: "".to_string(),
146                rough_size: 0,
147                exact_size: 0,
148            },
149            BrokerItem {
150                ts_start: DateTime::from_timestamp(10, 0).unwrap().naive_utc(),
151                ts_end: Default::default(),
152                collector_id: "rrc00".to_string(),
153                data_type: "updates".to_string(),
154                url: "".to_string(),
155                rough_size: 0,
156                exact_size: 0,
157            },
158        ];
159
160        assert_ne!(items, correct_items);
161        items.sort();
162        assert_eq!(items, correct_items);
163    }
164}