Flesh out api and add tests
This commit is contained in:
parent
e47099f492
commit
24cf670465
1
.gitignore
vendored
1
.gitignore
vendored
@ -1 +1,2 @@
|
|||||||
/target
|
/target
|
||||||
|
.env
|
13
Cargo.lock
generated
13
Cargo.lock
generated
@ -107,6 +107,7 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
"tokio",
|
||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1093,9 +1094,21 @@ dependencies = [
|
|||||||
"mio",
|
"mio",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"socket2",
|
"socket2",
|
||||||
|
"tokio-macros",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-macros"
|
||||||
|
version = "2.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-native-tls"
|
name = "tokio-native-tls"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
|
@ -10,4 +10,5 @@ reqwest = { version = "0.12.15", features = ["json"] }
|
|||||||
serde = { version = "1.0.219", features = ["derive"] }
|
serde = { version = "1.0.219", features = ["derive"] }
|
||||||
serde_json = "1.0.140"
|
serde_json = "1.0.140"
|
||||||
thiserror = "2.0.12"
|
thiserror = "2.0.12"
|
||||||
|
tokio = { version = "1.44.2", features = ["macros", "rt"] }
|
||||||
url = "2.5.4"
|
url = "2.5.4"
|
||||||
|
76
src/lib.rs
76
src/lib.rs
@ -1,6 +1,9 @@
|
|||||||
use crate::models::Response;
|
use crate::models::Response;
|
||||||
|
use crate::models::eta::{EtaRequest, EtaResp};
|
||||||
|
use crate::models::follow::{FollowRequest, FollowResp};
|
||||||
use crate::models::route::{RouteRequest, RouteResp};
|
use crate::models::route::{RouteRequest, RouteResp};
|
||||||
use reqwest::{Client, Url};
|
use reqwest::{Client, Url};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
pub mod models;
|
pub mod models;
|
||||||
@ -32,18 +35,19 @@ impl CTAClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn fetch_route(&mut self, route: &str) -> Result<RouteResp, Error> {
|
pub async fn make_request<T: Serialize, V: for<'a> Deserialize<'a>>(
|
||||||
let req = RouteRequest {
|
&mut self,
|
||||||
rt: route.to_string(),
|
endpoint: &str,
|
||||||
key: self.key.clone(),
|
req: T,
|
||||||
};
|
) -> Result<V, Error> {
|
||||||
|
let url = self.url.join(endpoint)?;
|
||||||
|
|
||||||
let url = self.url.join("ttpositions.aspx")?;
|
let resp: Response<V> = self
|
||||||
let resp: Response<RouteResp> = self
|
|
||||||
.client
|
.client
|
||||||
.get(url)
|
.get(url)
|
||||||
.query(&req)
|
.query(&req)
|
||||||
.query(&[("outputType", "JSON")])
|
.query(&[("outputType", "JSON")])
|
||||||
|
.query(&[("key", &self.key)])
|
||||||
.send()
|
.send()
|
||||||
.await?
|
.await?
|
||||||
.json()
|
.json()
|
||||||
@ -51,4 +55,62 @@ impl CTAClient {
|
|||||||
|
|
||||||
Ok(resp.ctatt)
|
Ok(resp.ctatt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn fetch_route(&mut self, route: &str) -> Result<RouteResp, Error> {
|
||||||
|
let req = RouteRequest {
|
||||||
|
rt: route.to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
self.make_request("ttpositions.aspx", req).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn fetch_eta(&mut self, eta_request: EtaRequest) -> Result<EtaResp, Error> {
|
||||||
|
self.make_request("ttarrivals.aspx", eta_request).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn fetch_train_schedule(&mut self, run_number: u32) -> Result<FollowResp, Error> {
|
||||||
|
self.make_request("ttfollow.aspx", FollowRequest { run_number })
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::CTAClient;
|
||||||
|
use crate::models::eta::EtaRequest;
|
||||||
|
|
||||||
|
pub fn client() -> CTAClient {
|
||||||
|
let token = std::env::var("CTA_TOKEN").expect("Missing CTA_TOKEN");
|
||||||
|
|
||||||
|
CTAClient::new(token)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
pub async fn test_fetch_eta() {
|
||||||
|
let mut client = client();
|
||||||
|
|
||||||
|
let _eta = client
|
||||||
|
.fetch_eta(EtaRequest {
|
||||||
|
mapid: None,
|
||||||
|
stpid: Some(30111),
|
||||||
|
max: Some(1),
|
||||||
|
rt: None,
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
pub async fn test_fetch_route() {
|
||||||
|
let mut client = client();
|
||||||
|
|
||||||
|
let _eta = client.fetch_route("blue").await.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
pub async fn test_fetch_train_schedule() {
|
||||||
|
let mut client = client();
|
||||||
|
|
||||||
|
let _train = client.fetch_train_schedule(109).await.unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
50
src/models/eta.rs
Normal file
50
src/models/eta.rs
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
use crate::models::Ctatt;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Serialize, Debug)]
|
||||||
|
pub struct EtaRequest {
|
||||||
|
pub mapid: Option<u32>,
|
||||||
|
pub stpid: Option<u32>,
|
||||||
|
pub max: Option<u32>,
|
||||||
|
pub rt: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
pub struct EtaResp {
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub header: Ctatt,
|
||||||
|
pub eta: Vec<Eta>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
pub struct Eta {
|
||||||
|
#[serde(rename = "staId")]
|
||||||
|
pub sta_id: String,
|
||||||
|
#[serde(rename = "stpId")]
|
||||||
|
pub stp_id: String,
|
||||||
|
#[serde(rename = "staNm")]
|
||||||
|
pub sta_nm: String,
|
||||||
|
#[serde(rename = "stpDe")]
|
||||||
|
pub stp_de: String,
|
||||||
|
pub rn: String,
|
||||||
|
pub rt: String,
|
||||||
|
#[serde(rename = "destSt")]
|
||||||
|
pub dest_st: String,
|
||||||
|
#[serde(rename = "destNm")]
|
||||||
|
pub tr_dr: String,
|
||||||
|
pub prdt: String,
|
||||||
|
#[serde(rename = "arrT")]
|
||||||
|
pub arr_t: String,
|
||||||
|
#[serde(rename = "isApp")]
|
||||||
|
pub is_app: String,
|
||||||
|
#[serde(rename = "isSch")]
|
||||||
|
pub is_sch: String,
|
||||||
|
#[serde(rename = "isDly")]
|
||||||
|
pub is_dly: String,
|
||||||
|
#[serde(rename = "isFlt")]
|
||||||
|
pub is_flt: String,
|
||||||
|
pub flags: Option<String>,
|
||||||
|
pub lat: String,
|
||||||
|
pub lon: String,
|
||||||
|
pub heading: String,
|
||||||
|
}
|
53
src/models/follow.rs
Normal file
53
src/models/follow.rs
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
use crate::models::Ctatt;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Serialize, Debug)]
|
||||||
|
pub struct FollowRequest {
|
||||||
|
#[serde(rename = "runnumber")]
|
||||||
|
pub run_number: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
pub struct Position {
|
||||||
|
pub lat: String,
|
||||||
|
pub lon: String,
|
||||||
|
pub heading: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
pub struct ScheduledStop {
|
||||||
|
#[serde(rename = "staId")]
|
||||||
|
pub sta_id: String,
|
||||||
|
#[serde(rename = "stpId")]
|
||||||
|
pub stp_id: String,
|
||||||
|
#[serde(rename = "staNm")]
|
||||||
|
pub sta_nm: String,
|
||||||
|
#[serde(rename = "stpDe")]
|
||||||
|
pub stp_de: String,
|
||||||
|
pub rn: String,
|
||||||
|
pub rt: String,
|
||||||
|
#[serde(rename = "destSt")]
|
||||||
|
pub dest_st: String,
|
||||||
|
#[serde(rename = "destNm")]
|
||||||
|
pub tr_dr: String,
|
||||||
|
pub prdt: String,
|
||||||
|
#[serde(rename = "arrT")]
|
||||||
|
pub arr_t: String,
|
||||||
|
#[serde(rename = "isApp")]
|
||||||
|
pub is_app: String,
|
||||||
|
#[serde(rename = "isSch")]
|
||||||
|
pub is_sch: String,
|
||||||
|
#[serde(rename = "isDly")]
|
||||||
|
pub is_dly: String,
|
||||||
|
#[serde(rename = "isFlt")]
|
||||||
|
pub is_flt: String,
|
||||||
|
pub flags: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
pub struct FollowResp {
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub header: Ctatt,
|
||||||
|
pub position: Position,
|
||||||
|
pub eta: Vec<ScheduledStop>,
|
||||||
|
}
|
@ -1,3 +1,5 @@
|
|||||||
|
pub mod eta;
|
||||||
|
pub mod follow;
|
||||||
pub mod route;
|
pub mod route;
|
||||||
pub mod train;
|
pub mod train;
|
||||||
|
|
||||||
|
@ -5,7 +5,6 @@ use serde::{Deserialize, Serialize};
|
|||||||
#[derive(Serialize, Debug)]
|
#[derive(Serialize, Debug)]
|
||||||
pub struct RouteRequest {
|
pub struct RouteRequest {
|
||||||
pub rt: String,
|
pub rt: String,
|
||||||
pub key: String,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user