use axum::{ routing::get, Router, }; use serde::{ Deserialize, Serialize, }; #[derive(Serialize, Deserialize)] struct TrafficDataThresholds { level: i64, threshold: i64, color: String, description: String, } #[derive(Serialize, Deserialize)] struct TrafficDataItem { id: String, level: i64, color: Option, description: Option, counter: Option, automatic: bool, thresholds: Vec, } #[tokio::main] async fn main() { let app = Router::new() .route("/", get(route_index)) .route("/metrics", get(route_metrics)); let listener = tokio::net::TcpListener::bind("[::]:3000").await.unwrap(); println!("Server listening on: http://{}", listener.local_addr().unwrap()); axum::serve(listener, app).await.unwrap(); } async fn route_index() -> String { return String::from("Prometheus exporter for Berlinerbaeder occupancy"); } async fn route_metrics() -> Result { let client = reqwest::Client::new(); let trafficdata_req = client.get(String::from("https://www.berlinerbaeder.de/traffic/trafficdata.json")) .send().await .map_err(|_err| String::from("trafficdata API cannot be fetched"))?; if trafficdata_req.status() != reqwest::StatusCode::OK { return Err(String::from("Unexpected response from trafficdata API")); } let trafficdata_body = trafficdata_req.text().await .map_err(|_err| "Cannot read body of trafficdata API")?; let trafficdata: Vec = serde_json::from_str(trafficdata_body.as_str()) .map_err(|err| format!("{}", err))?; let mut out = String::new(); for bad in trafficdata { out.push_str(&format!("berlinerbaeder_occupation{{bad=\"{}\"}} {}\n", bad.id, bad.counter.unwrap_or(-1))); } return Ok(out); }