From 353eb865db02540ed06ab49205132780566d9b9f Mon Sep 17 00:00:00 2001 From: Joey Hines Date: Fri, 23 May 2025 17:32:13 -0600 Subject: [PATCH] Handle errors from the API more gracefully --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/lib.rs | 8 ++++++-- src/models/eta.rs | 9 ++++++++- src/models/follow.rs | 12 ++++++++++-- src/models/mod.rs | 18 ++++++++++++++++-- src/models/route.rs | 9 ++++++++- 7 files changed, 50 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1f637bb..faad84a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -101,7 +101,7 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cta-api" -version = "0.2.0" +version = "0.3.0" dependencies = [ "reqwest", "serde", diff --git a/Cargo.toml b/Cargo.toml index 2e7249d..bd6702e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cta-api" -version = "0.2.0" +version = "0.3.0" edition = "2024" description = "CTA API Client" authors = ["Joey Hines joey@ahines.net"] diff --git a/src/lib.rs b/src/lib.rs index 0618fcf..7ad8285 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,7 @@ -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::{CTAResponse, Response}; use reqwest::{Client, Url}; use serde::{Deserialize, Serialize}; use thiserror::Error; @@ -18,6 +18,8 @@ pub enum Error { 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 { @@ -35,7 +37,7 @@ impl CTAClient { } } - pub async fn make_request Deserialize<'a>>( + pub async fn make_request Deserialize<'a> + CTAResponse>( &mut self, endpoint: &str, req: T, @@ -53,6 +55,8 @@ impl CTAClient { .json() .await?; + resp.ctatt.check_error()?; + Ok(resp.ctatt) } diff --git a/src/models/eta.rs b/src/models/eta.rs index b2d45a6..13e2abc 100644 --- a/src/models/eta.rs +++ b/src/models/eta.rs @@ -1,4 +1,4 @@ -use crate::models::Ctatt; +use crate::models::{CTAResponse, Ctatt}; use serde::{Deserialize, Serialize}; #[derive(Serialize, Debug)] @@ -13,9 +13,16 @@ pub struct EtaRequest { pub struct EtaResp { #[serde(flatten)] pub header: Ctatt, + #[serde(default)] pub eta: Vec, } +impl CTAResponse for EtaResp { + fn get_header(&self) -> Ctatt { + self.header.clone() + } +} + #[derive(Deserialize, Debug)] pub struct Eta { #[serde(rename = "staId")] diff --git a/src/models/follow.rs b/src/models/follow.rs index 6ccb696..beb2e99 100644 --- a/src/models/follow.rs +++ b/src/models/follow.rs @@ -1,4 +1,4 @@ -use crate::models::Ctatt; +use crate::models::{CTAResponse, Ctatt}; use serde::{Deserialize, Serialize}; #[derive(Serialize, Debug)] @@ -7,7 +7,7 @@ pub struct FollowRequest { pub run_number: u32, } -#[derive(Deserialize, Debug)] +#[derive(Deserialize, Debug, Default)] pub struct Position { pub lat: String, pub lon: String, @@ -48,6 +48,14 @@ pub struct ScheduledStop { pub struct FollowResp { #[serde(flatten)] pub header: Ctatt, + #[serde(default)] pub position: Position, + #[serde(default)] pub eta: Vec, } + +impl CTAResponse for FollowResp { + fn get_header(&self) -> Ctatt { + self.header.clone() + } +} diff --git a/src/models/mod.rs b/src/models/mod.rs index ebf10b2..e8e81a1 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -3,9 +3,10 @@ pub mod follow; pub mod route; pub mod train; +use crate::Error; use serde::Deserialize; -#[derive(Debug, Deserialize)] +#[derive(Debug, Deserialize, Clone)] pub struct Ctatt { pub tmst: String, #[serde(rename = "errCd")] @@ -14,7 +15,20 @@ pub struct Ctatt { pub err_nm: Option, } +pub trait CTAResponse { + fn get_header(&self) -> Ctatt; + fn check_error(&self) -> Result<(), Error> { + let header = self.get_header(); + + if let Some(err_number) = header.err_nm { + Err(Error::CtaError(err_number, header.err_cd)) + } else { + Ok(()) + } + } +} + #[derive(Debug, Deserialize)] -pub struct Response { +pub struct Response { pub ctatt: T, } diff --git a/src/models/route.rs b/src/models/route.rs index 3faa38e..c401ceb 100644 --- a/src/models/route.rs +++ b/src/models/route.rs @@ -1,5 +1,5 @@ -use crate::models::Ctatt; use crate::models::train::Train; +use crate::models::{CTAResponse, Ctatt}; use serde::{Deserialize, Serialize}; #[derive(Serialize, Debug)] @@ -18,5 +18,12 @@ pub struct Route { pub struct RouteResp { #[serde(flatten)] pub header: Ctatt, + #[serde(default)] pub route: Vec, } + +impl CTAResponse for RouteResp { + fn get_header(&self) -> Ctatt { + self.header.clone() + } +}