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
//! This module contains rust structs that describe the response
//! of the tankerkoenig stations API.
//!
//! The json responses are parsed with serde to strongly typed
//! rust structs.

/// Response of the tankerkoenig API for a request of fuel
/// stations in a certain area
#[derive(Debug, Deserialize, Serialize, PartialEq, PartialOrd, Clone)]
pub struct AreaNearResponse {
    /// Request status
    pub ok: bool,
    /// Data licence
    pub license: String,
    /// Data source
    pub data: String,
    /// Request status
    pub status: String,
    /// Vector of fuel stations in the area
    pub stations: Vec<NearStation>,
}

/// Information of a fuel station in the area returned by the
/// [AreaNearResponse].
#[derive(Debug, Deserialize, Serialize, PartialEq, PartialOrd, Clone)]
#[serde(rename_all = "camelCase")]
pub struct NearStation {
    /// Station id
    pub id: String,
    /// Name of the station
    pub name: String,
    /// Brand of the station (e.g. JET)
    pub brand: Option<String>,
    /// Street where the station is located
    pub street: String,
    /// Street number
    pub house_number: Option<String>,
    /// Local post code
    pub post_code: i64,
    /// Area of the station
    pub place: String,
    /// Latitude (geolocation)
    pub lat: f64,
    /// Longitude (geolocation)
    pub lng: f64,
    /// Distance from search point
    pub dist: f64,
    /// Diesel price
    pub diesel: Option<f64>,
    /// E5 price
    pub e5: Option<f64>,
    /// E10 price
    pub e10: Option<f64>,
    /// Open or Closed
    pub is_open: bool,
}

/// Response of the tankerkoenig API for a request for fuel in a
/// certain area
#[derive(Debug, Deserialize, Serialize, PartialEq, PartialOrd, Clone)]
pub struct AreaFuelResponse {
    /// Request status
    pub ok: bool,
    /// Data licence
    pub license: String,
    /// Data source
    pub data: String,
    /// Request status
    pub status: String,
    /// List of stations in the area with the requested fuel
    pub stations: Vec<AreaStationFuel>,
}

/// Information about the fuel of a station in the area, returned
/// by the [AreaFuelResponse]
#[derive(Debug, Deserialize, Serialize, PartialEq, PartialOrd, Clone)]
#[serde(rename_all = "camelCase")]
pub struct AreaStationFuel {
    /// Station id
    pub id: String,
    /// Station name
    pub name: String,
    /// Brand of the station (e.g. JET)
    pub brand: Option<String>,
    /// Street where the station is located
    pub street: String,
    /// Street number
    pub house_number: Option<String>,
    /// Local post code
    pub post_code: i64,
    /// Area of the station
    pub place: String,
    /// Latitude (geolocation)
    pub lat: f64,
    /// Longitude (geolocation)
    pub lng: f64,
    /// Distance of the station
    pub dist: f64,
    /// Price of the requested fuel
    pub price: Option<f64>,
    /// Open or closed
    pub is_open: bool,
}

/// Response of the tankerkoenig API for a request of detailed
/// information for a specific fuel station
#[derive(Debug, Deserialize, Serialize, PartialEq, PartialOrd, Clone)]
pub struct DetailsResponse {
    /// Request status
    pub ok: bool,
    /// Data licence
    pub license: String,
    /// Data source
    pub data: String,
    /// Request status
    pub status: String,
    /// Detailed fuel station information
    pub station: DetailStation,
}

/// Detailed information of a fuel station, returned by the [DetailsResponse]
#[derive(Debug, Deserialize, Serialize, PartialEq, PartialOrd, Clone)]
#[serde(rename_all = "camelCase")]
pub struct DetailStation {
    /// Station id
    pub id: String,
    /// Name of the station
    pub name: String,
    /// Brand of the station (e.g. JET)
    pub brand: String,
    /// Street where the station is located
    pub street: String,
    /// Street number
    pub house_number: Option<String>,
    /// Local post code
    pub post_code: i64,
    /// Area of the station
    pub place: String,
    /// Open 24 hours
    pub whole_day: bool,
    ///Currently open or closed
    pub is_open: bool,
    /// Diesel price
    pub diesel: Option<f64>,
    /// E5 price
    pub e5: Option<f64>,
    /// E10 price
    pub e10: Option<f64>,
    /// Latitude (geolocation)
    pub lat: f64,
    /// Longitude (geolocation)
    pub lng: f64,
    /// State of the station
    pub state: Option<String>,
    /// Additional information
    pub overrides: Vec<String>,
    /// Opening times of the station
    pub opening_times: Vec<OpeningTimes>,
}

