opensky_network/
flights.rs1use std::sync::Arc;
3
4use crate::errors::Error;
5use log::{debug, warn};
6use serde::{Deserialize, Serialize};
7
8#[derive(Debug, Serialize, Deserialize)]
9pub struct Flight {
11 pub icao24: String,
14 #[serde(alias = "firstSeen")]
15 pub first_seen: u64,
18 #[serde(alias = "estDepartureAirport")]
19 pub est_departure_airport: Option<String>,
22 #[serde(alias = "lastSeen")]
23 pub last_seen: u64,
26 #[serde(alias = "estArrivalAirport")]
27 pub est_arrival_airport: Option<String>,
30 pub callsign: Option<String>,
34 #[serde(alias = "estDepartureAirportHorizDistance")]
35 pub est_departure_airport_horiz_distance: Option<u32>,
38 #[serde(alias = "estDepartureAirportVertDistance")]
39 pub est_departure_airport_vert_distance: Option<u32>,
42 #[serde(alias = "estArrivalAirportHorizDistance")]
43 pub est_arrival_airport_horiz_distance: Option<u32>,
46 #[serde(alias = "estArrivalAirportVertDistance")]
47 pub est_arrival_airport_vert_distance: Option<u32>,
50 #[serde(alias = "departureAirportCandidatesCount")]
51 pub departure_airport_candidates_count: u16,
54 #[serde(alias = "arrivalAirportCandidatesCount")]
55 pub arrival_airport_candidates_count: u16,
57}
58
59#[derive(Debug, Clone)]
60pub enum FlightsRequestType {
61 All,
62 Aircraft(String),
63 Arrival(String),
64 Departure(String),
65}
66
67impl FlightsRequestType {
68 pub fn max_interval(&self) -> u64 {
69 match self {
70 FlightsRequestType::All => 2 * 60 * 60,
72 FlightsRequestType::Aircraft(_) => 30 * 24 * 60 * 60,
74 FlightsRequestType::Arrival(_) => 7 * 24 * 60 * 60,
76 FlightsRequestType::Departure(_) => 7 * 24 * 60 * 60,
78 }
79 }
80
81 fn endpoint(&self) -> &'static str {
82 match self {
83 FlightsRequestType::All => "all",
84 FlightsRequestType::Aircraft(_) => "aircraft",
85 FlightsRequestType::Arrival(_) => "arrival",
86 FlightsRequestType::Departure(_) => "departure",
87 }
88 }
89}
90
91#[derive(Debug, Clone)]
92pub struct FlightsRequest {
93 login: Option<Arc<(String, String)>>,
94 begin: u64,
95 end: u64,
96 request_type: FlightsRequestType,
97}
98
99impl FlightsRequest {
100 pub async fn send(&self) -> Result<Vec<Flight>, Error> {
101 let login_part = if let Some(login) = &self.login {
102 format!("{}:{}@", login.0, login.1)
103 } else {
104 String::new()
105 };
106
107 let endpoint = self.request_type.endpoint();
108 let interval = self.end - self.begin;
109 if interval > self.request_type.max_interval() {
110 warn!(
111 "Interval ({} secs) is larger than limits ({} secs)",
112 interval,
113 self.request_type.max_interval()
114 );
115 }
116
117 let mut args = format!("?begin={}&end={}", self.begin, self.end);
118 let additional_filters = match &self.request_type {
119 FlightsRequestType::All => String::new(),
120 FlightsRequestType::Aircraft(address) => format!("&icao24={}", address),
121 FlightsRequestType::Arrival(airport_icao) => format!("&airport={}", airport_icao),
122 FlightsRequestType::Departure(airport_icao) => format!("&airport={}", airport_icao),
123 };
124 args.push_str(&additional_filters);
125
126 let url = format!(
127 "https://{}opensky-network.org/api/flights/{}{}",
128 login_part, endpoint, args
129 );
130
131 debug!("Request url = {}", url);
132
133 let res = reqwest::get(url).await?;
134
135 match res.status() {
136 reqwest::StatusCode::OK => {
137 let bytes = res.bytes().await?.to_vec();
138
139 match serde_json::from_slice(&bytes) {
140 Ok(result) => Ok(result),
141 Err(e) => Err(Error::InvalidJson(e)),
142 }
143 }
144 reqwest::StatusCode::NOT_FOUND => Ok(Vec::new()),
145 status => Err(Error::Http(status)),
146 }
147 }
148}
149
150#[derive(Debug, Clone)]
151pub struct FlightsRequestBuilder {
152 inner: FlightsRequest,
153}
154
155impl FlightsRequestBuilder {
156 pub fn new(login: Option<Arc<(String, String)>>, begin: u64, end: u64) -> Self {
157 Self {
160 inner: FlightsRequest {
161 login,
162 begin,
163 end,
164 request_type: FlightsRequestType::All,
165 },
166 }
167 }
168
169 pub fn in_interval(&mut self, begin: u64, end: u64) -> &mut Self {
178 assert!(
179 end - begin <= 7200,
180 "Interval must not span greater than 2 hours"
181 );
182 assert!(end > begin, "End time must be greater than begin time");
183 self.inner.begin = begin;
184 self.inner.end = end;
185
186 self
187 }
188
189 pub fn by_aircraft(&mut self, address: String) -> &mut Self {
193 self.inner.request_type = FlightsRequestType::Aircraft(address);
194
195 self
196 }
197
198 pub fn by_arrival(&mut self, airport_icao: String) -> &mut Self {
201 self.inner.request_type = FlightsRequestType::Arrival(airport_icao);
202
203 self
204 }
205
206 pub fn by_departure(&mut self, airport_icao: String) -> &mut Self {
208 self.inner.request_type = FlightsRequestType::Departure(airport_icao);
209
210 self
211 }
212
213 pub fn consume(self) -> FlightsRequest {
218 self.inner
219 }
220
221 pub fn finish(&self) -> FlightsRequest {
226 self.inner.clone()
227 }
228
229 pub async fn send(self) -> Result<Vec<Flight>, Error> {
231 self.inner.send().await
232 }
233}
234
235impl From<FlightsRequestBuilder> for FlightsRequest {
236 fn from(frb: FlightsRequestBuilder) -> Self {
237 frb.consume()
238 }
239}