1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
//! MetroRail related responses from the WMATA API.
use crate::{Line, Station};
use chrono::{DateTime, FixedOffset};
use serde::Deserialize;
#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
pub struct Lines {
/// See [`Line`].
pub lines: Box<[LineResponse]>,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
pub struct LineResponse {
/// Two letter abbreviation of the line. See [`Line`].
#[serde(rename = "LineCode")]
pub line: Line,
/// Full name of the Line.
pub display_name: String,
/// [`Station`] for start of the Line.
#[serde(rename = "StartStationCode")]
pub start_station: Station,
/// [`Station`] for end of the Line.
#[serde(rename = "EndStationCode")]
pub end_station: Station,
/// Intermediate terminal [`Station`]. Ex: Mt. Vernon for Yellow, Silver Spring for Red.
#[serde(rename = "InternalDestination1")]
#[serde(deserialize_with = "crate::rail::station::empty_or_station")]
pub first_internal_destination: Option<Station>,
/// Intermediate terminal [`Station`]. Ex: Mt. Vernon for Yellow, Silver Spring for Red.
#[serde(deserialize_with = "crate::rail::station::empty_or_station")]
#[serde(rename = "InternalDestination2")]
pub second_internal_destination: Option<Station>,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
pub struct StationEntrances {
/// See [`StationEntrance`].
pub entrances: Box<[StationEntrance]>,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
pub struct StationEntrance {
/// Additional information for the entrance.
pub description: String,
/// Warning: Deprecated.
#[serde(rename = "ID")]
pub id: String,
/// Latitude of entrance.
#[serde(rename = "Lat")]
pub latitude: f64,
/// Longitude of entrance.
#[serde(rename = "Lon")]
pub longitude: f64,
/// Name of entrance.
pub name: String,
/// [`Station`] of this entrance.
#[serde(rename = "StationCode1")]
pub first_station: Station,
/// Second [`Station`] of this entrance.
#[serde(rename = "StationCode2")]
#[serde(deserialize_with = "crate::rail::station::empty_or_station")]
pub second_station: Option<Station>,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
pub struct TrainPositions {
/// See [`TrainPosition`].
pub train_positions: Box<[TrainPosition]>,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
pub struct TrainPosition {
/// Uniquely identifiable internal train identifier
pub train_id: String,
/// Non-unique train identifier, often used by WMATA's Rail Scheduling and Operations Teams, as well as over open radio communication.
pub train_number: String,
/// Number of cars. Can be 0.
pub car_count: i32,
/// The direction of movement regardless of which track the train is on.
#[serde(rename = "DirectionNum")]
pub direction_number: i32,
/// The circuit identifier the train is currently on.
pub circuit_id: i32,
/// Destination [`Station`].
#[serde(rename = "DestinationStationCode")]
#[serde(deserialize_with = "crate::rail::station::empty_or_station")]
pub destination_station: Option<Station>,
/// [`Line`] for this train.
#[serde(rename = "LineCode")]
pub line: Option<Line>,
/// Approximate "dwell time". This is not an exact value, but can be used to determine how long a train has been reported at the same track circuit.
pub seconds_at_location: i32,
/// Service Type of a train.
pub service_type: String,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
pub struct StandardRoutes {
/// See [`StandardRoute`].
pub standard_routes: Box<[StandardRoute]>,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
pub struct StandardRoute {
/// [`Line`] for this route.
#[serde(rename = "LineCode")]
pub line: Line,
#[serde(rename = "TrackNum")]
/// Track number. 1 or 2.
pub track_number: i32,
/// See [`TrackCircuitWithStation`].
pub track_circuits: Box<[TrackCircuitWithStation]>,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
pub struct TrackCircuitWithStation {
/// Order in which the circuit appears for the given line and track.
#[serde(rename = "SeqNum")]
pub sequence_number: i32,
/// An internal system-wide uniquely identifiable circuit number.
pub circuit_id: i32,
/// [`Station`] if this circuit is at a station.
#[serde(rename = "StationCode")]
#[serde(deserialize_with = "crate::rail::station::empty_or_station")]
pub station: Option<Station>,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
pub struct TrackCircuits {
/// See [`TrackCircuit`].
pub track_circuits: Box<[TrackCircuit]>,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
pub struct TrackCircuit {
/// Track number. 1 and 2 denote "main" lines, while 0 and 3 are connectors (between different types of tracks) and pocket tracks, respectively.
pub track: i32,
/// An internal system-wide uniquely identifiable circuit number.
pub circuit_id: i32,
/// See [`TrackNeighbor`].
pub neighbors: Box<[TrackNeighbor]>,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
pub struct TrackNeighbor {
/// Left or Right neighbor group. Generally speaking, left neighbors are to the west and south, while right neighbors are to the east/north.
pub neighbor_type: String,
/// Neighboring circuit ids.
pub circuit_ids: Box<[i32]>,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
pub struct ElevatorAndEscalatorIncidents {
/// See [`ElevatorAndEscalatorIncident`].
#[serde(rename = "ElevatorIncidents")]
pub incidents: Box<[ElevatorAndEscalatorIncident]>,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
pub struct ElevatorAndEscalatorIncident {
/// Unique identifier for unit, by type (a single elevator and escalator may have the same UnitName, but no two elevators or two escalators will have the same UnitName).
pub unit_name: String,
/// Type of unit. Will be ELEVATOR or ESCALATOR.
pub unit_type: String,
/// Warning: Deprecated. If listed here, the unit is inoperational or otherwise impaired.
pub unit_status: Option<String>,
/// [`Station`] of the incident.
#[serde(rename = "StationCode")]
pub station: Station,
/// Full station name, may include entrance information (e.g.: Metro Center, G and 11th St Entrance).
pub station_name: String,
/// Free-text description of the unit location within a station (e.g.: Escalator between mezzanine and platform).
pub location_description: String,
/// Warning: Deprecated.
pub symptom_code: Option<String>,
/// Warning: Deprecated. Use the time portion of the DateOutOfServ element.
pub time_out_of_service: String,
/// Description for why the unit is out of service or otherwise in reduced operation.
pub symptom_description: String,
/// Warning: Deprecated.
pub display_order: f64,
/// Date and time (Eastern Standard Time) unit was reported out of service.
#[serde(rename = "DateOutOfServ")]
#[serde(deserialize_with = "crate::date::deserialize")]
pub date_out_of_service: DateTime<FixedOffset>,
/// Date and time (Eastern Standard Time) outage details was last updated.
#[serde(deserialize_with = "crate::date::deserialize")]
pub date_updated: DateTime<FixedOffset>,
/// Estimated date and time (Eastern Standard Time) by when unit is expected to return to normal service.
#[serde(deserialize_with = "crate::date::deserialize_option")]
pub estimated_return_to_service: Option<DateTime<FixedOffset>>,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
pub struct RailIncidents {
/// See [`RailIncident`]
pub incidents: Box<[RailIncident]>,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
pub struct RailIncident {
/// Unique identifier for an incident.
#[serde(rename = "IncidentID")]
pub incident_id: String,
/// Free-text description of the incident.
pub description: String,
/// Warning: Deprecated.
pub start_location_full_name: Option<String>,
/// Warning: Deprecated.
pub end_location_full_name: Option<String>,
/// Warning: Deprecated.
pub passenger_delay: f64,
/// Warning: Deprecated.
pub delay_severity: Option<String>,
/// Free-text description of the incident type. Usually Delay or Alert but is subject to change at any time.
pub incident_type: String,
/// Warning: Deprecated.
pub emergency_text: Option<String>,
/// Semi-colon and space separated list of line codes (e.g.: RD; or BL; OR; or BL; OR; RD;). =(
pub lines_affected: String,
/// Date and time (Eastern Standard Time) of last update.
#[serde(deserialize_with = "crate::date::deserialize")]
pub date_updated: DateTime<FixedOffset>,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
pub struct StationToStationInfos {
/// See [`StationToStationInfo`]
pub station_to_station_infos: Box<[StationToStationInfo]>,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
pub struct StationToStationInfo {
/// Average of distance traveled between two stations and straight-line distance (as used for WMATA fare calculations).
pub composite_miles: f64,
/// Destination [`Station`].
#[serde(rename = "DestinationStation")]
pub destination_station: Station,
/// Structure containing fare information.
pub rail_fare: RailFare,
/// Estimated travel time (schedule time) in minutes between the source and destination station. This is not correlated to minutes (Min) in Real-Time Rail Predictions.
pub rail_time: i32,
/// Origin [`Station`].
#[serde(rename = "SourceStation")]
pub source_station: Station,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
pub struct RailFare {
/// Fare during off-peak times.
pub off_peak_time: f64,
/// Fare during peak times (weekdays from opening to 9:30 AM and 3-7 PM, and weekends from midnight to closing).
pub peak_time: f64,
/// Reduced fare for senior citizens or people with disabilities.
pub senior_disabled: f64,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
pub struct RailPredictions {
/// See [`RailPrediction`].
pub trains: Box<[RailPrediction]>,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
pub struct RailPrediction {
/// Number of cars on a train, usually 6 or 8, but might also return -.
pub car: Option<String>,
/// Abbreviated version of the final destination for a train. This is similar to what is displayed on the signs at stations.
pub destination: String,
/// [`Station`] of destination station.
#[serde(rename = "DestinationCode")]
#[serde(deserialize_with = "crate::rail::station::empty_or_station")]
pub destination_station: Option<Station>,
/// When DestinationCode is populated, this is the full name of the destination station, as shown on the WMATA website.
pub destination_name: String,
/// Denotes the track this train is on, but does not necessarily equate to Track 1 or Track 2. With the exception of terminal stations, predictions at the same station with different Group values refer to trains on different tracks.
pub group: String,
/// Two-letter abbreviation for the line (e.g.: RD, BL, YL, OR, GR, or SV). May also be blank or No for trains with no passengers.
pub line: String,
/// [`Station`] for where the train is arriving.
#[serde(rename = "LocationCode")]
pub location: Station,
/// Full name of the station where the train is arriving.
pub location_name: String,
/// Minutes until arrival. Can be a numeric value, ARR (arriving), BRD (boarding), ---, or empty.
#[serde(rename = "Min")]
pub minutes: String,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
pub struct StationInformation {
/// Structure describing address information.
pub address: StationAddress,
/// [`Station`] for this station.
#[serde(rename = "Code")]
pub station: Station,
/// Latitude of this station.
#[serde(rename = "Lat")]
pub latitude: f64,
/// Longitude of this station.
#[serde(rename = "Lon")]
pub longitude: f64,
/// First [`Line`] for this station.
#[serde(rename = "LineCode1")]
pub first_line: Line,
/// Second [`Line`] for this station.
#[serde(rename = "LineCode2")]
pub second_line: Option<Line>,
/// Third [`Line`] for this station.
#[serde(rename = "LineCode3")]
pub third_line: Option<Line>,
/// Fourth [`Line`] for this station.
#[serde(rename = "LineCode4")]
pub fourth_line: Option<Line>,
/// Station name.
pub name: String,
/// For stations with multiple platforms (e.g.: Gallery Place, Fort Totten, L'Enfant Plaza, and Metro Center), the additional [`Station`] will be listed here.
#[serde(rename = "StationTogether1")]
#[serde(deserialize_with = "crate::rail::station::empty_or_station")]
pub first_station_together: Option<Station>,
/// Similar in function to first_station_together. Currently not in use.
#[serde(rename = "StationTogether2")]
#[serde(deserialize_with = "crate::rail::station::empty_or_station")]
pub second_station_together: Option<Station>,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
pub struct StationAddress {
/// City of this station.
pub city: String,
/// State of this station.
pub state: String,
/// Street address (for GPS use) of this station.
pub street: String,
/// Zip code of this station.
pub zip: String,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
pub struct StationsParking {
/// See [`StationParking`].
pub stations_parking: Box<[StationParking]>,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
pub struct StationParking {
/// [`Station`] of this station.
#[serde(rename = "Code")]
pub station: Station,
/// When not None, provides additional parking resources such as nearby lots.
pub notes: Option<String>,
/// See [`AllDayParking`].
pub all_day_parking: AllDayParking,
/// See [`ShortTermParking`].
pub short_term_parking: ShortTermParking,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
pub struct AllDayParking {
/// Number of all-day parking spots available at a station.
pub total_count: i32,
/// All-day cost per day (weekday) for Metro riders.
pub rider_cost: Option<f64>,
/// All-day cost per day (weekday) for non-Metro riders.
pub non_rider_cost: Option<f64>,
/// Similar to RiderCost, except denoting Saturday prices.
pub saturday_rider_cost: Option<f64>,
/// Similar to NonRiderCost, except denoting Saturday prices.
pub saturday_non_rider_cost: Option<f64>,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
pub struct ShortTermParking {
/// Number of short-term parking spots available at a station (parking meters).
pub total_count: i32,
/// Misc. information relating to short-term parking.
pub notes: String,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
pub struct PathBetweenStations {
/// See [`Path`].
pub path: Box<[Path]>,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
pub struct Path {
/// Distance in feet to the previous station in the list.
#[serde(rename = "DistanceToPrev")]
pub distance_to_previous_station: i32,
/// [`Line`] of this station.
#[serde(rename = "LineCode")]
pub line: Line,
/// Ordered sequence number.
#[serde(rename = "SeqNum")]
pub sequence_number: i32,
/// [`Station`] of this station.
#[serde(rename = "StationCode")]
pub station: Station,
/// Full name for this station, as shown on the WMATA website.
pub station_name: String,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
pub struct StationTimings {
/// See [`StationTime`].
pub station_times: Box<[StationTime]>,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
pub struct StationTime {
/// [`Station`] of this station.
#[serde(rename = "Code")]
pub station: Station,
/// Full name of the station.
pub station_name: String,
// You're gonna love this
/// Container elements containing timing information based on day of the week. See [`StationFirstLastTrains`].
pub monday: StationFirstLastTrains,
/// Container elements containing timing information based on day of the week. See [`StationFirstLastTrains`].
pub tuesday: StationFirstLastTrains,
/// Container elements containing timing information based on day of the week. See [`StationFirstLastTrains`].
pub wednesday: StationFirstLastTrains,
/// Container elements containing timing information based on day of the week. See [`StationFirstLastTrains`].
pub thursday: StationFirstLastTrains,
/// Container elements containing timing information based on day of the week. See [`StationFirstLastTrains`].
pub friday: StationFirstLastTrains,
/// Container elements containing timing information based on day of the week. See [`StationFirstLastTrains`].
pub saturday: StationFirstLastTrains,
/// Container elements containing timing information based on day of the week. See [`StationFirstLastTrains`].
pub sunday: StationFirstLastTrains,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
pub struct StationFirstLastTrains {
/// Station opening time. Format is HH:mm.
pub opening_time: String,
/// See [`TrainTime`].
pub first_trains: Box<[TrainTime]>,
/// See [`TrainTime`].
pub last_trains: Box<[TrainTime]>,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
pub struct TrainTime {
/// Time the train leaves the station.
pub time: String,
/// [`Station`] for the destination station.
#[serde(rename = "DestinationStation")]
pub destination: Station,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
pub struct Stations {
/// See [`Station`].
pub stations: Box<[StationResponse]>,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
pub struct StationResponse {
/// See [`Address`].
pub address: Address,
/// [`Station`] of this station.
#[serde(rename = "Code")]
pub station: Station,
/// Latitude of this station.
#[serde(rename = "Lat")]
pub latitude: f64,
/// Longitude of this station.
#[serde(rename = "Lon")]
pub longitude: f64,
/// First [`Line`] of this station.
#[serde(rename = "LineCode1")]
pub first_line: Line,
/// Second [`Line`] of this station.
#[serde(rename = "LineCode2")]
pub second_line: Option<Line>,
/// Third [`Line`] of this station.
#[serde(rename = "LineCode3")]
pub third_line: Option<Line>,
/// Fourth [`Line`] of this station.
#[serde(rename = "LineCode4")]
pub fourth_line: Option<Line>,
/// Station name.
pub name: String,
/// For stations with multiple platforms (e.g.: Gallery Place, Fort Totten, L'Enfant Plaza, and Metro Center), the additional [`Station`] will be listed here.
#[serde(rename = "StationTogether1")]
#[serde(deserialize_with = "crate::rail::station::empty_or_station")]
pub first_station_together: Option<Station>,
/// imilar in function to first_station_together. Currently not in use.
#[serde(rename = "StationTogether2")]
#[serde(deserialize_with = "crate::rail::station::empty_or_station")]
pub second_station_together: Option<Station>,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
pub struct Address {
/// City of this station.
pub city: String,
/// State of this station.
pub state: String,
/// Street address (for GPS use) of this station.
pub street: String,
/// Zip code of this station.
pub zip: String,
}