/// Opening times for a fuel station
#[derive(Debug, Deserialize, Serialize, PartialEq, PartialOrd, Clone, Hash, Eq, Ord)]
pub struct OpeningTimes {
    /// Information about the scope of start end end
    pub text: String,
    /// Opening time
    pub start: String,
    /// Closing time
    pub end: String,
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn deserialize_area_station_response() {
        let data = std::fs::read_to_string("./test/data/list.json").unwrap();
        let station_response: AreaFuelResponse = serde_json::from_str(&data).unwrap();
        let station = station_response.stations.get(0).unwrap();
        assert!(station_response.ok);
        assert_eq!(
            station_response.license,
            "CC BY 4.0 -  https://creativecommons.tankerkoenig.de"
        );
        assert_eq!(station_response.data, "MTS-K");
        assert_eq!(station_response.status, "ok");
        assert_eq!(station.id, "474e5046-deaf-4f9b-9a32-9797b778f047");
        assert_eq!(station.name, "TotalEnergies Berlin");
        assert_eq!(station.brand, Some(String::from("TotalEnergies")));
        assert_eq!(station.street, "Margarete-Sommer-Str.");
        assert_eq!(station.place, "Berlin");
        assert_eq!(station.lat, 52.530831);
        assert_eq!(station.lng, 13.440946);
        assert_eq!(station.dist, 0_f64);
        assert_eq!(station.price, Some(1.649));
        assert!(station.is_open);
        assert_eq!(station.house_number, Some(String::from("2")));
        assert_eq!(station.post_code, 10407);
    }

    #[test]
    fn deserialize_area_stations_near_response() {
        let data = std::fs::read_to_string("./test/data/list_near.json").unwrap();
        let station_response: AreaNearResponse = serde_json::from_str(&data).unwrap();
        let station = station_response.stations.get(0).unwrap();
        assert!(station_response.ok);
        assert_eq!(
            station_response.license,
            "CC BY 4.0 -  https://creativecommons.tankerkoenig.de"
        );
        assert_eq!(station_response.data, "MTS-K");
        assert_eq!(station_response.status, "ok");
        assert_eq!(station.id, "474e5046-deaf-4f9b-9a32-9797b778f047");
        assert_eq!(station.name, "TotalEnergies Berlin");
        assert_eq!(station.brand, Some(String::from("TotalEnergies")));
        assert_eq!(station.street, "Margarete-Sommer-Str.");
        assert_eq!(station.place, "Berlin");
        assert_eq!(station.lat, 52.530831);
        assert_eq!(station.lng, 13.440946);
        assert_eq!(station.dist, 0_f64);
        assert_eq!(station.diesel, Some(1.489));
        assert_eq!(station.e5, Some(1.649));
        assert_eq!(station.e10, Some(1.589));
        assert!(station.is_open);
        assert_eq!(station.house_number, Some(String::from("2")));
        assert_eq!(station.post_code, 10407);
    }

    #[test]
    fn deserialize_detail_station_response() {
        let data = std::fs::read_to_string("./test/data/detail.json").unwrap();
        let station_detail_response: DetailsResponse = serde_json::from_str(&data).unwrap();
        let opening_times = station_detail_response
            .station
            .opening_times
            .get(0)
            .unwrap();
        assert!(station_detail_response.ok);
        assert_eq!(
            station_detail_response.license,
            "CC BY 4.0 -  https://creativecommons.tankerkoenig.de"
        );
        assert_eq!(station_detail_response.data, "MTS-K");
        assert_eq!(station_detail_response.status, "ok");
        assert_eq!(
            station_detail_response.station.id,
            "24a381e3-0d72-416d-bfd8-b2f65f6e5802"
        );
        assert_eq!(station_detail_response.station.name, "Esso Tankstelle");
        assert_eq!(station_detail_response.station.brand, "ESSO");
        assert_eq!(station_detail_response.station.street, "HAUPTSTR. 7");
        assert_eq!(
            station_detail_response.station.house_number,
            Some(String::from(" "))
        );
        assert_eq!(station_detail_response.station.post_code, 84152);
        assert_eq!(station_detail_response.station.place, "MENGKOFEN");
        assert!(!station_detail_response.station.whole_day);
        assert!(!station_detail_response.station.is_open);
        assert_eq!(station_detail_response.station.e5, Some(1.379));
        assert_eq!(station_detail_response.station.e10, Some(1.359));
        assert_eq!(station_detail_response.station.diesel, Some(1.169));
        assert_eq!(station_detail_response.station.lat, 48.72210601);
        assert_eq!(station_detail_response.station.lng, 12.44438439);
        assert_eq!(station_detail_response.station.state, None);
        assert_eq!(
            station_detail_response.station.overrides.get(0).unwrap(),
            "13.04.2017, 15:00:00 - 13.11.2017, 15:00:00: geschlossen"
        );
        assert_eq!(opening_times.text, "Mo-Fr");
        assert_eq!(opening_times.start, "06:00:00");
        assert_eq!(opening_times.end, "22:30:00");
    }
}