use crate::error::ServerError;
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
pub async fn run_blocking<F, T>(operation: F) -> Result<T, ServerError>
where
F: FnOnce() -> Result<T, ServerError> + Send + 'static,
T: Send + 'static,
{
match tokio::task::spawn_blocking(operation).await {
Ok(result) => result,
Err(err) => Err(ServerError::TaskFailed(format!(
"blocking task failed: {err}"
))),
}
}
#[cfg(not(feature = "async"))]
pub fn run_blocking<F, T>(operation: F) -> Result<T, ServerError>
where
F: FnOnce() -> Result<T, ServerError>,
{
operation()
}
#[cfg(all(test, feature = "async"))]
mod tests {
use super::*;
#[tokio::test]
async fn run_blocking_maps_panic_to_task_failed() {
let result = run_blocking(|| -> Result<(), ServerError> {
panic!("boom")
})
.await;
let err =
result.expect_err("blocking panic must surface as Err");
let is_task_failed = matches!(err, ServerError::TaskFailed(_));
assert!(is_task_failed, "unexpected variant: {err:?}");
}
#[tokio::test]
async fn run_blocking_returns_inner_error() {
let result = run_blocking(|| -> Result<(), ServerError> {
Err(ServerError::Custom("inner".to_string()))
})
.await;
let err = result.expect_err("inner Err must propagate");
let is_custom = matches!(err, ServerError::Custom(_));
assert!(is_custom, "unexpected variant: {err:?}");
}
#[tokio::test]
async fn run_blocking_returns_success_value() {
let result =
run_blocking(|| -> Result<usize, ServerError> { Ok(7) })
.await
.expect("ok");
assert_eq!(result, 7);
}
}