gatelogue_types/
lib.rs

1//! # Usage
2//! The data can be imported into your Rust project with `serde`. Add to your `Cargo.toml`:
3//! ```toml
4//! gatelogue-types = { version = "2", features = [...] }
5//!
6//! # To import directly from the repository:
7//! gatelogue-types = { git = "https://github.com/mrt-map/gatelogue", package = "gatelogue-types", features = [...] }
8//! ```
9//! where `features` as denoted by `...` are `reqwest_get`, `surf_get` and `ureq_get`.
10//!
11//! To retrieve the data:
12//! ```rust,ignore
13//! use gatelogue_types::GatelogueData;
14//! GatelogueData::reqwest_get_with_sources().await?; // with sources, requires `reqwest_get` feature
15//! GatelogueData::reqwest_get_no_sources().await?; // no sources, requires `reqwest_get` feature
16//! GatelogueData::surf_get_with_sources().await?; // with sources, requires `surf_get` feature
17//! GatelogueData::surf_get_no_sources().await?; // no sources, requires `surf_get` feature
18//! GatelogueData::ureq_get_with_sources()?; // with sources, requires `ureq_get` feature
19//! GatelogueData::ureq_get_no_sources()?; // no sources, requires `ureq_get` feature
20//! ```
21
22use duplicate::duplicate_item;
23use enum_as_inner::EnumAsInner;
24use serde::{Deserialize, Deserializer, Serialize};
25use std::collections::HashMap;
26use std::ops::{Deref, DerefMut};
27use thiserror::Error;
28
29#[derive(Error, Debug)]
30#[non_exhaustive]
31pub enum Error {
32    #[cfg(feature = "reqwest_get")]
33    #[error("reqwest error: {0:?}")]
34    Reqwest(#[from] reqwest::Error),
35    #[cfg(feature = "surf_get")]
36    #[error("surf error: {0:?}")]
37    Surf(surf::Error),
38    #[cfg(feature = "ureq_get")]
39    #[error("ureq error: {0:?}")]
40    Ureq(#[from] ureq::Error),
41
42    #[error("decoding error: {0:?}")]
43    Decode(#[from] serde_json::error::Error),
44
45    #[error("No node {0}")]
46    NoNode(ID),
47    #[error("{0} not {1}")]
48    IncorrectType(ID, &'static str),
49
50    #[error("unknown error")]
51    Unknown,
52}
53
54#[cfg(feature = "surf_get")]
55impl From<surf::Error> for Error {
56    fn from(err: surf::Error) -> Self {
57        Self::Surf(err)
58    }
59}
60
61pub type Result<T, E = Error> = std::result::Result<T, E>;
62
63pub type ID = u16;
64
65#[derive(Clone, Debug, Serialize, Deserialize)]
66pub struct GatelogueData {
67    pub timestamp: String,
68    pub version: u64,
69    pub nodes: HashMap<ID, Node>,
70}
71impl GatelogueData {
72    #[cfg(feature = "reqwest_get")]
73    pub async fn reqwest_get_with_sources() -> Result<Self> {
74        let bytes = reqwest::get(
75            "https://raw.githubusercontent.com/MRT-Map/gatelogue/refs/heads/dist/data.json",
76        )
77        .await?
78        .bytes()
79        .await?;
80        Ok(serde_json::from_slice(&bytes)?)
81    }
82    #[cfg(feature = "reqwest_get")]
83    pub async fn reqwest_get_no_sources() -> Result<Self> {
84        let bytes = reqwest::get("https://raw.githubusercontent.com/MRT-Map/gatelogue/refs/heads/dist/data_no_sources.json").await?.bytes().await?;
85        Ok(serde_json::from_slice(&bytes)?)
86    }
87    #[cfg(feature = "surf_get")]
88    pub async fn surf_get_with_sources() -> Result<Self> {
89        let bytes = surf::get(
90            "https://raw.githubusercontent.com/MRT-Map/gatelogue/refs/heads/dist/data.json",
91        )
92        .recv_bytes()
93        .await?;
94        Ok(serde_json::from_slice(&bytes)?)
95    }
96    #[cfg(feature = "surf_get")]
97    pub async fn surf_get_no_sources() -> Result<Self> {
98        let bytes = surf::get("https://raw.githubusercontent.com/MRT-Map/gatelogue/refs/heads/dist/data_no_sources.json").recv_bytes().await?;
99        Ok(serde_json::from_slice(&bytes)?)
100    }
101    #[cfg(feature = "ureq_get")]
102    pub fn ureq_get_with_sources() -> Result<Self> {
103        let reader = ureq::get(
104            "https://raw.githubusercontent.com/MRT-Map/gatelogue/refs/heads/dist/data.json",
105        )
106        .call()?
107        .into_body()
108        .into_reader();
109        Ok(serde_json::from_reader(reader)?)
110    }
111    #[cfg(feature = "ureq_get")]
112    pub fn ureq_get_no_sources() -> Result<Self> {
113        let reader = ureq::get("https://raw.githubusercontent.com/MRT-Map/gatelogue/refs/heads/dist/data_no_sources.json").call()?.into_body().into_reader();
114        Ok(serde_json::from_reader(reader)?)
115    }
116
117    #[duplicate_item(
118        nodes_  as_node Ty;
119        [air_airlines] [as_air_airline] [AirAirline];
120        [air_airports] [as_air_airport] [AirAirport];
121        [air_flights] [as_air_flight] [AirFlight];
122        [air_gates] [as_air_gate] [AirGate];
123        [bus_companies] [as_bus_company] [BusCompany];
124        [bus_lines] [as_bus_line] [BusLine];
125        [bus_stops] [as_bus_stop] [BusStop];
126        [rail_companies] [as_rail_company] [RailCompany];
127        [rail_lines] [as_rail_line] [RailLine];
128        [rail_stations] [as_rail_station] [RailStation];
129        [sea_companies] [as_sea_company] [SeaCompany];
130        [sea_lines] [as_sea_line] [SeaLine];
131        [sea_stops] [as_sea_stop] [SeaStop];
132        [spawn_warps] [as_spawn_warp] [SpawnWarp];
133        [towns] [as_town] [Town];
134    )]
135    pub fn nodes_(&self) -> impl Iterator<Item = &Ty> {
136        self.nodes.values().filter_map(|a| a.as_node())
137    }
138
139    #[duplicate_item(
140        nodes_mut  as_node_mut Ty;
141        [air_airlines_mut] [as_air_airline_mut] [AirAirline];
142        [air_airports_mut] [as_air_airport_mut] [AirAirport];
143        [air_flights_mut] [as_air_flight_mut] [AirFlight];
144        [air_gates_mut] [as_air_gate_mut] [AirGate];
145        [bus_companies_mut] [as_bus_company_mut] [BusCompany];
146        [bus_lines_mut] [as_bus_line_mut] [BusLine];
147        [bus_stops_mut] [as_bus_stop_mut] [BusStop];
148        [rail_companies_mut] [as_rail_company_mut] [RailCompany];
149        [rail_lines_mut] [as_rail_line_mut] [RailLine];
150        [rail_stations_mut] [as_rail_station_mut] [RailStation];
151        [sea_companies_mut] [as_sea_company_mut] [SeaCompany];
152        [sea_lines_mut] [as_sea_line_mut] [SeaLine];
153        [sea_stops_mut] [as_sea_stop_mut] [SeaStop];
154        [spawn_warps_mut] [as_spawn_warp_mut] [SpawnWarp];
155        [towns_mut] [as_town_mut] [Town];
156    )]
157    pub fn nodes_mut(&mut self) -> impl Iterator<Item = &mut Ty> {
158        self.nodes.values_mut().filter_map(|a| a.as_node_mut())
159    }
160
161    #[duplicate_item(
162        get_node  as_node Ty;
163        [get_air_airline] [as_air_airline] [AirAirline];
164        [get_air_airport] [as_air_airport] [AirAirport];
165        [get_air_flight] [as_air_flight] [AirFlight];
166        [get_air_gate] [as_air_gate] [AirGate];
167        [get_bus_company] [as_bus_company] [BusCompany];
168        [get_bus_line] [as_bus_line] [BusLine];
169        [get_bus_stop] [as_bus_stop] [BusStop];
170        [get_rail_company] [as_rail_company] [RailCompany];
171        [get_rail_line] [as_rail_line] [RailLine];
172        [get_rail_station] [as_rail_station] [RailStation];
173        [get_sea_company] [as_sea_company] [SeaCompany];
174        [get_sea_line] [as_sea_line] [SeaLine];
175        [get_sea_stop] [as_sea_stop] [SeaStop];
176        [get_spawn_warp] [as_spawn_warp] [SpawnWarp];
177        [get_town] [as_town] [Town];
178    )]
179    pub fn get_node(&self, id: ID) -> Result<&Ty> {
180        self.nodes
181            .get(&id)
182            .ok_or(Error::NoNode(id))?
183            .as_node()
184            .ok_or(Error::IncorrectType(id, stringify!(Ty)))
185    }
186
187    #[duplicate_item(
188        get_node_mut  as_node_mut Ty;
189        [get_air_airline_mut] [as_air_airline_mut] [AirAirline];
190        [get_air_airport_mut] [as_air_airport_mut] [AirAirport];
191        [get_air_flight_mut] [as_air_flight_mut] [AirFlight];
192        [get_air_gate_mut] [as_air_gate_mut] [AirGate];
193        [get_bus_company_mut] [as_bus_company_mut] [BusCompany];
194        [get_bus_line_mut] [as_bus_line_mut] [BusLine];
195        [get_bus_stop_mut] [as_bus_stop_mut] [BusStop];
196        [get_rail_company_mut] [as_rail_company_mut] [RailCompany];
197        [get_rail_line_mut] [as_rail_line_mut] [RailLine];
198        [get_rail_station_mut] [as_rail_station_mut] [RailStation];
199        [get_sea_company_mut] [as_sea_company_mut] [SeaCompany];
200        [get_sea_line_mut] [as_sea_line_mut] [SeaLine];
201        [get_sea_stop_mut] [as_sea_stop_mut] [SeaStop];
202        [get_spawn_warp_mut] [as_spawn_warp_mut] [SpawnWarp];
203        [get_town_mut] [as_town_mut] [Town];
204    )]
205    pub fn get_node_mut(&mut self, id: ID) -> Result<&mut Ty> {
206        self.nodes
207            .get_mut(&id)
208            .ok_or(Error::NoNode(id))?
209            .as_node_mut()
210            .ok_or(Error::IncorrectType(id, stringify!(Ty)))
211    }
212}
213
214#[derive(Clone, Debug, Serialize, Deserialize)]
215#[serde(untagged)]
216pub enum Sourced<T> {
217    Unsourced(T),
218    Sourced { v: T, s: Vec<String> },
219}
220
221impl<T> Deref for Sourced<T> {
222    type Target = T;
223
224    fn deref(&self) -> &Self::Target {
225        match self {
226            Self::Unsourced(v) | Self::Sourced { v, .. } => v,
227        }
228    }
229}
230impl<T> DerefMut for Sourced<T> {
231    fn deref_mut(&mut self) -> &mut Self::Target {
232        match self {
233            Self::Unsourced(v) | Self::Sourced { v, .. } => v,
234        }
235    }
236}
237
238#[derive(Clone, Debug, Serialize, Deserialize)]
239pub struct NodeCommon {
240    pub i: ID,
241    pub source: Vec<String>,
242}
243
244#[derive(Clone, Copy, PartialEq, Eq, Debug, Serialize, Deserialize)]
245pub enum World {
246    Old,
247    New,
248    Space,
249}
250
251#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
252pub struct Proximity {
253    pub distance: Option<f64>,
254    pub explicit: bool,
255}
256
257#[derive(Clone, Debug, Serialize, Deserialize)]
258pub struct LocatedNodeCommon {
259    pub i: ID,
260    pub source: Vec<String>,
261    pub world: Option<Sourced<World>>,
262    pub coordinates: Option<Sourced<(f64, f64)>>,
263    #[serde(deserialize_with = "deserialise_proximity")]
264    pub proximity: HashMap<ID, Sourced<Proximity>>,
265    pub shared_facility: Vec<Sourced<ID>>,
266}
267
268#[derive(Clone, Debug, Serialize, Deserialize, EnumAsInner)]
269#[serde(tag = "type")]
270pub enum Node {
271    AirAirline(AirAirline),
272    AirAirport(AirAirport),
273    AirFlight(AirFlight),
274    AirGate(AirGate),
275    BusCompany(BusCompany),
276    BusLine(BusLine),
277    BusStop(BusStop),
278    SeaCompany(SeaCompany),
279    SeaLine(SeaLine),
280    SeaStop(SeaStop),
281    RailCompany(RailCompany),
282    RailLine(RailLine),
283    RailStation(RailStation),
284    SpawnWarp(SpawnWarp),
285    Town(Town),
286}
287
288#[derive(Clone, Copy, PartialEq, Eq, Debug, Serialize, Deserialize)]
289pub enum AirMode {
290    #[serde(rename = "helicopter")]
291    Helicopter,
292    #[serde(rename = "seaplane")]
293    Seaplane,
294    #[serde(rename = "warp plane")]
295    WarpPlane,
296    #[serde(rename = "traincarts plane")]
297    TrainCartsPlane,
298}
299
300#[derive(Clone, Debug, Serialize, Deserialize)]
301pub struct AirAirline {
302    pub name: String,
303    pub link: Option<Sourced<String>>,
304    pub flights: Vec<Sourced<ID>>,
305    pub gates: Vec<Sourced<ID>>,
306    #[serde(flatten)]
307    pub common: NodeCommon,
308}
309
310#[derive(Clone, Debug, Serialize, Deserialize)]
311pub struct AirAirport {
312    pub code: String,
313    pub name: Option<Sourced<String>>,
314    pub link: Option<Sourced<String>>,
315    pub modes: Option<Sourced<Vec<AirMode>>>,
316    pub gates: Vec<Sourced<ID>>,
317    #[serde(flatten)]
318    pub common: LocatedNodeCommon,
319}
320
321#[derive(Clone, Debug, Serialize, Deserialize)]
322pub struct AirFlight {
323    pub codes: Vec<String>,
324    pub mode: Option<Sourced<AirMode>>,
325    pub airline: Sourced<ID>,
326    pub gates: Vec<Sourced<ID>>,
327    #[serde(flatten)]
328    pub common: NodeCommon,
329}
330
331#[derive(Clone, Debug, Serialize, Deserialize)]
332pub struct AirGate {
333    pub codes: Option<String>,
334    pub size: Option<Sourced<String>>,
335    pub airline: Option<Sourced<ID>>,
336    pub airport: Sourced<ID>,
337    pub flights: Vec<Sourced<ID>>,
338    #[serde(flatten)]
339    pub common: NodeCommon,
340}
341
342#[derive(Clone, Debug, Serialize, Deserialize)]
343pub struct BusCompany {
344    pub name: String,
345    pub lines: Vec<Sourced<ID>>,
346    pub stops: Vec<Sourced<ID>>,
347    pub local: bool,
348    #[serde(flatten)]
349    pub common: NodeCommon,
350}
351
352#[derive(Clone, Debug, Serialize, Deserialize)]
353pub struct BusLine {
354    pub code: String,
355    pub name: Option<Sourced<String>>,
356    pub colour: Option<Sourced<String>>,
357    pub company: Sourced<ID>,
358    pub ref_stop: Option<Sourced<ID>>,
359    #[serde(flatten)]
360    pub common: NodeCommon,
361}
362
363#[derive(Clone, Debug, Serialize, Deserialize)]
364pub struct BusStop {
365    pub codes: Vec<String>,
366    pub name: Option<Sourced<String>>,
367    pub company: Sourced<ID>,
368    #[serde(deserialize_with = "deserialise_connections")]
369    pub connections: HashMap<ID, Vec<Sourced<Connection>>>,
370    #[serde(flatten)]
371    pub common: LocatedNodeCommon,
372}
373
374#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
375pub enum RailMode {
376    #[serde(rename = "warp")]
377    Warp,
378    #[serde(rename = "cart")]
379    Cart,
380    #[serde(rename = "traincarts")]
381    TrainCart,
382    #[serde(rename = "vehicles")]
383    Vehicles,
384}
385
386#[derive(Clone, Debug, Serialize, Deserialize)]
387pub struct RailCompany {
388    pub name: String,
389    pub lines: Vec<Sourced<ID>>,
390    pub stations: Vec<Sourced<ID>>,
391    pub local: bool,
392    #[serde(flatten)]
393    pub common: NodeCommon,
394}
395
396#[derive(Clone, Debug, Serialize, Deserialize)]
397pub struct RailLine {
398    pub code: String,
399    pub name: Option<Sourced<String>>,
400    pub colour: Option<Sourced<String>>,
401    pub company: Sourced<ID>,
402    pub ref_stop: Option<Sourced<ID>>,
403    #[serde(flatten)]
404    pub common: NodeCommon,
405}
406
407#[derive(Clone, Debug, Serialize, Deserialize)]
408pub struct RailStation {
409    pub codes: Vec<String>,
410    pub name: Option<Sourced<String>>,
411    pub company: Sourced<ID>,
412    #[serde(deserialize_with = "deserialise_connections")]
413    pub connections: HashMap<ID, Vec<Sourced<Connection>>>,
414    #[serde(flatten)]
415    pub common: LocatedNodeCommon,
416}
417
418#[derive(Clone, Debug, Serialize, Deserialize)]
419pub struct SeaCompany {
420    pub name: String,
421    pub lines: Vec<Sourced<ID>>,
422    pub stops: Vec<Sourced<ID>>,
423    pub local: bool,
424    #[serde(flatten)]
425    pub common: NodeCommon,
426}
427
428#[derive(Clone, Debug, Serialize, Deserialize)]
429pub struct SeaLine {
430    pub code: String,
431    pub name: Option<Sourced<String>>,
432    pub colour: Option<Sourced<String>>,
433    pub company: Sourced<ID>,
434    pub ref_stop: Option<Sourced<ID>>,
435    #[serde(flatten)]
436    pub common: NodeCommon,
437}
438
439#[derive(Clone, Debug, Serialize, Deserialize)]
440pub struct SeaStop {
441    pub codes: Vec<String>,
442    pub name: Option<Sourced<String>>,
443    pub company: Sourced<ID>,
444    #[serde(deserialize_with = "deserialise_connections")]
445    pub connections: HashMap<ID, Vec<Sourced<Connection>>>,
446    #[serde(flatten)]
447    pub common: LocatedNodeCommon,
448}
449
450#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
451#[serde(rename_all = "lowercase")]
452pub enum SpawnWarpType {
453    Premier,
454    Terminus,
455    Portal,
456    Misc,
457}
458
459#[derive(Clone, Debug, Serialize, Deserialize)]
460pub struct SpawnWarp {
461    pub name: String,
462    pub warp_type: SpawnWarpType,
463    #[serde(flatten)]
464    pub common: LocatedNodeCommon,
465}
466
467#[derive(Clone, Copy, PartialEq, Eq, Debug, Serialize, Deserialize)]
468pub enum Rank {
469    Unranked,
470    Councillor,
471    Mayor,
472    Senator,
473    Governor,
474    Premier,
475    Community,
476}
477
478#[derive(Clone, Debug, Serialize, Deserialize)]
479pub struct Town {
480    pub name: String,
481    pub rank: Sourced<Rank>,
482    pub mayor: Sourced<String>,
483    pub deputy_mayor: Sourced<Option<String>>,
484    #[serde(flatten)]
485    pub common: LocatedNodeCommon,
486}
487
488#[derive(Clone, Debug, Serialize, Deserialize)]
489pub struct Connection {
490    pub line: ID,
491    pub direction: Option<Direction>,
492}
493
494#[derive(Clone, Debug, Serialize, Deserialize)]
495pub struct Direction {
496    pub direction: ID,
497    pub forward_label: Option<String>,
498    pub backward_label: Option<String>,
499    pub one_way: Sourced<bool>,
500}
501
502fn deserialise_connections<'de, D: Deserializer<'de>>(
503    de: D,
504) -> Result<HashMap<ID, Vec<Sourced<Connection>>>, D::Error> {
505    HashMap::<String, Vec<Sourced<Connection>>>::deserialize(de)?
506        .into_iter()
507        .map(|(k, v)| Ok((k.parse::<ID>().map_err(serde::de::Error::custom)?, v)))
508        .collect()
509}
510fn deserialise_proximity<'de, D: Deserializer<'de>>(
511    de: D,
512) -> Result<HashMap<ID, Sourced<Proximity>>, D::Error> {
513    HashMap::<String, Sourced<Proximity>>::deserialize(de)?
514        .into_iter()
515        .map(|(k, v)| Ok((k.parse::<ID>().map_err(serde::de::Error::custom)?, v)))
516        .collect()
517}
518
519#[cfg(test)]
520#[cfg(feature = "ureq_get")]
521mod test {
522    use super::*;
523
524    #[test]
525    fn ureq1() {
526        let _ = GatelogueData::ureq_get_with_sources().unwrap();
527    }
528
529    #[test]
530    fn ureq2() {
531        let _ = GatelogueData::ureq_get_no_sources().unwrap();
532    }
533}