use axum::{Json, extract::State, http::StatusCode};
use std::sync::atomic::Ordering;
use tokio::sync::oneshot;
use tokio::time::{Duration, timeout};
use crate::{
models::{ApiResponse, DeviceInfo},
state::AppState,
};
const INFO_HTTP_TIMEOUT: Duration = Duration::from_millis(3000);
#[derive(serde::Serialize)]
#[serde(untagged)]
pub enum InfoResult {
Ok(DeviceInfo),
Err(ApiResponse),
}
pub async fn get_info(State(state): State<AppState>) -> (StatusCode, Json<InfoResult>) {
if !state.serial_connected.load(Ordering::SeqCst) {
return (
StatusCode::SERVICE_UNAVAILABLE,
Json(InfoResult::Err(ApiResponse {
success: false,
message: "ESP32 disconnected; serial command unavailable".to_string(),
})),
);
}
if state.collection_running.load(Ordering::SeqCst) {
return (
StatusCode::SERVICE_UNAVAILABLE,
Json(InfoResult::Err(ApiResponse {
success: false,
message: "Collection is running; stop it first to query info".to_string(),
})),
);
}
let (resp_tx, resp_rx) = oneshot::channel();
if state.info_request_tx.send(resp_tx).await.is_err() {
return (
StatusCode::SERVICE_UNAVAILABLE,
Json(InfoResult::Err(ApiResponse {
success: false,
message: "Serial task is shutting down".to_string(),
})),
);
}
match timeout(INFO_HTTP_TIMEOUT, resp_rx).await {
Ok(Ok(Ok(info))) => (StatusCode::OK, Json(InfoResult::Ok(info))),
Ok(Ok(Err(message))) => {
let status = if message.contains("timed out")
|| message.contains("not esp-csi-cli-rs")
|| message.contains("magic prefix")
{
StatusCode::GATEWAY_TIMEOUT
} else {
StatusCode::BAD_GATEWAY
};
(
status,
Json(InfoResult::Err(ApiResponse {
success: false,
message,
})),
)
}
Ok(Err(_)) | Err(_) => (
StatusCode::GATEWAY_TIMEOUT,
Json(InfoResult::Err(ApiResponse {
success: false,
message: "info request timed out".to_string(),
})),
),
}
}