use anyhow::anyhow;
use http::StatusCode;
use std::fmt::Display;
pub trait ContextWrapper<T, E> {
fn anyhow(self) -> Result<T, anyhow::Error>;
fn anyhow_with(self, msg: impl AsRef<str>) -> Result<T, anyhow::Error>;
fn track(self) -> Self;
fn track_with(self, msg: impl AsRef<str>) -> Self;
}
pub trait ErrToMsg {
fn to_msg(self, loc: &std::panic::Location) -> String;
fn to_msg_with(self, msg: impl AsRef<str>, loc: &std::panic::Location) -> String;
}
impl<E: Display> ErrToMsg for E {
#[track_caller]
fn to_msg(self, loc: &std::panic::Location) -> String {
let e = self.to_string();
if e.is_empty() {
format!("\n- [{}:{}]", loc.file(), loc.line())
} else {
format!("\n- [{}:{}] {e}", loc.file(), loc.line())
}
}
#[track_caller]
fn to_msg_with(self, msg: impl AsRef<str>, loc: &std::panic::Location) -> String {
let e = self.to_string();
if e.is_empty() {
format!("\n- [{}:{}] {}", loc.file(), loc.line(), msg.as_ref())
} else {
format!("\n- [{}:{}] {} {e}", loc.file(), loc.line(), msg.as_ref())
}
}
}
impl<T, E: Display> ContextWrapper<T, E> for Result<T, E> {
#[track_caller]
fn anyhow(self) -> Result<T, anyhow::Error> {
match self {
Ok(v) => Ok(v),
Err(e) => Err(anyhow!(e.to_msg(std::panic::Location::caller()))),
}
}
#[track_caller]
fn anyhow_with(self, msg: impl AsRef<str>) -> Result<T, anyhow::Error> {
match self {
Ok(v) => Ok(v),
Err(e) => Err(anyhow!(e.to_msg_with(msg, std::panic::Location::caller()))),
}
}
#[track_caller]
fn track(self) -> Self {
if let Err(e) = &self {
tracing::error!(target: "dutils", "{}", e.to_msg(std::panic::Location::caller()));
}
self
}
#[track_caller]
fn track_with(self, msg: impl AsRef<str>) -> Self {
if let Err(e) = &self {
tracing::error!(target: "dutils", "{}", e.to_msg_with(msg, std::panic::Location::caller()));
}
self
}
}
impl<T> ContextWrapper<T, ()> for Option<T> {
#[track_caller]
fn anyhow(self) -> Result<T, anyhow::Error> {
match self {
Some(v) => Ok(v),
None => Err(anyhow!(
"Value is None".to_msg(std::panic::Location::caller())
)),
}
}
#[track_caller]
fn anyhow_with(self, msg: impl AsRef<str>) -> Result<T, anyhow::Error> {
match self {
Some(v) => Ok(v),
None => Err(anyhow!(
"Value is None".to_msg_with(msg, std::panic::Location::caller())
)),
}
}
#[track_caller]
fn track(self) -> Self {
if self.is_none() {
tracing::error!(target: "dutils", "{}", "Value is None".to_msg(std::panic::Location::caller()));
}
self
}
#[track_caller]
fn track_with(self, msg: impl AsRef<str>) -> Self {
if self.is_none() {
tracing::error!(target: "dutils", "{}", "Value is None".to_msg_with(msg, std::panic::Location::caller()));
}
self
}
}
fn api_err_response(status: StatusCode, msg: impl AsRef<str>) -> http::Response<String> {
http::Response::builder()
.status(status)
.body(serde_json::to_string(msg.as_ref()).unwrap())
.unwrap()
}
pub trait ApiError {
type Ok;
fn bad_request_from_error(self) -> core::result::Result<Self::Ok, http::Response<String>>;
fn bad_request(
self,
msg: impl AsRef<str>,
) -> core::result::Result<Self::Ok, http::Response<String>>;
fn not_found(
self,
msg: impl AsRef<str>,
) -> core::result::Result<Self::Ok, http::Response<String>>;
fn internal(
self,
msg: impl AsRef<str>,
) -> core::result::Result<Self::Ok, http::Response<String>>;
fn internal_from_error(self) -> core::result::Result<Self::Ok, http::Response<String>>;
}
pub trait ApiErrorConflict {
type Ok;
fn conflict(
self,
msg: impl AsRef<str>,
) -> core::result::Result<Self::Ok, http::Response<String>>;
}
impl ApiError for String {
type Ok = ();
fn bad_request_from_error(self) -> core::result::Result<Self::Ok, http::Response<String>> {
Err(api_err_response(StatusCode::BAD_REQUEST, self))
}
fn bad_request(
self,
msg: impl AsRef<str>,
) -> core::result::Result<Self::Ok, http::Response<String>> {
Err(api_err_response(StatusCode::BAD_REQUEST, msg))
}
fn not_found(
self,
msg: impl AsRef<str>,
) -> core::result::Result<Self::Ok, http::Response<String>> {
Err(api_err_response(StatusCode::NOT_FOUND, msg))
}
fn internal(
self,
msg: impl AsRef<str>,
) -> core::result::Result<Self::Ok, http::Response<String>> {
Err(api_err_response(StatusCode::INTERNAL_SERVER_ERROR, msg))
}
fn internal_from_error(self) -> core::result::Result<Self::Ok, http::Response<String>> {
Err(api_err_response(StatusCode::INTERNAL_SERVER_ERROR, self))
}
}
impl ApiError for &'_ str {
type Ok = ();
fn bad_request_from_error(self) -> core::result::Result<Self::Ok, http::Response<String>> {
Err(api_err_response(StatusCode::BAD_REQUEST, self))
}
fn bad_request(
self,
msg: impl AsRef<str>,
) -> core::result::Result<Self::Ok, http::Response<String>> {
Err(api_err_response(StatusCode::BAD_REQUEST, msg))
}
fn not_found(
self,
msg: impl AsRef<str>,
) -> core::result::Result<Self::Ok, http::Response<String>> {
Err(api_err_response(StatusCode::NOT_FOUND, msg))
}
fn internal(
self,
msg: impl AsRef<str>,
) -> core::result::Result<Self::Ok, http::Response<String>> {
Err(api_err_response(StatusCode::INTERNAL_SERVER_ERROR, msg))
}
fn internal_from_error(self) -> core::result::Result<Self::Ok, http::Response<String>> {
Err(api_err_response(StatusCode::INTERNAL_SERVER_ERROR, self))
}
}
impl<T, E: Display> ApiError for core::result::Result<T, E> {
type Ok = T;
fn bad_request_from_error(self) -> core::result::Result<T, http::Response<String>> {
match self {
Ok(a) => Ok(a),
Err(e) => Err(api_err_response(StatusCode::BAD_REQUEST, e.to_string())),
}
}
fn bad_request(self, msg: impl AsRef<str>) -> core::result::Result<T, http::Response<String>> {
match self {
Ok(a) => Ok(a),
Err(_) => Err(api_err_response(StatusCode::BAD_REQUEST, msg)),
}
}
fn not_found(self, msg: impl AsRef<str>) -> core::result::Result<T, http::Response<String>> {
match self {
Ok(a) => Ok(a),
Err(_) => Err(api_err_response(StatusCode::NOT_FOUND, msg)),
}
}
fn internal(self, msg: impl AsRef<str>) -> core::result::Result<T, http::Response<String>> {
match self {
Ok(a) => Ok(a),
Err(_) => Err(api_err_response(StatusCode::INTERNAL_SERVER_ERROR, msg)),
}
}
fn internal_from_error(self) -> core::result::Result<T, http::Response<String>> {
match self {
Ok(a) => Ok(a),
Err(e) => Err(api_err_response(
StatusCode::INTERNAL_SERVER_ERROR,
e.to_string(),
)),
}
}
}
impl<T> ApiError for Option<T> {
type Ok = T;
fn bad_request_from_error(self) -> core::result::Result<T, http::Response<String>> {
match self {
Some(a) => Ok(a),
None => Err(api_err_response(StatusCode::BAD_REQUEST, "No value")),
}
}
fn bad_request(self, msg: impl AsRef<str>) -> core::result::Result<T, http::Response<String>> {
match self {
Some(a) => Ok(a),
None => Err(api_err_response(StatusCode::BAD_REQUEST, msg)),
}
}
fn not_found(self, msg: impl AsRef<str>) -> core::result::Result<T, http::Response<String>> {
match self {
Some(a) => Ok(a),
None => Err(api_err_response(StatusCode::NOT_FOUND, msg)),
}
}
fn internal(self, msg: impl AsRef<str>) -> core::result::Result<T, http::Response<String>> {
match self {
Some(a) => Ok(a),
None => Err(api_err_response(StatusCode::INTERNAL_SERVER_ERROR, msg)),
}
}
fn internal_from_error(self) -> core::result::Result<T, http::Response<String>> {
match self {
Some(a) => Ok(a),
None => Err(api_err_response(
StatusCode::INTERNAL_SERVER_ERROR,
"No value",
)),
}
}
}
impl<T> ApiErrorConflict for Option<T> {
type Ok = ();
fn conflict(self, msg: impl AsRef<str>) -> core::result::Result<(), http::Response<String>> {
match self {
None => Ok(()),
Some(_) => Err(api_err_response(StatusCode::CONFLICT, msg)),
}
}
}
pub trait ToResult<T> {
fn to_result(self) -> Result<T, anyhow::Error>;
}
impl<T> ToResult<T> for T {
fn to_result(self) -> Result<T, anyhow::Error> {
Ok(self)
}
}