1use yaserde::*;
5
6pub const SOAP_ENDPOINT: &str = "https://lite.realtime.nationalrail.co.uk/OpenLDBWS/ldb12.asmx";
8
9#[derive(Debug, Default, YaDeserialize, YaSerialize)]
10#[yaserde(
11 rename = "trainServices",
12 namespaces = {
13 "lt8" = "http://thalesgroup.com/RTTI/2021-11-01/ldb/types"
14 },
15 prefix = "lt8"
16)]
17pub struct ServiceItemWithCallingPointsListWrapper {
18 #[yaserde(rename = "service", prefix = "lt8")]
19 pub services: Vec<ServiceItemWithCallingPoints>,
20}
21
22#[derive(Debug, Default, YaDeserialize, YaSerialize)]
23#[yaserde(
24 rename = "GetStationBoardResult",
25 namespaces = {
26 "r" = "http://thalesgroup.com/RTTI/2021-11-01/ldb/",
27 "lt4" = "http://thalesgroup.com/RTTI/2015-11-27/ldb/types",
28 "lt8" = "http://thalesgroup.com/RTTI/2021-11-01/ldb/types",
29 },
30 prefix = "r"
31)]
32pub struct StationBoardWithDetails {
33 #[yaserde(rename = "generatedAt", prefix = "lt4")]
34 pub generated_at: String,
35 #[yaserde(rename = "locationName", prefix = "lt4")]
36 pub location_name: String,
37 #[yaserde(rename = "trainServices", prefix = "lt8")]
38 pub train_services: ServiceItemWithCallingPointsListWrapper,
39}
40
41#[derive(Debug, Default, YaDeserialize, YaSerialize)]
42#[yaserde(
43 rename = "GetDepartureBoardResponse",
44 namespaces = {
45 "r" = "http://thalesgroup.com/RTTI/2021-11-01/ldb/",
46 },
47 prefix = "r"
48)]
49pub struct GetDepBoardWithDetailsResponse {
50 #[yaserde(rename = "GetStationBoardResult", prefix = "r")]
51 pub station_board_result: StationBoardWithDetails,
52}
53
54#[derive(Debug, Default, YaDeserialize, YaSerialize)]
55#[yaserde(rename = "Body", namespaces = {"r" = "http://thalesgroup.com/RTTI/2021-11-01/ldb/"})]
56pub struct SoapBodyGetDepBoardWithDetailsResponse {
57 #[yaserde(rename = "GetDepBoardWithDetailsResponse", prefix = "r")]
58 pub dep_board_with_details_response: GetDepBoardWithDetailsResponse,
59}
60
61#[derive(Debug, Default, YaDeserialize, YaSerialize)]
62#[yaserde(
63 rename = "Envelope",
64 namespaces = {
65 "s" = "http://schemas.xmlsoap.org/soap/envelope/",
66 },
67 prefix = "s"
68)]
69pub struct SoapEnvelopeGetDepBoardWithDetailsResponse {
70 #[yaserde(rename = "Body", prefix = "s")]
71 pub body: SoapBodyGetDepBoardWithDetailsResponse,
72}
73
74#[derive(Debug, Default, YaDeserialize, YaSerialize)]
75#[yaserde(
76 rename = "callingPoint",
77 namespaces = {
78 "lt8" = "http://thalesgroup.com/RTTI/2021-11-01/ldb/types",
79 },
80 prefix = "lt8"
81)]
82pub struct CallingPoint {
83 #[yaserde(rename = "crs", prefix = "lt8")]
84 pub crs: String, #[yaserde(rename = "st", prefix = "lt8")]
86 pub st: String,
87 #[yaserde(rename = "et", prefix = "lt8")]
88 pub et: Option<String>,
89 #[yaserde(rename = "at", prefix = "lt8")]
90 pub at: Option<String>,
91}
92
93#[derive(Debug, Default, YaDeserialize, YaSerialize)]
94#[yaserde(
95 rename = "callingPointList",
96 namespaces = {
97 "lt8" = "http://thalesgroup.com/RTTI/2021-11-01/ldb/types",
98 },
99 prefix = "lt8"
100)]
101pub struct CallingPointGroup {
102 #[yaserde(rename = "callingPoint", prefix = "lt8")]
103 pub calling_point: Vec<CallingPoint>,
104}
105
106#[derive(Debug, Default, YaDeserialize, YaSerialize)]
107#[yaserde(
108 rename = "subsequentCallingPoints",
109 namespaces = {
110 "lt8" = "http://thalesgroup.com/RTTI/2021-11-01/ldb/types",
111 },
112 prefix = "lt8",
113)]
114pub struct SubsequentCallingPointsListWrapper {
115 #[yaserde(rename = "callingPointList", prefix = "lt8")]
116 pub calling_points_list: Vec<CallingPointGroup>, }
118
119#[derive(Debug, Default, YaDeserialize, YaSerialize)]
120#[yaserde(
121 rename = "service",
122 namespaces = {
123 "lt4" = "http://thalesgroup.com/RTTI/2015-11-27/ldb/types",
124 "lt5" = "http://thalesgroup.com/RTTI/2016-02-16/ldb/types",
125 "lt8" = "http://thalesgroup.com/RTTI/2021-11-01/ldb/types",
126 },
127 prefix = "lt8"
128)]
129pub struct ServiceItemWithCallingPoints {
130 #[yaserde(rename = "sta", prefix = "lt4")]
131 pub sta: Option<String>,
132 #[yaserde(rename = "eta", prefix = "lt4")]
133 pub eta: Option<String>,
134 #[yaserde(rename = "std", prefix = "lt4")]
135 pub std: Option<String>,
136 #[yaserde(rename = "etd", prefix = "lt4")]
137 pub etd: Option<String>,
138 #[yaserde(rename = "platform", prefix = "lt4")]
139 pub platform: Option<String>,
140 #[yaserde(rename = "operator", prefix = "lt4")]
141 pub operator: String,
142 #[yaserde(rename = "operatorCode", prefix = "lt4")]
143 pub operator_code: String,
144 #[yaserde(rename = "serviceType", prefix = "lt4")]
145 pub service_type: String,
146 #[yaserde(rename = "length", prefix = "lt4")]
147 pub length: Option<u32>,
148 #[yaserde(rename = "isCancelled", prefix = "lt4")]
149 pub is_cancelled: Option<bool>,
150 #[yaserde(rename = "cancelReason", prefix = "lt4")]
151 pub cancel_reason: Option<String>,
152 #[yaserde(rename = "delayReason", prefix = "lt4")]
153 pub delay_reason: Option<String>,
154 #[yaserde(rename = "serviceID", prefix = "lt4")]
155 pub service_id: String,
156 #[yaserde(rename = "destination", prefix = "lt5")]
159 pub destination: Destination,
160 #[yaserde(rename = "subsequentCallingPoints", prefix = "lt8")]
161 pub subsequent_calling_points: SubsequentCallingPointsListWrapper,
162}
163
164#[derive(Debug, Default, YaDeserialize, YaSerialize)]
165#[yaserde(
166 rename = "location",
167 namespaces = {
168 "lt4" = "http://thalesgroup.com/RTTI/2015-11-27/ldb/types"
169 },
170 prefix = "lt4"
171)]
172pub struct Location {
173 #[yaserde(rename = "locationName", prefix = "lt4")]
174 pub location_name: String,
175}
176
177#[derive(Debug, Default, YaDeserialize, YaSerialize)]
178#[yaserde(
179 rename = "destination",
180 namespaces = {
181 "lt4" = "http://thalesgroup.com/RTTI/2015-11-27/ldb/types",
182 "lt5" = "http://thalesgroup.com/RTTI/2016-02-16/ldb/types"
183 },
184 prefix = "lt5"
185)]
186pub struct Destination {
187 #[yaserde(rename = "location", prefix = "lt4")]
188 pub locations: Vec<Location>,
189}
190
191pub trait RequestType {
192 fn id() -> &'static str;
193 fn year_version() -> &'static str;
194 fn values(&self) -> Vec<(&'static str, String)>;
195
196 fn generate_request_body(&self, ldb_token: &str) -> String {
198 let mut request_tag = format!(" <ldb:{}Request>\n", Self::id());
199 for value in self.values() {
200 let value_name = value.0;
201 let value_val = value.1;
202 request_tag += &format!(" <ldb:{value_name}>{value_val}</ldb:{value_name}>\n");
203 }
204 request_tag += &format!(" </ldb:{}Request>", Self::id());
205
206 return format!(
207 r#"
208<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:typ="http://thalesgroup.com/RTTI/2013-11-28/Token/types" xmlns:ldb="http://thalesgroup.com/RTTI/2021-11-01/ldb/">
209 <soap:Header>
210 <typ:AccessToken>
211 <typ:TokenValue>{}</typ:TokenValue>
212 </typ:AccessToken>
213 </soap:Header>
214 <soap:Body>
215{}
216 </soap:Body>
217</soap:Envelope>"#,
218 ldb_token, request_tag
219 );
220 }
221
222 fn generate_soap_action_header(&self) -> String {
224 format!("http://thalesgroup.com/RTTI/{}/ldb/{}", Self::year_version(), Self::id())
225 }
226}
227
228pub struct GetDepBoardWithDetailsRequest {
229 pub num_rows: i32,
230 pub crs: String,
231 pub filter_crs: Option<String>,
232 pub filter_type: Option<String>,
233 pub time_offset: Option<i32>,
234 pub time_window: Option<i32>,
235}
236
237impl GetDepBoardWithDetailsRequest {
238 pub fn get_station_board_result(response_raw: &str) -> Result<StationBoardWithDetails, String> {
240 let envelope: SoapEnvelopeGetDepBoardWithDetailsResponse = de::from_str(response_raw)?;
241 let station_board_result = envelope
242 .body
243 .dep_board_with_details_response
244 .station_board_result;
245 Ok(station_board_result)
246 }
247}
248
249impl RequestType for GetDepBoardWithDetailsRequest {
250 fn id() -> &'static str {
251 "GetDepBoardWithDetails"
252 }
253
254 fn year_version() -> &'static str {
255 "2015-05-14"
256 }
257
258 fn values(&self) -> Vec<(&'static str, String)> {
259 let mut v = vec![
260 ("numRows", self.num_rows.to_string()),
261 ("crs", self.crs.clone()),
262 ];
263
264 if let Some(ref filter_crs) = self.filter_crs {
265 v.push(("filterCrs", filter_crs.clone()));
266 }
267
268 if let Some(ref filter_type) = self.filter_type {
269 v.push(("filterType", filter_type.clone()));
270 }
271
272 if let Some(time_offset) = self.time_offset {
273 v.push(("timeOffset", time_offset.to_string()));
274 }
275
276 if let Some(time_window) = self.time_window {
277 v.push(("timeWindow", time_window.to_string()));
278 }
279
280 v
281 }
282}