diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml
index 58ed0c8..6952f86 100644
--- a/.github/workflows/rust.yml
+++ b/.github/workflows/rust.yml
@@ -10,13 +10,13 @@ jobs:
rust: [stable, nightly]
steps:
- - uses: rui314/setup-mold@v1
- - uses: hecrj/setup-rust-action@v1
- with:
- rust-version: ${{ matrix.rust }}
- - uses: actions/checkout@master
- - name: Run tests
- run: cargo check --verbose
+ - uses: rui314/setup-mold@v1
+ - uses: hecrj/setup-rust-action@v1
+ with:
+ rust-version: ${{ matrix.rust }}
+ - uses: actions/checkout@master
+ - name: Run tests
+ run: cargo check --verbose
test:
if: "!contains(github.event.head_commit.message, '[SKIP CI]')"
@@ -27,13 +27,13 @@ jobs:
rust: [stable, nightly]
steps:
- - uses: rui314/setup-mold@v1
- - uses: hecrj/setup-rust-action@v1
- with:
- rust-version: ${{ matrix.rust }}
- - uses: actions/checkout@master
- - name: Run tests
- run: cargo test --verbose
+ - uses: rui314/setup-mold@v1
+ - uses: hecrj/setup-rust-action@v1
+ with:
+ rust-version: ${{ matrix.rust }}
+ - uses: actions/checkout@master
+ - name: Run tests
+ run: cargo test --verbose
test-fastfloat:
if: "!contains(github.event.head_commit.message, '[SKIP CI]')"
@@ -44,19 +44,18 @@ jobs:
rust: [stable, nightly]
steps:
- - uses: rui314/setup-mold@v1
- - uses: hecrj/setup-rust-action@v1
- with:
- rust-version: ${{ matrix.rust }}
- - uses: actions/checkout@master
- - name: Run tests
- run: cargo test --verbose --features fastfloat
+ - uses: rui314/setup-mold@v1
+ - uses: hecrj/setup-rust-action@v1
+ with:
+ rust-version: ${{ matrix.rust }}
+ - uses: actions/checkout@master
+ - name: Run tests
+ run: cargo test --verbose --features fastfloat
rustdoc:
runs-on: ubuntu-latest
-
+ needs: [check, test, test-fastfloat]
steps:
- - uses: rui314/setup-mold@v1
- name: Checkout repository
uses: actions/checkout@v2
@@ -65,6 +64,7 @@ jobs:
- name: Deploy Docs
uses: peaceiris/actions-gh-pages@364c31d33bb99327c77b3a5438a83a357a6729ad # v3.4.0
+ if: success() && github.ref == 'refs/heads/main' && !contains(github.event.head_commit.message, '[SKIP CI]')
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_branch: gh-pages
diff --git a/Cargo.toml b/Cargo.toml
index d42b067..47870c6 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,7 +1,7 @@
[package]
name = "lta_models"
-version = "0.5.0"
-authors = ["budinverse "]
+version = "0.6.0"
+authors = ["zeon256 "]
edition = "2021"
license = "MIT"
description = "πModels for lta-rs"
@@ -13,22 +13,24 @@ exclude = [
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
-serde = { version = "1.0.159", features = ["derive"] }
+serde = { version = "1.0.195", features = ["derive"] }
serde_repr = "0.1.12"
-serde_json = "1.0.95"
regex = "1.7.3"
-time = { version = "0.3.20", features = ["serde-human-readable"]}
+time = { version = "0.3.20", features = ["serde-human-readable", "macros"]}
lazy_static = "1.4.0"
fast-float = { version = "0.2", optional = true }
+serde_json = "1.0.40"
[dev-dependencies]
-serde_json = "1.0.40"
+bincode = "1.3.3"
criterion = "0.3"
mimalloc = "0.1.25"
+rmp-serde = "1.1.2"
+flexbuffers = "2.0.0"
[features]
fastfloat = ["fast-float"]
[[bench]]
name = "benchmark"
-harness = false
\ No newline at end of file
+harness = false
diff --git a/README.md b/README.md
index fe22642..13280cc 100644
--- a/README.md
+++ b/README.md
@@ -15,6 +15,9 @@
+
+
+
This repository contains the data structures required to interact with LTA's datamall APIs. All data structures implements `Serialize` and `Deserialize`.
@@ -23,9 +26,19 @@ This repository contains the data structures required to interact with LTA's dat
## `Cargo.toml` setup
```toml
# extra features available: fastfloat
-lta-models = { version = "0.5.0" }
+lta-models = { version = "0.6.0" }
```
+## Supported formats
+
+| Format | Supported? | Tested? |
+| ----------- | ---------- | ------- |
+| JSON | β
| β
|
+| Bincode | β
| β
|
+| Flexbuffer | β
| β
|
+| MessagePack | β
| β
|
+
+
## Performance & `fast-float` implementation
Some of the deserialization code _may_ benefit from using the `fastfloat` feature, but during testing the biggest performance improvement can be seen when you swap out the system allocator to something faster like [`mimalloc`](https://github.com/microsoft/mimalloc) or [`jemalloc`](https://github.com/jemalloc/jemalloc)
diff --git a/benches/benchmark.rs b/benches/benchmark.rs
index 3aca0a4..9d08309 100644
--- a/benches/benchmark.rs
+++ b/benches/benchmark.rs
@@ -1,20 +1,25 @@
use criterion::{black_box, criterion_group, criterion_main, Criterion};
+use lta_models::{bus::bus_arrival::NextBusRaw, prelude::*};
use mimalloc::MiMalloc;
+use time::macros::datetime;
#[global_allocator]
static GLOBAL: MiMalloc = MiMalloc;
#[rustfmt::skip]
mod de {
- use lta_models::{prelude::*, crowd::crowd_density::{CrowdDensityForecast, CrowdDensityForecastRawResp}};
+ use std::fmt::Debug;
+
+ use lta_models::prelude::*;
+ use lta_models::bus::bus_arrival::NextBusRaw;
use serde::{Deserialize, Serialize};
pub fn generate_bench<'de, I, S, F>(input_fn: F) -> S
where
F: FnOnce() -> &'de str,
I: Deserialize<'de> + Into,
- S: Serialize,
+ S: Serialize + Debug
{
let str_data = input_fn();
serde_json::from_str(str_data).map(|v: I| v.into()).unwrap()
@@ -31,7 +36,7 @@ mod de {
}
pub fn bus_arrival() -> BusArrivalResp {
- gen!(RawBusArrivalResp, _, "../dumped_data/bus_arrival.json")
+ gen!(BusArrivalRespRaw, _, "../dumped_data/bus_arrival.json")
}
pub fn bus_routes() -> Vec {
@@ -94,69 +99,90 @@ mod de {
gen!(StationCrowdLevelRawResp, _, "../dumped_data/crowd_density_rt.json")
}
- pub fn crowd_density_forecast() -> CrowdDensityForecast {
+ pub fn crowd_density_forecast() -> CrowdDensityForecast {
gen!(CrowdDensityForecastRawResp, _, "../dumped_data/crowd_density_forecast.json")
}
+
+ pub fn transmute_nextbus(data: NextBusRaw) -> NextBus {
+ let nx = unsafe {
+ std::mem::transmute::<_, NextBus>(data)
+ };
+ nx
+ }
}
#[rustfmt::skip]
pub fn criterion_benchmark(c: &mut Criterion) {
- c.bench_function("de::bike_parking.json", |b| b.iter(|| de::bike_parking()));
- c.bench_function("de::bus_arrival.json", |b| b.iter(|| de::bus_arrival()));
- c.bench_function("de::bus_route.json", |b| b.iter(|| de::bus_routes()));
- c.bench_function("de::bus_services.json", |b| b.iter(|| de::bus_service()));
- c.bench_function("de::bus_stops.json", |b| b.iter(|| de::bus_stops()));
- c.bench_function("de::carpark_avail.json", |b| b.iter(|| de::carpark_avail()));
- c.bench_function("de::erp_rates.json", |b| b.iter(|| de::erp_rates()));
- c.bench_function("de::est_travel_time.json", |b| b.iter(|| de::est_travel_time()));
- c.bench_function("de::faulty_traffic_lights.json", |b| b.iter(|| de::faulty_traffic_lights()));
- c.bench_function("de::passenger_vol_bus_stops.json", |b| b.iter(|| de::passenger_vol_bus_stops()));
- c.bench_function("de::passenger_od_bus_stops.json", |b| b.iter(|| de::passenger_vol_od_bus_stops()));
- c.bench_function("de::passenger_vol_od_train.json", |b| b.iter(|| de::passenger_vol_od_train()));
- c.bench_function("de::passenger_vol_train.json", |b| b.iter(|| de::passenger_vol_train()));
- c.bench_function("de::taxi_avail.json", |b| b.iter(|| de::taxi_avail()));
- c.bench_function("de::taxi_stands.json", |b| b.iter(|| de::taxi_stands()));
- c.bench_function("de::train_service_alert.json", |b| b.iter(|| de::train_service_alert()));
- c.bench_function("de::crowd_density_rt.json", |b| b.iter(|| de::crowd_density_rt()));
- c.bench_function("de::crowd_density_forecast.json", |b| b.iter(|| de::crowd_density_forecast()));
-
- let bike_parking = de::bike_parking();
- let bus_arrival = de::bus_arrival();
- let bus_routes = de::bus_routes();
- let bus_service = de::bus_service();
- let bus_stops = de::bus_stops();
- let carpark_avail = de::carpark_avail();
- let erp_rates = de::erp_rates();
- let est_travel_time = de::est_travel_time();
- let faulty_traffic_lights = de::faulty_traffic_lights();
- let passenger_vol_bus_stops = de::passenger_vol_bus_stops();
- let passenger_vol_od_bus_stops = de::passenger_vol_od_bus_stops();
- let passenger_vol_train = de::passenger_vol_train();
- let passenger_vol_od_train = de::passenger_vol_od_train();
- let taxi_avail = de::taxi_avail();
- let taxi_stands = de::taxi_stands();
- let train_service_alert = de::train_service_alert();
- let crowd_density_rt = de::crowd_density_rt();
- let crowd_density_forecast = de::crowd_density_forecast();
-
- c.bench_function("ser::bike_parking.json", |b| b.iter(|| serde_json::to_string(black_box(&bike_parking))));
- c.bench_function("ser::bus_arrival.json", |b| b.iter(|| serde_json::to_string(black_box(&bus_arrival))));
- c.bench_function("ser::bus_route.json", |b| b.iter(|| serde_json::to_string(black_box(&bus_routes))));
- c.bench_function("ser::bus_services.json", |b| b.iter(|| serde_json::to_string(black_box(&bus_service))));
- c.bench_function("ser::bus_stops.json", |b| b.iter(|| serde_json::to_string(black_box(&bus_stops))));
- c.bench_function("ser::carpark_avail.json", |b| b.iter(|| serde_json::to_string(black_box(&carpark_avail))));
- c.bench_function("ser::erp_rates.json", |b| b.iter(|| serde_json::to_string(black_box(&erp_rates))));
- c.bench_function("ser::est_travel_time.json", |b| b.iter(|| serde_json::to_string(black_box(&est_travel_time))));
- c.bench_function("ser::faulty_traffic_lights.json", |b| b.iter(|| serde_json::to_string(black_box(&faulty_traffic_lights))));
- c.bench_function("ser::passenger_vol_bus_stops.json", |b| b.iter(|| serde_json::to_string(black_box(&passenger_vol_bus_stops))));
- c.bench_function("ser::passenger_od_bus_stops.json", |b| b.iter(|| serde_json::to_string(black_box(&passenger_vol_od_bus_stops))));
- c.bench_function("ser::passenger_vol_od_train.json", |b| b.iter(|| serde_json::to_string(black_box(&passenger_vol_od_train))));
- c.bench_function("ser::passenger_vol_train.json", |b| b.iter(|| serde_json::to_string(black_box(&passenger_vol_train))));
- c.bench_function("ser::taxi_avail.json", |b| b.iter(|| serde_json::to_string(black_box(&taxi_avail))));
- c.bench_function("ser::taxi_stands.json", |b| b.iter(|| serde_json::to_string(black_box(&taxi_stands))));
- c.bench_function("ser::train_service_alert.json", |b| b.iter(|| serde_json::to_string(black_box(&train_service_alert))));
- c.bench_function("ser::crowd_density_rt.json", |b| b.iter(|| serde_json::to_string(black_box(&crowd_density_rt))));
- c.bench_function("ser::crowd_density_forecast.json", |b| b.iter(|| serde_json::to_string(black_box(&crowd_density_forecast))));
+ let sample_data = NextBusRaw {
+ origin_code: 77009,
+ dest_code: 77009,
+ est_arrival: datetime!(2023-04-06 14:47:57 +8),
+ lat: 1.314452,
+ long: 103.910009,
+ visit_no: 1,
+ load: BusLoad::SeatsAvailable,
+ feature: BusFeature::WheelChairAccessible,
+ bus_type: BusType::SingleDecker,
+ };
+
+ c.bench_function("transmute_nextbus", |b| b.iter(|| de::transmute_nextbus(black_box(sample_data.clone()))));
+ c.bench_function("into_nextbus", |b| b.iter(|| NextBus::from(black_box(sample_data.clone()))));
+ c.bench_function("de::bike_parking.json", |b| b.iter(|| de::bike_parking()));
+ c.bench_function("de::bus_arrival.json", |b| b.iter(|| de::bus_arrival()));
+ c.bench_function("de::bus_route.json", |b| b.iter(|| de::bus_routes()));
+ c.bench_function("de::bus_services.json", |b| b.iter(|| de::bus_service()));
+ c.bench_function("de::bus_stops.json", |b| b.iter(|| de::bus_stops()));
+ c.bench_function("de::carpark_avail.json", |b| b.iter(|| de::carpark_avail()));
+ c.bench_function("de::erp_rates.json", |b| b.iter(|| de::erp_rates()));
+ c.bench_function("de::est_travel_time.json", |b| b.iter(|| de::est_travel_time()));
+ c.bench_function("de::faulty_traffic_lights.json", |b| b.iter(|| de::faulty_traffic_lights()));
+ c.bench_function("de::passenger_vol_bus_stops.json", |b| b.iter(|| de::passenger_vol_bus_stops()));
+ c.bench_function("de::passenger_od_bus_stops.json", |b| b.iter(|| de::passenger_vol_od_bus_stops()));
+ c.bench_function("de::passenger_vol_od_train.json", |b| b.iter(|| de::passenger_vol_od_train()));
+ c.bench_function("de::passenger_vol_train.json", |b| b.iter(|| de::passenger_vol_train()));
+ c.bench_function("de::taxi_avail.json", |b| b.iter(|| de::taxi_avail()));
+ c.bench_function("de::taxi_stands.json", |b| b.iter(|| de::taxi_stands()));
+ c.bench_function("de::train_service_alert.json", |b| b.iter(|| de::train_service_alert()));
+ c.bench_function("de::crowd_density_rt.json", |b| b.iter(|| de::crowd_density_rt()));
+ c.bench_function("de::crowd_density_forecast.json", |b| b.iter(|| de::crowd_density_forecast()));
+
+ let bike_parking = de::bike_parking();
+ let bus_arrival = de::bus_arrival();
+ let bus_routes = de::bus_routes();
+ let bus_service = de::bus_service();
+ let bus_stops = de::bus_stops();
+ let carpark_avail = de::carpark_avail();
+ let erp_rates = de::erp_rates();
+ let est_travel_time = de::est_travel_time();
+ let faulty_traffic_lights = de::faulty_traffic_lights();
+ let passenger_vol_bus_stops = de::passenger_vol_bus_stops();
+ let passenger_vol_od_bus_stops = de::passenger_vol_od_bus_stops();
+ let passenger_vol_train = de::passenger_vol_train();
+ let passenger_vol_od_train = de::passenger_vol_od_train();
+ let taxi_avail = de::taxi_avail();
+ let taxi_stands = de::taxi_stands();
+ let train_service_alert = de::train_service_alert();
+ let crowd_density_rt = de::crowd_density_rt();
+ let crowd_density_forecast = de::crowd_density_forecast();
+
+ c.bench_function("ser::bike_parking.json", |b| b.iter(|| serde_json::to_string(black_box(&bike_parking))));
+ c.bench_function("ser::bus_arrival.json", |b| b.iter(|| serde_json::to_string(black_box(&bus_arrival))));
+ c.bench_function("ser::bus_route.json", |b| b.iter(|| serde_json::to_string(black_box(&bus_routes))));
+ c.bench_function("ser::bus_services.json", |b| b.iter(|| serde_json::to_string(black_box(&bus_service))));
+ c.bench_function("ser::bus_stops.json", |b| b.iter(|| serde_json::to_string(black_box(&bus_stops))));
+ c.bench_function("ser::carpark_avail.json", |b| b.iter(|| serde_json::to_string(black_box(&carpark_avail))));
+ c.bench_function("ser::erp_rates.json", |b| b.iter(|| serde_json::to_string(black_box(&erp_rates))));
+ c.bench_function("ser::est_travel_time.json", |b| b.iter(|| serde_json::to_string(black_box(&est_travel_time))));
+ c.bench_function("ser::faulty_traffic_lights.json", |b| b.iter(|| serde_json::to_string(black_box(&faulty_traffic_lights))));
+ c.bench_function("ser::passenger_vol_bus_stops.json", |b| b.iter(|| serde_json::to_string(black_box(&passenger_vol_bus_stops))));
+ c.bench_function("ser::passenger_od_bus_stops.json", |b| b.iter(|| serde_json::to_string(black_box(&passenger_vol_od_bus_stops))));
+ c.bench_function("ser::passenger_vol_od_train.json", |b| b.iter(|| serde_json::to_string(black_box(&passenger_vol_od_train))));
+ c.bench_function("ser::passenger_vol_train.json", |b| b.iter(|| serde_json::to_string(black_box(&passenger_vol_train))));
+ c.bench_function("ser::taxi_avail.json", |b| b.iter(|| serde_json::to_string(black_box(&taxi_avail))));
+ c.bench_function("ser::taxi_stands.json", |b| b.iter(|| serde_json::to_string(black_box(&taxi_stands))));
+ c.bench_function("ser::train_service_alert.json", |b| b.iter(|| serde_json::to_string(black_box(&train_service_alert))));
+ c.bench_function("ser::crowd_density_rt.json", |b| b.iter(|| serde_json::to_string(black_box(&crowd_density_rt))));
+ c.bench_function("ser::crowd_density_forecast.json", |b| b.iter(|| serde_json::to_string(black_box(&crowd_density_forecast))));
}
criterion_group!(benches, criterion_benchmark);
diff --git a/dumped_data/bus_arrival.json b/dumped_data/bus_arrival.json
index a6bbba3..4ecaf2d 100644
--- a/dumped_data/bus_arrival.json
+++ b/dumped_data/bus_arrival.json
@@ -1,117 +1,413 @@
{
"odata.metadata": "http://datamall2.mytransport.sg/ltaodataservice/$metadata#BusArrivalv2/@Element",
- "BusStopCode": "83139",
+ "BusStopCode": "82009",
"Services": [
- {
- "ServiceNo": "15",
- "Operator": "GAS",
- "NextBus": {
- "OriginCode": "77009",
- "DestinationCode": "77009",
- "EstimatedArrival": "2020-09-30T19:25:55+08:00",
- "Latitude": "1.3319648333333334",
- "Longitude": "103.9025555",
- "VisitNumber": "1",
- "Load": "SEA",
- "Feature": "WAB",
- "Type": "SD"
+ {
+ "ServiceNo": "150",
+ "Operator": "SBST",
+ "NextBus": {
+ "OriginCode": "82009",
+ "DestinationCode": "82009",
+ "EstimatedArrival": "2024-01-17T18:00:00+08:00",
+ "Latitude": "0.0",
+ "Longitude": "0.0",
+ "VisitNumber": "1",
+ "Load": "SEA",
+ "Feature": "WAB",
+ "Type": "SD"
+ },
+ "NextBus2": {
+ "OriginCode": "82009",
+ "DestinationCode": "82009",
+ "EstimatedArrival": "2024-01-17T18:15:00+08:00",
+ "Latitude": "0.0",
+ "Longitude": "0.0",
+ "VisitNumber": "1",
+ "Load": "SEA",
+ "Feature": "WAB",
+ "Type": "SD"
+ },
+ "NextBus3": {
+ "OriginCode": "82009",
+ "DestinationCode": "82009",
+ "EstimatedArrival": "2024-01-17T18:30:00+08:00",
+ "Latitude": "0.0",
+ "Longitude": "0.0",
+ "VisitNumber": "1",
+ "Load": "SEA",
+ "Feature": "WAB",
+ "Type": "SD"
+ }
},
- "NextBus2": {
- "OriginCode": "77009",
- "DestinationCode": "77009",
- "EstimatedArrival": "2020-09-30T19:40:55+08:00",
- "Latitude": "1.3449385",
- "Longitude": "103.9371435",
- "VisitNumber": "1",
- "Load": "SEA",
- "Feature": "WAB",
- "Type": "SD"
+ {
+ "ServiceNo": "154",
+ "Operator": "GAS",
+ "NextBus": {
+ "OriginCode": "82009",
+ "DestinationCode": "22009",
+ "EstimatedArrival": "2024-01-17T18:01:00+08:00",
+ "Latitude": "0.0",
+ "Longitude": "0.0",
+ "VisitNumber": "1",
+ "Load": "SEA",
+ "Feature": "WAB",
+ "Type": "SD"
+ },
+ "NextBus2": {
+ "OriginCode": "82009",
+ "DestinationCode": "22009",
+ "EstimatedArrival": "2024-01-17T18:09:00+08:00",
+ "Latitude": "0.0",
+ "Longitude": "0.0",
+ "VisitNumber": "1",
+ "Load": "SEA",
+ "Feature": "WAB",
+ "Type": "SD"
+ },
+ "NextBus3": {
+ "OriginCode": "82009",
+ "DestinationCode": "22009",
+ "EstimatedArrival": "2024-01-17T18:18:00+08:00",
+ "Latitude": "0.0",
+ "Longitude": "0.0",
+ "VisitNumber": "1",
+ "Load": "SEA",
+ "Feature": "WAB",
+ "Type": "DD"
+ }
},
- "NextBus3": {
- "OriginCode": "77009",
- "DestinationCode": "77009",
- "EstimatedArrival": "2020-09-30T19:57:37+08:00",
- "Latitude": "1.3692186666666668",
- "Longitude": "103.94679866666667",
- "VisitNumber": "1",
- "Load": "SEA",
- "Feature": "WAB",
- "Type": "SD"
- }
- },
- {
- "ServiceNo": "150",
- "Operator": "SBST",
- "NextBus": {
- "OriginCode": "82009",
- "DestinationCode": "82009",
- "EstimatedArrival": "2020-09-30T19:23:44+08:00",
- "Latitude": "1.3184525",
- "Longitude": "103.90032683333334",
- "VisitNumber": "1",
- "Load": "SEA",
- "Feature": "WAB",
- "Type": "SD"
+ {
+ "ServiceNo": "22",
+ "Operator": "SBST",
+ "NextBus": {
+ "OriginCode": "82009",
+ "DestinationCode": "54009",
+ "EstimatedArrival": "2024-01-17T17:59:00+08:00",
+ "Latitude": "0.0",
+ "Longitude": "0.0",
+ "VisitNumber": "1",
+ "Load": "SEA",
+ "Feature": "WAB",
+ "Type": "SD"
+ },
+ "NextBus2": {
+ "OriginCode": "82009",
+ "DestinationCode": "54009",
+ "EstimatedArrival": "2024-01-17T18:12:00+08:00",
+ "Latitude": "0.0",
+ "Longitude": "0.0",
+ "VisitNumber": "1",
+ "Load": "SEA",
+ "Feature": "WAB",
+ "Type": "SD"
+ },
+ "NextBus3": {
+ "OriginCode": "82009",
+ "DestinationCode": "54009",
+ "EstimatedArrival": "2024-01-17T18:19:00+08:00",
+ "Latitude": "0.0",
+ "Longitude": "0.0",
+ "VisitNumber": "1",
+ "Load": "SEA",
+ "Feature": "WAB",
+ "Type": "SD"
+ }
},
- "NextBus2": {
- "OriginCode": "82009",
- "DestinationCode": "82009",
- "EstimatedArrival": "2020-09-30T19:33:48+08:00",
- "Latitude": "0",
- "Longitude": "0",
- "VisitNumber": "1",
- "Load": "SEA",
- "Feature": "WAB",
- "Type": "SD"
+ {
+ "ServiceNo": "60",
+ "Operator": "SBST",
+ "NextBus": {
+ "OriginCode": "82009",
+ "DestinationCode": "82009",
+ "EstimatedArrival": "2024-01-17T17:56:00+08:00",
+ "Latitude": "0.0",
+ "Longitude": "0.0",
+ "VisitNumber": "1",
+ "Load": "SEA",
+ "Feature": "WAB",
+ "Type": "SD"
+ },
+ "NextBus2": {
+ "OriginCode": "82009",
+ "DestinationCode": "82009",
+ "EstimatedArrival": "2024-01-17T18:08:00+08:00",
+ "Latitude": "0.0",
+ "Longitude": "0.0",
+ "VisitNumber": "1",
+ "Load": "SEA",
+ "Feature": "WAB",
+ "Type": "DD"
+ },
+ "NextBus3": {
+ "OriginCode": "82009",
+ "DestinationCode": "82009",
+ "EstimatedArrival": "2024-01-17T18:18:00+08:00",
+ "Latitude": "0.0",
+ "Longitude": "0.0",
+ "VisitNumber": "1",
+ "Load": "SEA",
+ "Feature": "WAB",
+ "Type": "SD"
+ }
},
- "NextBus3": {
- "OriginCode": "82009",
- "DestinationCode": "82009",
- "EstimatedArrival": "2020-09-30T19:48:48+08:00",
- "Latitude": "0",
- "Longitude": "0",
- "VisitNumber": "1",
- "Load": "SEA",
- "Feature": "WAB",
- "Type": "SD"
- }
- },
- {
- "ServiceNo": "155",
- "Operator": "SBST",
- "NextBus": {
- "OriginCode": "52009",
- "DestinationCode": "84009",
- "EstimatedArrival": "2020-09-30T19:21:28+08:00",
- "Latitude": "1.3187676666666666",
- "Longitude": "103.902475",
- "VisitNumber": "1",
- "Load": "SDA",
- "Feature": "WAB",
- "Type": "SD"
+ {
+ "ServiceNo": "60A",
+ "Operator": "SBST",
+ "NextBus": {
+ "OriginCode": "82009",
+ "DestinationCode": "84511",
+ "EstimatedArrival": "2024-01-17T18:02:00+08:00",
+ "Latitude": "0.0",
+ "Longitude": "0.0",
+ "VisitNumber": "1",
+ "Load": "SEA",
+ "Feature": "WAB",
+ "Type": "DD"
+ },
+ "NextBus2": {
+ "OriginCode": "82009",
+ "DestinationCode": "84511",
+ "EstimatedArrival": "2024-01-17T18:23:00+08:00",
+ "Latitude": "0.0",
+ "Longitude": "0.0",
+ "VisitNumber": "1",
+ "Load": "SEA",
+ "Feature": "WAB",
+ "Type": "SD"
+ },
+ "NextBus3": {
+ "OriginCode": "",
+ "DestinationCode": "",
+ "EstimatedArrival": "",
+ "Latitude": "",
+ "Longitude": "",
+ "VisitNumber": "",
+ "Load": "",
+ "Feature": "",
+ "Type": ""
+ }
+ },
+ {
+ "ServiceNo": "61",
+ "Operator": "SMRT",
+ "NextBus": {
+ "OriginCode": "82009",
+ "DestinationCode": "43009",
+ "EstimatedArrival": "2024-01-17T17:58:00+08:00",
+ "Latitude": "0.0",
+ "Longitude": "0.0",
+ "VisitNumber": "1",
+ "Load": "SEA",
+ "Feature": "WAB",
+ "Type": "SD"
+ },
+ "NextBus2": {
+ "OriginCode": "82009",
+ "DestinationCode": "43009",
+ "EstimatedArrival": "2024-01-17T18:10:00+08:00",
+ "Latitude": "0.0",
+ "Longitude": "0.0",
+ "VisitNumber": "1",
+ "Load": "SEA",
+ "Feature": "WAB",
+ "Type": "SD"
+ },
+ "NextBus3": {
+ "OriginCode": "82009",
+ "DestinationCode": "43009",
+ "EstimatedArrival": "2024-01-17T18:22:00+08:00",
+ "Latitude": "0.0",
+ "Longitude": "0.0",
+ "VisitNumber": "1",
+ "Load": "SEA",
+ "Feature": "WAB",
+ "Type": "SD"
+ }
+ },
+ {
+ "ServiceNo": "63",
+ "Operator": "SBST",
+ "NextBus": {
+ "OriginCode": "82009",
+ "DestinationCode": "82009",
+ "EstimatedArrival": "2024-01-17T18:01:00+08:00",
+ "Latitude": "0.0",
+ "Longitude": "0.0",
+ "VisitNumber": "1",
+ "Load": "SEA",
+ "Feature": "WAB",
+ "Type": "SD"
+ },
+ "NextBus2": {
+ "OriginCode": "82009",
+ "DestinationCode": "82009",
+ "EstimatedArrival": "2024-01-17T18:11:00+08:00",
+ "Latitude": "0.0",
+ "Longitude": "0.0",
+ "VisitNumber": "1",
+ "Load": "SEA",
+ "Feature": "WAB",
+ "Type": "SD"
+ },
+ "NextBus3": {
+ "OriginCode": "82009",
+ "DestinationCode": "82009",
+ "EstimatedArrival": "2024-01-17T18:21:00+08:00",
+ "Latitude": "0.0",
+ "Longitude": "0.0",
+ "VisitNumber": "1",
+ "Load": "SEA",
+ "Feature": "WAB",
+ "Type": "SD"
+ }
+ },
+ {
+ "ServiceNo": "63M",
+ "Operator": "SBST",
+ "NextBus": {
+ "OriginCode": "82009",
+ "DestinationCode": "82009",
+ "EstimatedArrival": "2024-01-17T18:02:00+08:00",
+ "Latitude": "0.0",
+ "Longitude": "0.0",
+ "VisitNumber": "1",
+ "Load": "SEA",
+ "Feature": "WAB",
+ "Type": "SD"
+ },
+ "NextBus2": {
+ "OriginCode": "82009",
+ "DestinationCode": "82009",
+ "EstimatedArrival": "2024-01-17T18:17:00+08:00",
+ "Latitude": "0.0",
+ "Longitude": "0.0",
+ "VisitNumber": "1",
+ "Load": "SEA",
+ "Feature": "WAB",
+ "Type": "SD"
+ },
+ "NextBus3": {
+ "OriginCode": "82009",
+ "DestinationCode": "82009",
+ "EstimatedArrival": "2024-01-17T18:32:00+08:00",
+ "Latitude": "0.0",
+ "Longitude": "0.0",
+ "VisitNumber": "1",
+ "Load": "SEA",
+ "Feature": "WAB",
+ "Type": "SD"
+ }
+ },
+ {
+ "ServiceNo": "76",
+ "Operator": "SBST",
+ "NextBus": {
+ "OriginCode": "82009",
+ "DestinationCode": "55509",
+ "EstimatedArrival": "2024-01-17T18:10:00+08:00",
+ "Latitude": "0.0",
+ "Longitude": "0.0",
+ "VisitNumber": "1",
+ "Load": "SEA",
+ "Feature": "WAB",
+ "Type": "DD"
+ },
+ "NextBus2": {
+ "OriginCode": "82009",
+ "DestinationCode": "55509",
+ "EstimatedArrival": "2024-01-17T18:21:00+08:00",
+ "Latitude": "0.0",
+ "Longitude": "0.0",
+ "VisitNumber": "1",
+ "Load": "SEA",
+ "Feature": "WAB",
+ "Type": "DD"
+ },
+ "NextBus3": {
+ "OriginCode": "82009",
+ "DestinationCode": "55509",
+ "EstimatedArrival": "2024-01-17T18:32:00+08:00",
+ "Latitude": "0.0",
+ "Longitude": "0.0",
+ "VisitNumber": "1",
+ "Load": "SEA",
+ "Feature": "WAB",
+ "Type": "SD"
+ }
},
- "NextBus2": {
- "OriginCode": "52009",
- "DestinationCode": "84009",
- "EstimatedArrival": "2020-09-30T19:33:55+08:00",
- "Latitude": "1.3196455",
- "Longitude": "103.88187283333333",
- "VisitNumber": "1",
- "Load": "SEA",
- "Feature": "WAB",
- "Type": "SD"
+ {
+ "ServiceNo": "93",
+ "Operator": "SBST",
+ "NextBus": {
+ "OriginCode": "82009",
+ "DestinationCode": "14009",
+ "EstimatedArrival": "2024-01-17T18:03:00+08:00",
+ "Latitude": "0.0",
+ "Longitude": "0.0",
+ "VisitNumber": "1",
+ "Load": "SEA",
+ "Feature": "WAB",
+ "Type": "SD"
+ },
+ "NextBus2": {
+ "OriginCode": "82009",
+ "DestinationCode": "14009",
+ "EstimatedArrival": "2024-01-17T18:18:00+08:00",
+ "Latitude": "0.0",
+ "Longitude": "0.0",
+ "VisitNumber": "1",
+ "Load": "SEA",
+ "Feature": "WAB",
+ "Type": "SD"
+ },
+ "NextBus3": {
+ "OriginCode": "82009",
+ "DestinationCode": "14009",
+ "EstimatedArrival": "2024-01-17T18:33:00+08:00",
+ "Latitude": "0.0",
+ "Longitude": "0.0",
+ "VisitNumber": "1",
+ "Load": "SEA",
+ "Feature": "WAB",
+ "Type": "SD"
+ }
},
- "NextBus3": {
- "OriginCode": "52009",
- "DestinationCode": "84009",
- "EstimatedArrival": "2020-09-30T19:50:46+08:00",
- "Latitude": "1.3332161666666666",
- "Longitude": "103.87857",
- "VisitNumber": "1",
- "Load": "SEA",
- "Feature": "WAB",
- "Type": "SD"
+ {
+ "ServiceNo": "94",
+ "Operator": "SBST",
+ "NextBus": {
+ "OriginCode": "82009",
+ "DestinationCode": "82009",
+ "EstimatedArrival": "2024-01-17T18:05:00+08:00",
+ "Latitude": "0.0",
+ "Longitude": "0.0",
+ "VisitNumber": "1",
+ "Load": "SEA",
+ "Feature": "WAB",
+ "Type": "SD"
+ },
+ "NextBus2": {
+ "OriginCode": "82009",
+ "DestinationCode": "82009",
+ "EstimatedArrival": "2024-01-17T18:14:00+08:00",
+ "Latitude": "0.0",
+ "Longitude": "0.0",
+ "VisitNumber": "1",
+ "Load": "SEA",
+ "Feature": "WAB",
+ "Type": "SD"
+ },
+ "NextBus3": {
+ "OriginCode": "82009",
+ "DestinationCode": "82009",
+ "EstimatedArrival": "2024-01-17T18:22:00+08:00",
+ "Latitude": "0.0",
+ "Longitude": "0.0",
+ "VisitNumber": "1",
+ "Load": "SEA",
+ "Feature": "WAB",
+ "Type": "DD"
+ }
}
- }
]
}
\ No newline at end of file
diff --git a/src/bus.rs b/src/bus.rs
index ac37f48..955e25d 100644
--- a/src/bus.rs
+++ b/src/bus.rs
@@ -2,16 +2,16 @@
pub mod prelude {
pub use {
- crate::bus::bus_arrival::{BusArrivalResp, RawBusArrivalResp},
+ crate::bus::bus_arrival::{BusArrivalResp, NextBus, BusArrivalRespRaw},
crate::bus::bus_routes::{BusRoute, BusRouteResp},
- crate::bus::bus_services::{BusService, BusServiceResp},
+ crate::bus::bus_services::{BusFreq, BusService, BusServiceResp},
crate::bus::bus_stops::{BusStop, BusStopsResp},
};
}
pub mod bus_arrival {
use serde::{Deserialize, Serialize};
- use time::{OffsetDateTime, serde::iso8601};
+ use time::{serde::iso8601, serde::timestamp, OffsetDateTime};
use crate::bus_enums::{BusFeature, BusLoad, BusType, Operator};
use crate::utils::de::{from_str, treat_error_as_none};
@@ -19,33 +19,27 @@ pub mod bus_arrival {
#[cfg(feature = "fastfloat")]
use crate::utils::de::from_str_fast_float;
- #[deprecated(since = "0.5", note = "Will be removed in future versions")]
- pub const URL: &str = "http://datamall2.mytransport.sg/ltaodataservice/BusArrivalv2";
-
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
- #[serde(rename_all(deserialize = "PascalCase"))]
+ #[serde(rename_all = "PascalCase")]
pub struct RawArrivalBusService {
pub service_no: String,
pub operator: Operator,
#[serde(deserialize_with = "treat_error_as_none")]
- pub next_bus: Option,
+ pub next_bus: Option,
#[serde(deserialize_with = "treat_error_as_none")]
- pub next_bus_2: Option,
+ pub next_bus_2: Option,
#[serde(deserialize_with = "treat_error_as_none")]
- pub next_bus_3: Option,
+ pub next_bus_3: Option,
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
- #[serde(rename_all(deserialize = "PascalCase"))]
pub struct ArrivalBusService {
pub service_no: String,
-
pub operator: Operator,
-
pub next_bus: [Option; 3],
}
@@ -54,68 +48,101 @@ pub mod bus_arrival {
Self {
service_no: data.service_no,
operator: data.operator,
- next_bus: [data.next_bus, data.next_bus_2, data.next_bus_3],
+ next_bus: [
+ data.next_bus.map(Into::into),
+ data.next_bus_2.map(Into::into),
+ data.next_bus_3.map(Into::into),
+ ],
}
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Deserialize, Serialize)]
- #[serde(rename_all(deserialize = "PascalCase"))]
- pub struct NextBus {
+ #[serde(rename_all = "PascalCase")]
+ pub struct NextBusRaw {
#[serde(deserialize_with = "from_str")]
pub origin_code: u32,
- #[serde(deserialize_with = "from_str", alias = "DestinationCode")]
+ #[serde(deserialize_with = "from_str", rename = "DestinationCode")]
pub dest_code: u32,
-
+
/// Time in GMT+8
- #[serde(alias = "EstimatedArrival", deserialize_with = "iso8601::deserialize")]
+ #[serde(
+ rename = "EstimatedArrival",
+ deserialize_with = "iso8601::deserialize",
+ serialize_with = "iso8601::serialize"
+ )]
pub est_arrival: OffsetDateTime,
- #[cfg(feature = "fastfloat")]
- #[serde(deserialize_with = "from_str_fast_float", alias = "Latitude")]
- pub lat: f64,
-
- #[cfg(feature = "fastfloat")]
- #[serde(deserialize_with = "from_str_fast_float", alias = "Longitude")]
- pub long: f64,
-
- #[cfg(not(feature = "fastfloat"))]
- #[serde(deserialize_with = "from_str", alias = "Latitude")]
+ #[serde(rename = "Latitude")]
+ #[cfg_attr(not(feature = "fastfloat"), serde(deserialize_with = "from_str"))]
+ #[cfg_attr(feature = "fastfloat", serde(deserialize_with = "from_str_fast_float"))]
pub lat: f64,
- #[cfg(not(feature = "fastfloat"))]
- #[serde(deserialize_with = "from_str", alias = "Longitude")]
+ #[serde(rename = "Longitude")]
+ #[cfg_attr(not(feature = "fastfloat"), serde(deserialize_with = "from_str"))]
+ #[cfg_attr(feature = "fastfloat", serde(deserialize_with = "from_str_fast_float"))]
pub long: f64,
- #[serde(deserialize_with = "from_str", alias = "VisitNumber")]
- pub visit_no: u32,
+ #[serde(deserialize_with = "from_str", rename = "VisitNumber")]
+ pub visit_no: u8,
pub load: BusLoad,
- pub feature: Option,
+ pub feature: BusFeature,
#[serde(alias = "Type")]
pub bus_type: BusType,
}
+ #[derive(Debug, Clone, PartialEq, PartialOrd, Deserialize, Serialize)]
+ pub struct NextBus {
+ pub origin_code: u32,
+ pub dest_code: u32,
+ /// Time in GMT+8
+ #[serde(with = "timestamp")]
+ pub est_arrival: OffsetDateTime,
+ pub lat: f64,
+ pub long: f64,
+ pub visit_no: u8,
+ pub load: BusLoad,
+ pub feature: BusFeature,
+ pub bus_type: BusType,
+ }
+
+ impl From for NextBus {
+ #[inline(always)]
+ fn from(r: NextBusRaw) -> Self {
+ Self {
+ origin_code: r.origin_code,
+ dest_code: r.dest_code,
+ est_arrival: r.est_arrival,
+ lat: r.lat,
+ long: r.long,
+ visit_no: r.visit_no,
+ load: r.load,
+ feature: r.feature,
+ bus_type: r.bus_type,
+ }
+ }
+ }
+
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
- #[serde(rename_all(deserialize = "PascalCase"))]
- pub struct RawBusArrivalResp {
+ #[serde(rename_all = "PascalCase")]
+ pub struct BusArrivalRespRaw {
#[serde(deserialize_with = "from_str")]
pub bus_stop_code: u32,
pub services: Vec,
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
- #[serde(rename_all(deserialize = "PascalCase"))]
pub struct BusArrivalResp {
pub bus_stop_code: u32,
pub services: Vec,
}
- impl From for BusArrivalResp {
- fn from(data: RawBusArrivalResp) -> Self {
+ impl From for BusArrivalResp {
+ fn from(data: BusArrivalRespRaw) -> Self {
Self {
bus_stop_code: data.bus_stop_code,
services: data.services.into_iter().map(|v| v.into()).collect(),
@@ -125,32 +152,31 @@ pub mod bus_arrival {
}
pub mod bus_services {
+ use std::num::NonZeroU8;
+
use crate::bus_enums::{BusCategory, Operator};
use crate::utils::de::from_str_error_as_none;
use crate::utils::regex::BUS_FREQ_RE;
use serde::{Deserialize, Deserializer, Serialize};
-
- #[deprecated(since = "0.5", note = "Will be removed in future versions")]
- pub const URL: &str = "http://datamall2.mytransport.sg/ltaodataservice/BusServices";
/// Both min and max are in terms of minutes
- #[derive(Debug, Clone, PartialEq, Serialize)]
+ #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct BusFreq {
- pub min: Option,
- pub max: Option,
+ pub min: Option,
+ pub max: Option,
}
impl BusFreq {
- pub fn new(min: u32, max: u32) -> Self {
+ pub fn new(min: u8, max: u8) -> Self {
BusFreq {
- min: Some(min),
- max: Some(max),
+ min: Some(NonZeroU8::new(min).unwrap()),
+ max: Some(NonZeroU8::new(max).unwrap()),
}
}
- pub fn no_max(min: u32) -> Self {
+ pub fn no_max(min: u8) -> Self {
BusFreq {
- min: Some(min),
+ min: Some(NonZeroU8::new(min).unwrap()),
max: None,
}
}
@@ -170,22 +196,22 @@ pub mod bus_services {
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
- #[serde(rename_all(deserialize = "PascalCase"))]
- pub struct BusService {
+ #[serde(rename_all = "PascalCase")]
+ pub struct BusServiceRaw {
pub service_no: String,
pub operator: Operator,
#[serde(alias = "Direction")]
- pub no_direction: u32,
+ pub no_direction: u8,
pub category: BusCategory,
#[serde(deserialize_with = "from_str_error_as_none")]
- pub origin_code: Option,
+ pub origin_code: Option,
#[serde(deserialize_with = "from_str_error_as_none", alias = "DestinationCode")]
- pub dest_code: Option,
+ pub dest_code: Option,
#[serde(alias = "AM_Peak_Freq", deserialize_with = "from_str_to_bus_freq")]
pub am_peak_freq: BusFreq,
@@ -199,7 +225,22 @@ pub mod bus_services {
#[serde(alias = "PM_Offpeak_Freq", deserialize_with = "from_str_to_bus_freq")]
pub pm_offpeak_freq: BusFreq,
- pub loop_desc: Option,
+ pub loop_desc: String,
+ }
+
+ #[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
+ pub struct BusService {
+ pub service_no: String,
+ pub operator: Operator,
+ pub no_direction: u8,
+ pub category: BusCategory,
+ pub origin_code: Option,
+ pub dest_code: Option,
+ pub am_peak_freq: BusFreq,
+ pub am_offpeak_freq: BusFreq,
+ pub pm_peak_freq: BusFreq,
+ pub pm_offpeak_freq: BusFreq,
+ pub loop_desc: String,
}
fn from_str_to_bus_freq<'de, D>(deserializer: D) -> Result
@@ -223,14 +264,32 @@ pub mod bus_services {
Ok(bus_freq)
}
+ impl From for BusService {
+ fn from(r: BusServiceRaw) -> Self {
+ Self {
+ service_no: r.service_no,
+ operator: r.operator,
+ no_direction: r.no_direction,
+ category: r.category,
+ origin_code: r.origin_code,
+ dest_code: r.dest_code,
+ am_offpeak_freq: r.am_offpeak_freq,
+ am_peak_freq: r.am_peak_freq,
+ pm_offpeak_freq: r.pm_offpeak_freq,
+ pm_peak_freq: r.pm_peak_freq,
+ loop_desc: r.loop_desc,
+ }
+ }
+ }
+
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
pub struct BusServiceResp {
- pub value: Vec,
+ pub value: Vec,
}
impl From for Vec {
fn from(data: BusServiceResp) -> Self {
- data.value
+ data.value.into_iter().map(Into::into).collect()
}
}
}
@@ -242,20 +301,17 @@ pub mod bus_routes {
use crate::utils::de::from_str;
use crate::utils::serde_date::str_time_option::{de_str_time_opt_br, ser_str_time_opt};
- #[deprecated(since = "0.5", note = "Will be removed in future versions")]
- pub const URL: &str = "http://datamall2.mytransport.sg/ltaodataservice/BusRoutes";
-
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
- #[serde(rename_all(deserialize = "PascalCase"))]
- pub struct BusRoute {
+ #[serde(rename_all = "PascalCase")]
+ pub struct BusRouteRaw {
pub service_no: String,
pub operator: Operator,
- pub direction: u32,
+ pub direction: u8,
#[serde(alias = "StopSequence")]
- pub stop_seq: u32,
+ pub stop_seq: u8,
#[serde(deserialize_with = "from_str")]
pub bus_stop_code: u32,
@@ -306,14 +362,50 @@ pub mod bus_routes {
pub sun_last: Option