opensky_network/
tracks.rs1use std::{
3 sync::Arc,
4 time::{SystemTime, UNIX_EPOCH},
5};
6
7use log::{debug, warn};
8use serde::{Deserialize, Deserializer, Serialize};
9use serde_json::{Map, Value};
10
11use crate::errors::Error;
12
13#[derive(Debug, Serialize, Deserialize)]
14pub struct FlightTrack {
16 pub icao24: String,
19 #[serde(alias = "startTime")]
20 pub start_time: f64,
22 #[serde(alias = "endTime")]
23 pub end_time: f64,
25 pub callsign: Option<String>,
27 pub path: Vec<Waypoint>,
29}
30
31#[derive(Debug, Serialize)]
32pub struct Waypoint {
34 pub time: u64,
37 pub latitude: Option<f64>,
39 pub longitude: Option<f64>,
41 pub baro_altitude: Option<f64>,
43 pub true_track: Option<f64>,
46 pub on_ground: bool,
49}
50
51impl<'de> Deserialize<'de> for Waypoint {
52 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
53 where
54 D: Deserializer<'de>,
55 {
56 let values = Deserialize::deserialize(deserializer)?;
57 match values {
58 Value::Array(arr) => Ok(Waypoint::from(arr)),
59 Value::Object(obj) => Ok(Waypoint::from(obj)),
60 _ => Err(serde::de::Error::custom("expected array")),
61 }
62 }
63}
64
65impl From<Vec<Value>> for Waypoint {
66 fn from(value: Vec<Value>) -> Self {
67 Waypoint {
68 time: value[0].as_u64().unwrap(),
69 latitude: value[1].as_f64(),
70 longitude: value[2].as_f64(),
71 baro_altitude: value[3].as_f64(),
72 true_track: value[4].as_f64(),
73 on_ground: value[5].as_bool().unwrap(),
74 }
75 }
76}
77
78impl From<Map<String, Value>> for Waypoint {
79 fn from(value: Map<String, Value>) -> Self {
80 Waypoint {
81 time: value["time"].as_u64().unwrap(),
82 latitude: value["latitude"].as_f64(),
83 longitude: value["longitude"].as_f64(),
84 baro_altitude: value["baro_altitude"].as_f64(),
85 true_track: value["true_track"].as_f64(),
86 on_ground: value["on_ground"].as_bool().unwrap(),
87 }
88 }
89}
90
91#[derive(Debug, Clone)]
92pub struct TrackRequest {
93 login: Option<Arc<(String, String)>>,
94 icao24: String,
95 time: u64,
96}
97
98impl TrackRequest {
99 pub async fn send(&self) -> Result<FlightTrack, Error> {
100 let login_part = if let Some(login) = &self.login {
101 format!("{}:{}@", login.0, login.1)
102 } else {
103 String::new()
104 };
105
106 let now = SystemTime::now()
107 .duration_since(UNIX_EPOCH)
108 .unwrap()
109 .as_secs();
110 if self.time != 0 && now - self.time > 30 * 24 * 60 * 60 {
111 warn!(
112 "Interval ({} secs) is larger than limits ({} secs)",
113 now - self.time,
114 30 * 24 * 60 * 60
115 );
116 }
117
118 let url = format!(
119 "https://{}opensky-network.org/api/tracks/all?icao24={}&time={}",
120 login_part, self.icao24, self.time
121 );
122
123 debug!("url = {}", url);
124
125 let res = reqwest::get(url).await?;
126
127 match res.status() {
128 reqwest::StatusCode::OK => {
129 let bytes = res.bytes().await?.to_vec();
130
131 let result: FlightTrack = match serde_json::from_slice(&bytes) {
132 Ok(result) => result,
133 Err(e) => {
134 return Err(Error::InvalidJson(e));
135 }
136 };
137
138 Ok(result)
139 }
140 status => Err(Error::Http(status)),
141 }
142 }
143}
144
145pub struct TrackRequestBuilder {
146 inner: TrackRequest,
147}
148
149impl TrackRequestBuilder {
150 pub fn new(login: Option<Arc<(String, String)>>, icao24: String) -> Self {
151 Self {
152 inner: TrackRequest {
153 login,
154 icao24,
155 time: 0,
156 },
157 }
158 }
159
160 pub fn at_time(&mut self, time: u64) -> &mut Self {
161 self.inner.time = time;
162
163 self
164 }
165
166 pub async fn send(self) -> Result<FlightTrack, Error> {
167 self.inner.send().await
168 }
169}