use std::io::Error as IoError;
use reqwest::{Client, Error as ReqwestError, Response};
use serde::de::DeserializeOwned;
use serde_json::Error as JsonError;
use shardline_server::HealthResponse;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum CliRuntimeError {
#[error("standard io failed")]
Io(#[from] IoError),
#[error("http request failed")]
Http(#[from] ReqwestError),
#[error("json operation failed")]
Json(#[from] JsonError),
#[error("server returned http status {status}: {body}")]
ServerStatus {
status: u16,
body: String,
},
#[error("server health status was {status}")]
Unhealthy {
status: String,
},
}
pub async fn run_health_check(server_url: &str) -> Result<(), CliRuntimeError> {
let client = Client::new();
let response = client.get(endpoint(server_url, "healthz")).send().await?;
let health = read_json_response::<HealthResponse>(response).await?;
if health.status != "ok" {
return Err(CliRuntimeError::Unhealthy {
status: health.status,
});
}
Ok(())
}
fn endpoint(server_url: &str, path: &str) -> String {
format!(
"{}/{}",
server_url.trim_end_matches('/'),
path.trim_start_matches('/')
)
}
async fn read_json_response<T>(response: Response) -> Result<T, CliRuntimeError>
where
T: DeserializeOwned,
{
let status = response.status();
if !status.is_success() {
let body = response.text().await?;
return Err(CliRuntimeError::ServerStatus {
status: status.as_u16(),
body,
});
}
Ok(response.json::<T>().await?)
}
#[cfg(test)]
mod tests {
use super::endpoint;
#[test]
fn endpoint_joins_base_url_and_path() {
assert_eq!(
endpoint("http://127.0.0.1:8080/", "/v1/stats"),
"http://127.0.0.1:8080/v1/stats"
);
}
}