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