initial commit
This commit is contained in:
commit
9f9900ca6b
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/target
|
1603
Cargo.lock
generated
Normal file
1603
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
13
Cargo.toml
Normal file
13
Cargo.toml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
[package]
|
||||||
|
name = "cta-api"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
description = "CTA API Client"
|
||||||
|
authors = ["Joey Hines joey@ahines.net"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
reqwest = { version = "0.12.15", features = ["json"] }
|
||||||
|
serde = { version = "1.0.219", features = ["derive"] }
|
||||||
|
serde_json = "1.0.140"
|
||||||
|
thiserror = "2.0.12"
|
||||||
|
url = "2.5.4"
|
54
src/lib.rs
Normal file
54
src/lib.rs
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
use crate::models::Response;
|
||||||
|
use crate::models::route::{RouteRequest, RouteResp};
|
||||||
|
use reqwest::{Client, Url};
|
||||||
|
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),
|
||||||
|
}
|
||||||
|
|
||||||
|
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 fetch_route(&mut self, route: &str) -> Result<RouteResp, Error> {
|
||||||
|
let req = RouteRequest {
|
||||||
|
rt: route.to_string(),
|
||||||
|
key: self.key.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let url = self.url.join("ttpositions.aspx")?;
|
||||||
|
let resp: Response<RouteResp> = self
|
||||||
|
.client
|
||||||
|
.get(url)
|
||||||
|
.query(&req)
|
||||||
|
.query(&[("outputType", "JSON")])
|
||||||
|
.send()
|
||||||
|
.await?
|
||||||
|
.json()
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(resp.ctatt)
|
||||||
|
}
|
||||||
|
}
|
18
src/models/mod.rs
Normal file
18
src/models/mod.rs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
pub mod route;
|
||||||
|
pub mod train;
|
||||||
|
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct Ctatt {
|
||||||
|
pub tmst: String,
|
||||||
|
#[serde(rename = "errCd")]
|
||||||
|
pub err_cd: String,
|
||||||
|
#[serde(rename = "errNm")]
|
||||||
|
pub err_nm: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct Response<T> {
|
||||||
|
pub ctatt: T,
|
||||||
|
}
|
23
src/models/route.rs
Normal file
23
src/models/route.rs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
use crate::models::Ctatt;
|
||||||
|
use crate::models::train::Train;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Serialize, Debug)]
|
||||||
|
pub struct RouteRequest {
|
||||||
|
pub rt: String,
|
||||||
|
pub key: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
pub struct Route {
|
||||||
|
#[serde(rename = "@name")]
|
||||||
|
pub name: String,
|
||||||
|
pub train: Vec<Train>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
pub struct RouteResp {
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub header: Ctatt,
|
||||||
|
pub route: Vec<Route>,
|
||||||
|
}
|
29
src/models/train.rs
Normal file
29
src/models/train.rs
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct Train {
|
||||||
|
pub rn: String,
|
||||||
|
#[serde(rename = "destSt")]
|
||||||
|
pub dest_st: String,
|
||||||
|
#[serde(rename = "destNm")]
|
||||||
|
pub dest_nm: String,
|
||||||
|
#[serde(rename = "trDr")]
|
||||||
|
pub tr_dr: String,
|
||||||
|
#[serde(rename = "nextStaId")]
|
||||||
|
pub next_sta_id: String,
|
||||||
|
#[serde(rename = "nextStpId")]
|
||||||
|
pub next_stp_id: String,
|
||||||
|
#[serde(rename = "nextStaNm")]
|
||||||
|
pub next_sta_nm: String,
|
||||||
|
pub prdt: String,
|
||||||
|
#[serde(rename = "arrT")]
|
||||||
|
pub arrt: String,
|
||||||
|
#[serde(rename = "isApp")]
|
||||||
|
pub is_app: String,
|
||||||
|
#[serde(rename = "isDly")]
|
||||||
|
pub is_dly: String,
|
||||||
|
pub flags: Option<String>,
|
||||||
|
pub lat: String,
|
||||||
|
pub lon: String,
|
||||||
|
pub heading: String,
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user