use rocket::{get, post, routes, serde::json::Json, State}; pub struct ServerState { primary_name: String, map: std::collections::HashMap< String, std::sync::Arc<tokio::sync::RwLock<crate::controller::CommonData>>, >, } impl ServerState { pub fn new( primary_name: &impl ToString, map: std::collections::HashMap< String, std::sync::Arc<tokio::sync::RwLock<crate::controller::CommonData>>, >, ) -> Self { let primary_name = primary_name.to_string(); Self { primary_name, map } } } pub fn rocket(state: ServerState) -> rocket::Rocket<rocket::Build> { rocket::build().manage(state).mount( "/", routes![ metrics, interfaces, all_interfaces, primary_interface, interface, enable_control, disable_control ], ) } #[get("/interfaces")] fn interfaces(state: &State<ServerState>) -> Json<Vec<String>> { Json(state.map.keys().cloned().collect()) } #[get("/interfaces/primary")] async fn primary_interface( state: &State<ServerState>, ) -> Result<Json<crate::controller::CommonData>, ServerError> { let s = state .map .get(&state.primary_name) .ok_or(ServerError::InvalidPrimaryName)? .read() .await .clone(); Ok(Json(s)) } #[get("/interfaces/data")] async fn all_interfaces( state: &State<ServerState>, ) -> Json<Vec<(String, crate::controller::CommonData)>> { let mut data = Vec::new(); for (k, v) in &state.map { data.push((k.clone(), v.read().await.clone())); } Json(data) } #[get("/interface/<name>")] async fn interface( name: &str, state: &State<ServerState>, ) -> Result<Json<crate::controller::CommonData>, ServerError> { let data = state .map .get(name) .ok_or(ServerError::NotFound)? .read() .await .clone(); Ok(Json(data)) } #[get("/metrics")] fn metrics() -> Result<String, ServerError> { Ok( prometheus::TextEncoder::new() .encode_to_string(&prometheus::default_registry().gather())?, ) } #[post("/enable-control")] async fn enable_control() { log::warn!("enabling control"); crate::config::write_to_config() .await .enable_secondary_control = true; } #[post("/disable-control")] async fn disable_control() { log::warn!("disabling control"); crate::config::write_to_config() .await .enable_secondary_control = false; } enum ServerError { Prometheus, NotFound, InvalidPrimaryName, } impl From<prometheus::Error> for ServerError { fn from(_: prometheus::Error) -> Self { Self::Prometheus } } impl<'a> rocket::response::Responder<'a, 'a> for ServerError { fn respond_to(self, _: &'a rocket::Request<'_>) -> rocket::response::Result<'a> { Err(match self { Self::Prometheus => rocket::http::Status::InternalServerError, Self::NotFound => rocket::http::Status::NotFound, Self::InvalidPrimaryName => rocket::http::Status::ServiceUnavailable, }) } }