implement caching for vvs api
This commit is contained in:
parent
3550ab1fc4
commit
98a697f576
751
Cargo.lock
generated
751
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
14
Cargo.toml
14
Cargo.toml
@ -1,15 +1,15 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "merklingen_connection_check"
|
name = "merklingen_connection_check"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
chrono = { version = "0.4.39", features = ["serde"] }
|
chrono = { version = "0.4.41", features = ["serde"] }
|
||||||
html-escape = "0.2.13"
|
html-escape = "0.2.13"
|
||||||
quick-xml = { version = "0.37.2", features = ["serialize"] }
|
quick-xml = { version = "0.37.5", features = ["serialize"] }
|
||||||
reqwest = { version = "0.12.12", features = ["charset", "h2", "http2", "macos-system-configuration", "rustls-tls"], default-features = false }
|
reqwest = { version = "0.12.20", features = ["charset", "h2", "http2", "macos-system-configuration", "rustls-tls"], default-features = false }
|
||||||
rocket = "0.5.1"
|
rocket = "0.5.1"
|
||||||
serde = { version = "1.0.217", features = ["derive"] }
|
serde = { version = "1.0.219", features = ["derive"] }
|
||||||
serde_json = "1.0.138"
|
serde_json = "1.0.140"
|
||||||
tokio = { version = "1.43.0", features = ["full"] }
|
tokio = { version = "1.45.1", features = ["full"] }
|
||||||
urlencoding = "2.1.3"
|
urlencoding = "2.1.3"
|
||||||
|
233
src/main.rs
233
src/main.rs
@ -1,17 +1,51 @@
|
|||||||
mod bahnhof;
|
mod bahnhof;
|
||||||
mod vvs;
|
mod vvs;
|
||||||
|
|
||||||
use std::{collections::BTreeMap, sync::Arc, time::Duration};
|
use std::{
|
||||||
|
collections::{BTreeMap, HashMap},
|
||||||
|
sync::Arc,
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
use chrono::{Datelike, Local, TimeDelta, Timelike};
|
use chrono::{Datelike, Local, TimeDelta, Timelike};
|
||||||
use rocket::{get, http::Status, response::content::RawHtml, routes, FromForm, State};
|
use rocket::{FromForm, State, get, http::Status, response::content::RawHtml, routes};
|
||||||
use tokio::{
|
use tokio::{
|
||||||
sync::Mutex,
|
sync::Mutex,
|
||||||
time::{sleep, Instant},
|
time::{Instant, sleep},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::vvs::VvsStopIdentifier;
|
||||||
|
|
||||||
#[rocket::launch]
|
#[rocket::launch]
|
||||||
async fn rocket() -> _ {
|
async fn rocket() -> _ {
|
||||||
|
let vvs_stop_cache = Arc::new(Mutex::new(HashMap::<
|
||||||
|
String,
|
||||||
|
(Instant, Result<Option<(String, VvsStopIdentifier)>, String>),
|
||||||
|
>::new()));
|
||||||
|
let vvs_time_cache = Arc::new(Mutex::new(HashMap::<
|
||||||
|
(
|
||||||
|
VvsStopIdentifier,
|
||||||
|
VvsStopIdentifier,
|
||||||
|
u16,
|
||||||
|
u8,
|
||||||
|
u8,
|
||||||
|
u8,
|
||||||
|
u8,
|
||||||
|
Option<u16>,
|
||||||
|
bool,
|
||||||
|
),
|
||||||
|
(Instant, Result<Option<u32>, String>),
|
||||||
|
>::new()));
|
||||||
|
let vvs_tc = Arc::clone(&vvs_time_cache);
|
||||||
|
let vvs_sc = Arc::clone(&vvs_stop_cache);
|
||||||
|
tokio::spawn(async move {
|
||||||
|
loop {
|
||||||
|
tokio::time::sleep(Duration::from_secs(60 * 30)).await;
|
||||||
|
let now = tokio::time::Instant::now();
|
||||||
|
vvs_tc.lock().await.retain(|_, (t, ..)| *t >= now);
|
||||||
|
vvs_sc.lock().await.retain(|_, (t, ..)| *t >= now);
|
||||||
|
}
|
||||||
|
});
|
||||||
rocket::build()
|
rocket::build()
|
||||||
.manage(Arc::new(()))
|
.manage(Arc::new(()))
|
||||||
.manage(Mutex::new(
|
.manage(Mutex::new(
|
||||||
@ -21,6 +55,8 @@ async fn rocket() -> _ {
|
|||||||
String,
|
String,
|
||||||
)>::None,
|
)>::None,
|
||||||
))
|
))
|
||||||
|
.manage(vvs_stop_cache)
|
||||||
|
.manage(vvs_time_cache)
|
||||||
.mount(
|
.mount(
|
||||||
"/",
|
"/",
|
||||||
routes![
|
routes![
|
||||||
@ -271,9 +307,15 @@ async fn rate_limit(rate_limit: &State<Arc<()>>) -> Arc<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[get("/vvs/find_stop/simple/<stop>")]
|
#[get("/vvs/find_stop/simple/<stop>")]
|
||||||
async fn vvs_find_stop_simple(stop: &str, counter: &State<Arc<()>>) -> String {
|
async fn vvs_find_stop_simple(
|
||||||
|
stop: &str,
|
||||||
|
counter: &State<Arc<()>>,
|
||||||
|
vvs_stop_cache: &State<
|
||||||
|
Arc<Mutex<HashMap<String, (Instant, Result<Option<(String, VvsStopIdentifier)>, String>)>>>,
|
||||||
|
>,
|
||||||
|
) -> String {
|
||||||
let _counter = rate_limit(counter).await;
|
let _counter = rate_limit(counter).await;
|
||||||
match vvs::find_stop_best(stop).await {
|
match vvs::find_stop_best(stop, &mut *vvs_stop_cache.lock().await).await {
|
||||||
Ok(Some((name, _))) => {
|
Ok(Some((name, _))) => {
|
||||||
format!("ok: {name}")
|
format!("ok: {name}")
|
||||||
}
|
}
|
||||||
@ -296,6 +338,29 @@ async fn vvs_short_in_hrs_mins_before_simple(
|
|||||||
to: &str,
|
to: &str,
|
||||||
form: VvsShortInHrsMinsBeforeSimple,
|
form: VvsShortInHrsMinsBeforeSimple,
|
||||||
counter: &State<Arc<()>>,
|
counter: &State<Arc<()>>,
|
||||||
|
vvs_stop_cache: &State<
|
||||||
|
Arc<Mutex<HashMap<String, (Instant, Result<Option<(String, VvsStopIdentifier)>, String>)>>>,
|
||||||
|
>,
|
||||||
|
vvs_time_cache: &State<
|
||||||
|
Arc<
|
||||||
|
Mutex<
|
||||||
|
HashMap<
|
||||||
|
(
|
||||||
|
VvsStopIdentifier,
|
||||||
|
VvsStopIdentifier,
|
||||||
|
u16,
|
||||||
|
u8,
|
||||||
|
u8,
|
||||||
|
u8,
|
||||||
|
u8,
|
||||||
|
Option<u16>,
|
||||||
|
bool,
|
||||||
|
),
|
||||||
|
(Instant, Result<Option<u32>, String>),
|
||||||
|
>,
|
||||||
|
>,
|
||||||
|
>,
|
||||||
|
>,
|
||||||
) -> (Status, String) {
|
) -> (Status, String) {
|
||||||
let time = Local::now() + Duration::from_secs_f64(hrs * 60.0 * 60.0);
|
let time = Local::now() + Duration::from_secs_f64(hrs * 60.0 * 60.0);
|
||||||
let form = VvsMinsBeforeSimpleForm {
|
let form = VvsMinsBeforeSimpleForm {
|
||||||
@ -307,7 +372,7 @@ async fn vvs_short_in_hrs_mins_before_simple(
|
|||||||
interchanges: form.interchanges,
|
interchanges: form.interchanges,
|
||||||
allowtaxi: form.allowtaxi,
|
allowtaxi: form.allowtaxi,
|
||||||
};
|
};
|
||||||
vvs_mins_before_simple_impl(from, to, form, counter).await
|
vvs_mins_before_simple_impl(from, to, form, counter, vvs_stop_cache, vvs_time_cache).await
|
||||||
}
|
}
|
||||||
#[derive(FromForm)]
|
#[derive(FromForm)]
|
||||||
struct VvsShortInHrsMinsBeforeCheckSimple {
|
struct VvsShortInHrsMinsBeforeCheckSimple {
|
||||||
@ -324,6 +389,29 @@ async fn vvs_short_in_hrs_mins_before_check_simple(
|
|||||||
to: &str,
|
to: &str,
|
||||||
form: VvsShortInHrsMinsBeforeCheckSimple,
|
form: VvsShortInHrsMinsBeforeCheckSimple,
|
||||||
counter: &State<Arc<()>>,
|
counter: &State<Arc<()>>,
|
||||||
|
vvs_stop_cache: &State<
|
||||||
|
Arc<Mutex<HashMap<String, (Instant, Result<Option<(String, VvsStopIdentifier)>, String>)>>>,
|
||||||
|
>,
|
||||||
|
vvs_time_cache: &State<
|
||||||
|
Arc<
|
||||||
|
Mutex<
|
||||||
|
HashMap<
|
||||||
|
(
|
||||||
|
VvsStopIdentifier,
|
||||||
|
VvsStopIdentifier,
|
||||||
|
u16,
|
||||||
|
u8,
|
||||||
|
u8,
|
||||||
|
u8,
|
||||||
|
u8,
|
||||||
|
Option<u16>,
|
||||||
|
bool,
|
||||||
|
),
|
||||||
|
(Instant, Result<Option<u32>, String>),
|
||||||
|
>,
|
||||||
|
>,
|
||||||
|
>,
|
||||||
|
>,
|
||||||
) -> (Status, String) {
|
) -> (Status, String) {
|
||||||
let time = Local::now() + Duration::from_secs_f64(hrs * 60.0 * 60.0);
|
let time = Local::now() + Duration::from_secs_f64(hrs * 60.0 * 60.0);
|
||||||
let form = VvsMinsBeforeCheckSimpleForm {
|
let form = VvsMinsBeforeCheckSimpleForm {
|
||||||
@ -336,7 +424,7 @@ async fn vvs_short_in_hrs_mins_before_check_simple(
|
|||||||
minutes: form.minutes,
|
minutes: form.minutes,
|
||||||
allowtaxi: form.allowtaxi,
|
allowtaxi: form.allowtaxi,
|
||||||
};
|
};
|
||||||
vvs_mins_before_check_simple_impl(from, to, form, counter).await
|
vvs_mins_before_check_simple_impl(from, to, form, counter, vvs_stop_cache, vvs_time_cache).await
|
||||||
}
|
}
|
||||||
#[derive(FromForm)]
|
#[derive(FromForm)]
|
||||||
struct VvsMinsBeforeSimpleForm {
|
struct VvsMinsBeforeSimpleForm {
|
||||||
@ -354,40 +442,87 @@ async fn vvs_mins_before_simple(
|
|||||||
to: &str,
|
to: &str,
|
||||||
form: VvsMinsBeforeSimpleForm,
|
form: VvsMinsBeforeSimpleForm,
|
||||||
counter: &State<Arc<()>>,
|
counter: &State<Arc<()>>,
|
||||||
|
vvs_stop_cache: &State<
|
||||||
|
Arc<Mutex<HashMap<String, (Instant, Result<Option<(String, VvsStopIdentifier)>, String>)>>>,
|
||||||
|
>,
|
||||||
|
vvs_time_cache: &State<
|
||||||
|
Arc<
|
||||||
|
Mutex<
|
||||||
|
HashMap<
|
||||||
|
(
|
||||||
|
VvsStopIdentifier,
|
||||||
|
VvsStopIdentifier,
|
||||||
|
u16,
|
||||||
|
u8,
|
||||||
|
u8,
|
||||||
|
u8,
|
||||||
|
u8,
|
||||||
|
Option<u16>,
|
||||||
|
bool,
|
||||||
|
),
|
||||||
|
(Instant, Result<Option<u32>, String>),
|
||||||
|
>,
|
||||||
|
>,
|
||||||
|
>,
|
||||||
|
>,
|
||||||
) -> (Status, String) {
|
) -> (Status, String) {
|
||||||
vvs_mins_before_simple_impl(from, to, form, counter).await
|
vvs_mins_before_simple_impl(from, to, form, counter, vvs_stop_cache, vvs_time_cache).await
|
||||||
}
|
}
|
||||||
async fn vvs_mins_before_simple_impl(
|
async fn vvs_mins_before_simple_impl(
|
||||||
from: &str,
|
from: &str,
|
||||||
to: &str,
|
to: &str,
|
||||||
form: VvsMinsBeforeSimpleForm,
|
form: VvsMinsBeforeSimpleForm,
|
||||||
counter: &State<Arc<()>>,
|
counter: &State<Arc<()>>,
|
||||||
|
vvs_stop_cache: &State<
|
||||||
|
Arc<Mutex<HashMap<String, (Instant, Result<Option<(String, VvsStopIdentifier)>, String>)>>>,
|
||||||
|
>,
|
||||||
|
vvs_time_cache: &State<
|
||||||
|
Arc<
|
||||||
|
Mutex<
|
||||||
|
HashMap<
|
||||||
|
(
|
||||||
|
VvsStopIdentifier,
|
||||||
|
VvsStopIdentifier,
|
||||||
|
u16,
|
||||||
|
u8,
|
||||||
|
u8,
|
||||||
|
u8,
|
||||||
|
u8,
|
||||||
|
Option<u16>,
|
||||||
|
bool,
|
||||||
|
),
|
||||||
|
(Instant, Result<Option<u32>, String>),
|
||||||
|
>,
|
||||||
|
>,
|
||||||
|
>,
|
||||||
|
>,
|
||||||
) -> (Status, String) {
|
) -> (Status, String) {
|
||||||
let _counter = rate_limit(counter).await;
|
let _counter = rate_limit(counter).await;
|
||||||
if Arc::strong_count(&counter) > 2 {}
|
let mut vvs_stop_cache_lock = vvs_stop_cache.lock().await;
|
||||||
let (_, from) = match vvs::find_stop_best(from).await {
|
let (_, from) = match vvs::find_stop_best(from, &mut *vvs_stop_cache_lock).await {
|
||||||
Ok(Some(v)) => v,
|
Ok(Some(v)) => v,
|
||||||
Ok(None) => {
|
Ok(None) => {
|
||||||
return (
|
return (
|
||||||
Status::NotFound,
|
Status::NotFound,
|
||||||
format!("err: Couldn't find origin stop {from}"),
|
format!("err: Couldn't find origin stop {from}"),
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
Err(e) => return (Status::InternalServerError, format!("err: {e}")),
|
Err(e) => return (Status::InternalServerError, format!("err: {e}")),
|
||||||
};
|
};
|
||||||
let (_, to) = match vvs::find_stop_best(to).await {
|
let (_, to) = match vvs::find_stop_best(to, &mut *vvs_stop_cache_lock).await {
|
||||||
Ok(Some(v)) => v,
|
Ok(Some(v)) => v,
|
||||||
Ok(None) => {
|
Ok(None) => {
|
||||||
return (
|
return (
|
||||||
Status::NotFound,
|
Status::NotFound,
|
||||||
format!("err: Couldn't find destination stop {to}"),
|
format!("err: Couldn't find destination stop {to}"),
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
Err(e) => return (Status::InternalServerError, format!("err: {e}")),
|
Err(e) => return (Status::InternalServerError, format!("err: {e}")),
|
||||||
};
|
};
|
||||||
|
drop(vvs_stop_cache_lock);
|
||||||
match vvs::find_trip_arrival_before(
|
match vvs::find_trip_arrival_before(
|
||||||
&from,
|
from,
|
||||||
&to,
|
to,
|
||||||
form.year,
|
form.year,
|
||||||
form.month,
|
form.month,
|
||||||
form.day,
|
form.day,
|
||||||
@ -395,6 +530,7 @@ async fn vvs_mins_before_simple_impl(
|
|||||||
form.minute,
|
form.minute,
|
||||||
form.interchanges,
|
form.interchanges,
|
||||||
form.allowtaxi.is_some_and(|allow| allow),
|
form.allowtaxi.is_some_and(|allow| allow),
|
||||||
|
&mut *vvs_time_cache.lock().await,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
@ -423,39 +559,87 @@ async fn vvs_mins_before_check_simple(
|
|||||||
to: &str,
|
to: &str,
|
||||||
form: VvsMinsBeforeCheckSimpleForm,
|
form: VvsMinsBeforeCheckSimpleForm,
|
||||||
counter: &State<Arc<()>>,
|
counter: &State<Arc<()>>,
|
||||||
|
vvs_stop_cache: &State<
|
||||||
|
Arc<Mutex<HashMap<String, (Instant, Result<Option<(String, VvsStopIdentifier)>, String>)>>>,
|
||||||
|
>,
|
||||||
|
vvs_time_cache: &State<
|
||||||
|
Arc<
|
||||||
|
Mutex<
|
||||||
|
HashMap<
|
||||||
|
(
|
||||||
|
VvsStopIdentifier,
|
||||||
|
VvsStopIdentifier,
|
||||||
|
u16,
|
||||||
|
u8,
|
||||||
|
u8,
|
||||||
|
u8,
|
||||||
|
u8,
|
||||||
|
Option<u16>,
|
||||||
|
bool,
|
||||||
|
),
|
||||||
|
(Instant, Result<Option<u32>, String>),
|
||||||
|
>,
|
||||||
|
>,
|
||||||
|
>,
|
||||||
|
>,
|
||||||
) -> (Status, String) {
|
) -> (Status, String) {
|
||||||
vvs_mins_before_check_simple_impl(from, to, form, counter).await
|
vvs_mins_before_check_simple_impl(from, to, form, counter, vvs_stop_cache, vvs_time_cache).await
|
||||||
}
|
}
|
||||||
async fn vvs_mins_before_check_simple_impl(
|
async fn vvs_mins_before_check_simple_impl(
|
||||||
from: &str,
|
from: &str,
|
||||||
to: &str,
|
to: &str,
|
||||||
form: VvsMinsBeforeCheckSimpleForm,
|
form: VvsMinsBeforeCheckSimpleForm,
|
||||||
counter: &State<Arc<()>>,
|
counter: &State<Arc<()>>,
|
||||||
|
vvs_stop_cache: &State<
|
||||||
|
Arc<Mutex<HashMap<String, (Instant, Result<Option<(String, VvsStopIdentifier)>, String>)>>>,
|
||||||
|
>,
|
||||||
|
vvs_time_cache: &State<
|
||||||
|
Arc<
|
||||||
|
Mutex<
|
||||||
|
HashMap<
|
||||||
|
(
|
||||||
|
VvsStopIdentifier,
|
||||||
|
VvsStopIdentifier,
|
||||||
|
u16,
|
||||||
|
u8,
|
||||||
|
u8,
|
||||||
|
u8,
|
||||||
|
u8,
|
||||||
|
Option<u16>,
|
||||||
|
bool,
|
||||||
|
),
|
||||||
|
(Instant, Result<Option<u32>, String>),
|
||||||
|
>,
|
||||||
|
>,
|
||||||
|
>,
|
||||||
|
>,
|
||||||
) -> (Status, String) {
|
) -> (Status, String) {
|
||||||
let _counter = rate_limit(counter).await;
|
let _counter = rate_limit(counter).await;
|
||||||
let (_, from) = match vvs::find_stop_best(from).await {
|
let mut vvs_stop_cache_lock = vvs_stop_cache.lock().await;
|
||||||
|
let (_, from) = match vvs::find_stop_best(from, &mut *vvs_stop_cache_lock).await {
|
||||||
Ok(Some(v)) => v,
|
Ok(Some(v)) => v,
|
||||||
Ok(None) => {
|
Ok(None) => {
|
||||||
return (
|
return (
|
||||||
Status::NotFound,
|
Status::NotFound,
|
||||||
format!("err: Couldn't find origin stop {from}"),
|
format!("err: Couldn't find origin stop {from}"),
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
Err(e) => return (Status::InternalServerError, format!("err: {e}")),
|
Err(e) => return (Status::InternalServerError, format!("err: {e}")),
|
||||||
};
|
};
|
||||||
let (_, to) = match vvs::find_stop_best(to).await {
|
let (_, to) = match vvs::find_stop_best(to, &mut *vvs_stop_cache_lock).await {
|
||||||
Ok(Some(v)) => v,
|
Ok(Some(v)) => v,
|
||||||
Ok(None) => {
|
Ok(None) => {
|
||||||
return (
|
return (
|
||||||
Status::NotFound,
|
Status::NotFound,
|
||||||
format!("err: Couldn't find destination stop {to}"),
|
format!("err: Couldn't find destination stop {to}"),
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
Err(e) => return (Status::InternalServerError, format!("err: {e}")),
|
Err(e) => return (Status::InternalServerError, format!("err: {e}")),
|
||||||
};
|
};
|
||||||
|
drop(vvs_stop_cache_lock);
|
||||||
match vvs::find_trip_arrival_before(
|
match vvs::find_trip_arrival_before(
|
||||||
&from,
|
from,
|
||||||
&to,
|
to,
|
||||||
form.year,
|
form.year,
|
||||||
form.month,
|
form.month,
|
||||||
form.day,
|
form.day,
|
||||||
@ -463,16 +647,17 @@ async fn vvs_mins_before_check_simple_impl(
|
|||||||
form.minute,
|
form.minute,
|
||||||
form.interchanges,
|
form.interchanges,
|
||||||
form.allowtaxi.is_some_and(|allow| allow),
|
form.allowtaxi.is_some_and(|allow| allow),
|
||||||
|
&mut *vvs_time_cache.lock().await,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(Some(v)) => {
|
Ok(Some(v)) => {
|
||||||
if v <= form.minutes {
|
if *v <= form.minutes {
|
||||||
(Status::Ok, String::new())
|
(Status::Ok, String::new())
|
||||||
} else {
|
} else {
|
||||||
(
|
(
|
||||||
Status::Gone,
|
Status::Gone,
|
||||||
format!("gone: {v} > {} minutes", form.minutes),
|
format!("gone: {} > {} minutes", *v, form.minutes),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
132
src/vvs.rs
132
src/vvs.rs
@ -1,21 +1,57 @@
|
|||||||
|
use std::{collections::HashMap, time::Duration};
|
||||||
|
|
||||||
use chrono::{DateTime, Local, TimeZone};
|
use chrono::{DateTime, Local, TimeZone};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
use tokio::time::Instant;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
pub struct VvsStopIdentifier {
|
pub struct VvsStopIdentifier {
|
||||||
id: String,
|
id: String,
|
||||||
gid: String,
|
gid: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn find_stop_best(query: &str) -> Result<Option<(String, VvsStopIdentifier)>, String> {
|
pub async fn find_stop_best(
|
||||||
Ok(find_stop(query)
|
query: &str,
|
||||||
|
cache: &mut HashMap<String, (Instant, Result<Option<(String, VvsStopIdentifier)>, String>)>,
|
||||||
|
) -> Result<Option<(String, VvsStopIdentifier)>, String> {
|
||||||
|
if let Some(cache_hit) = cache
|
||||||
|
.get(query)
|
||||||
|
.filter(|cache_hit| cache_hit.0 >= Instant::now())
|
||||||
|
{
|
||||||
|
cache_hit.1.clone()
|
||||||
|
} else {
|
||||||
|
let r = find_stop_best_no_cache(query).await;
|
||||||
|
cache
|
||||||
|
.entry(query.to_owned())
|
||||||
|
.insert_entry((
|
||||||
|
Instant::now()
|
||||||
|
// cached value's max age:
|
||||||
|
+ match &r {
|
||||||
|
// found stop, unlikely to change
|
||||||
|
Ok(Some((_, _))) => Duration::from_secs(20 * 60),
|
||||||
|
// found no stop, not very likely to change either
|
||||||
|
Ok(None) => Duration::from_secs(5 * 60),
|
||||||
|
// something went wrong, allow relatively quick retries
|
||||||
|
Err(_) => Duration::from_secs(30),
|
||||||
|
},
|
||||||
|
r,
|
||||||
|
))
|
||||||
|
.get()
|
||||||
|
.1
|
||||||
|
.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub async fn find_stop_best_no_cache(
|
||||||
|
query: &str,
|
||||||
|
) -> Result<Option<(String, VvsStopIdentifier)>, String> {
|
||||||
|
Ok(find_stop_no_cache(query)
|
||||||
.await?
|
.await?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|(name, _types, identifier)| Some((name, identifier?)))
|
.filter_map(|(name, _types, identifier)| Some((name, identifier?)))
|
||||||
.next())
|
.next())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn find_stop(
|
pub async fn find_stop_no_cache(
|
||||||
query: &str,
|
query: &str,
|
||||||
) -> Result<
|
) -> Result<
|
||||||
Vec<(
|
Vec<(
|
||||||
@ -132,7 +168,79 @@ pub async fn find_stop(
|
|||||||
/// of the remaining trips, calculates how many minutes before the requested arrival time the departure time is, and returns the minimum of these durations in minutes.
|
/// of the remaining trips, calculates how many minutes before the requested arrival time the departure time is, and returns the minimum of these durations in minutes.
|
||||||
/// For example, if you request to arrive at 8:50, a trip arrives at 8:45 and departs at 8:40, then this function would return `Some(10)`.
|
/// For example, if you request to arrive at 8:50, a trip arrives at 8:45 and departs at 8:40, then this function would return `Some(10)`.
|
||||||
/// Returns `None` if no valid trips were found.
|
/// Returns `None` if no valid trips were found.
|
||||||
pub async fn find_trip_arrival_before(
|
pub async fn find_trip_arrival_before<'a>(
|
||||||
|
from: VvsStopIdentifier,
|
||||||
|
to: VvsStopIdentifier,
|
||||||
|
year: u16,
|
||||||
|
month: u8,
|
||||||
|
day: u8,
|
||||||
|
hour: u8,
|
||||||
|
minute: u8,
|
||||||
|
max_interchanges: Option<u16>,
|
||||||
|
allow_taxi: bool,
|
||||||
|
cache: &'a mut HashMap<
|
||||||
|
(
|
||||||
|
VvsStopIdentifier,
|
||||||
|
VvsStopIdentifier,
|
||||||
|
u16,
|
||||||
|
u8,
|
||||||
|
u8,
|
||||||
|
u8,
|
||||||
|
u8,
|
||||||
|
Option<u16>,
|
||||||
|
bool,
|
||||||
|
),
|
||||||
|
(Instant, Result<Option<u32>, String>),
|
||||||
|
>,
|
||||||
|
) -> &'a Result<Option<u32>, String> {
|
||||||
|
let query = (
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
year,
|
||||||
|
month,
|
||||||
|
day,
|
||||||
|
hour,
|
||||||
|
minute,
|
||||||
|
max_interchanges,
|
||||||
|
allow_taxi,
|
||||||
|
);
|
||||||
|
if cache
|
||||||
|
.get(&query)
|
||||||
|
.is_none_or(|cache_hit| cache_hit.0 < Instant::now())
|
||||||
|
{
|
||||||
|
let r = find_trip_arrival_before_no_cache(
|
||||||
|
&query.0,
|
||||||
|
&query.1,
|
||||||
|
year,
|
||||||
|
month,
|
||||||
|
day,
|
||||||
|
hour,
|
||||||
|
minute,
|
||||||
|
max_interchanges,
|
||||||
|
allow_taxi,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
cache.insert(
|
||||||
|
query.clone(),
|
||||||
|
(
|
||||||
|
Instant::now()
|
||||||
|
// cached value's max age:
|
||||||
|
+ match &r {
|
||||||
|
// found connection, may change, especially shortly before arrival,
|
||||||
|
// because delays can cause the requested arrival time to no longer be met.
|
||||||
|
Ok(Some(minutes)) => Duration::from_secs((*minutes as u64 * 10).clamp(30, 180)),
|
||||||
|
// found no connection, not extremely likely to change
|
||||||
|
Ok(None) => Duration::from_secs(180),
|
||||||
|
// something went wrong, allow relatively quick retries
|
||||||
|
Err(_) => Duration::from_secs(30),
|
||||||
|
},
|
||||||
|
r,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
&cache.get(&query).unwrap().1
|
||||||
|
}
|
||||||
|
pub async fn find_trip_arrival_before_no_cache(
|
||||||
from: &VvsStopIdentifier,
|
from: &VvsStopIdentifier,
|
||||||
to: &VvsStopIdentifier,
|
to: &VvsStopIdentifier,
|
||||||
year: u16,
|
year: u16,
|
||||||
@ -234,9 +342,9 @@ pub async fn find_trip_arrival_before(
|
|||||||
}
|
}
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct Response2 {
|
struct Response2 {
|
||||||
/// in seconds
|
// /// in seconds
|
||||||
#[serde(rename = "duration")]
|
// #[serde(rename = "duration")]
|
||||||
duration: i32,
|
// duration: i32,
|
||||||
#[serde(rename = "origin")]
|
#[serde(rename = "origin")]
|
||||||
origin: Response3,
|
origin: Response3,
|
||||||
#[serde(rename = "destination")]
|
#[serde(rename = "destination")]
|
||||||
@ -246,8 +354,8 @@ pub async fn find_trip_arrival_before(
|
|||||||
}
|
}
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct Response3 {
|
struct Response3 {
|
||||||
#[serde(rename = "name")]
|
// #[serde(rename = "name")]
|
||||||
name: String,
|
// name: String,
|
||||||
#[serde(rename = "departureTimeBaseTimetable")]
|
#[serde(rename = "departureTimeBaseTimetable")]
|
||||||
pub departure_time_base_timetable: Option<DateTime<Local>>,
|
pub departure_time_base_timetable: Option<DateTime<Local>>,
|
||||||
#[serde(rename = "departureTimePlanned")]
|
#[serde(rename = "departureTimePlanned")]
|
||||||
@ -257,8 +365,8 @@ pub async fn find_trip_arrival_before(
|
|||||||
}
|
}
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct Response4 {
|
struct Response4 {
|
||||||
#[serde(rename = "name")]
|
// #[serde(rename = "name")]
|
||||||
name: String,
|
// name: String,
|
||||||
#[serde(rename = "arrivalTimeBaseTimetable")]
|
#[serde(rename = "arrivalTimeBaseTimetable")]
|
||||||
pub arrival_time_base_timetable: Option<DateTime<Local>>,
|
pub arrival_time_base_timetable: Option<DateTime<Local>>,
|
||||||
#[serde(rename = "arrivalTimePlanned")]
|
#[serde(rename = "arrivalTimePlanned")]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user