use chrono::DateTime; use url::Url; use urlencoding::encode; use crate::error::train_order_api_error::ResolveTripNumberError; use crate::model::db_vendo_navigator_api::TrainOrdering; use crate::model::travelynx::TrainType; pub async fn get_railcar_identifier_by_journey( train_type: TrainType, trip_number: usize, station_uic: usize, departure_time: usize ) -> Result { let train_ordering = query_train_order_api(train_type, trip_number, station_uic, departure_time).await?; println!("Received train ordering response {:?}", train_ordering); find_railcar_identifier(train_ordering) } fn find_railcar_identifier(train_ordering: TrainOrdering) -> Result { //TODO: refactor - trainsets can consist out of several trains let trainset = train_ordering.train_sets.first() .ok_or(ResolveTripNumberError::Api("No items in field 'fahrzeuggruppe'".to_string()))?; let identifier_str = trainset.identifier.to_owned(); let train_type = &trainset.journey.train_type; let identifier = crop_first_n_chars(identifier_str.as_str(), train_type.to_string().len()) .to_string().parse::()?; Ok(identifier) } async fn query_train_order_api( train_type: TrainType, trip_number: usize, station_uic: usize, departure_time: usize ) -> reqwest::Result { println!("Resolving trip {train_type} {trip_number} from {station_uic} at {departure_time}"); let client = reqwest::ClientBuilder::new() .build()?; let api_url = build_api_url(train_type, trip_number, station_uic, departure_time); println!("Fetching {api_url}"); client.get(api_url) .header("Accept", "application/x.db.vendo.mob.wagenreihung.v3+json") .header("X-Correlation-ID", "ABCDE") .send() .await? .json::() .await } fn build_api_url( train_type: TrainType, trip_number: usize, station_uic: usize, departure_time: usize ) -> Url { let train_trip = format!("{}_{}", train_type.to_string(), trip_number); let departure = DateTime::from_timestamp(departure_time as i64, 0) .expect("invalid departure time"); Url::parse(format!( "https://app.vendo.noncd.db.de/mob/zuglaeufe/{}/halte/by-abfahrt/{}_{}/wagenreihung", train_trip, station_uic, encode(departure.to_rfc3339().as_str()) ).as_str()).unwrap() } fn crop_first_n_chars(s: &str, count: usize) -> &str { match s.char_indices().skip(count).next() { Some((pos, _)) => &s[pos..], None => "" } }