cta-api/src/lib.rs

121 lines
3.0 KiB
Rust

use crate::models::eta::{EtaRequest, EtaResp};
use crate::models::follow::{FollowRequest, FollowResp};
use crate::models::route::{RouteRequest, RouteResp};
use crate::models::{CTAResponse, Response};
use reqwest::{Client, Url};
use serde::{Deserialize, Serialize};
use thiserror::Error;
pub mod models;
const CTA_API_URL: &str = "http://lapi.transitchicago.com/api/1.0/";
#[derive(Error, Debug)]
pub enum Error {
#[error("Failed to deserialize response from API: {0}")]
JsonParseFailure(#[from] serde_json::Error),
#[error("Reqwest failure: {0}")]
ReqwestFailure(#[from] reqwest::Error),
#[error("URL parse failure: {0}")]
UrlParseError(#[from] url::ParseError),
#[error("Got error number '{0}': {1}")]
CtaError(String, String),
}
pub struct CTAClient {
key: String,
client: Client,
url: Url,
}
impl CTAClient {
pub fn new(key: String) -> Self {
Self {
key,
client: Default::default(),
url: Url::parse(CTA_API_URL).unwrap(),
}
}
pub async fn make_request<T: Serialize, V: for<'a> Deserialize<'a> + CTAResponse>(
&mut self,
endpoint: &str,
req: T,
) -> Result<V, Error> {
let url = self.url.join(endpoint)?;
let resp: Response<V> = self
.client
.get(url)
.query(&req)
.query(&[("outputType", "JSON")])
.query(&[("key", &self.key)])
.send()
.await?
.json()
.await?;
resp.ctatt.check_error()?;
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();
}
}