plane 0.5.5

Session backend orchestrator for ambitious browser-based apps.
Documentation
use axum::{
    http::StatusCode,
    response::{IntoResponse, Response},
    Json,
};
use plane_common::{
    protocol::{ApiError, ApiErrorKind},
    util::random_string,
};
use std::{error::Error, fmt::Debug};

pub fn err_to_response<E: Debug>(
    error: E,
    status: StatusCode,
    user_message: &str,
    code: ApiErrorKind,
) -> Response {
    let err_id = random_string();

    if status.is_server_error() {
        tracing::error!(
            ?err_id,
            ?error,
            "Server error encountered while handling request."
        );
    } else {
        tracing::warn!(
            ?err_id,
            ?error,
            "Client error encountered while handling request."
        );
    }

    let result = ApiError {
        id: err_id.clone(),
        message: user_message.to_string(),
        kind: code,
    };

    (status, Json(result)).into_response()
}

pub trait IntoApiError<T>: Sized {
    fn or_not_found(self, user_message: &str) -> Result<T, Response> {
        self.or_status(StatusCode::NOT_FOUND, user_message, ApiErrorKind::NotFound)
    }

    fn or_internal_error(self, user_message: &str) -> Result<T, Response> {
        self.or_status(
            StatusCode::INTERNAL_SERVER_ERROR,
            user_message,
            ApiErrorKind::Other,
        )
    }

    fn or_status(
        self,
        status: StatusCode,
        user_message: &str,
        code: ApiErrorKind,
    ) -> Result<T, Response>;
}

impl<T, E: Error> IntoApiError<T> for Result<T, E> {
    fn or_status(
        self,
        status: StatusCode,
        user_message: &str,
        code: ApiErrorKind,
    ) -> Result<T, Response> {
        match self {
            Ok(v) => Ok(v),
            Err(error) => {
                let err = err_to_response(&error, status, user_message, code);
                Err(err)
            }
        }
    }
}

impl<T> IntoApiError<T> for Option<T> {
    fn or_status(
        self,
        status: StatusCode,
        user_message: &str,
        code: ApiErrorKind,
    ) -> Result<T, Response> {
        match self {
            Some(v) => Ok(v),
            None => {
                let err = err_to_response("Missing value.", status, user_message, code);
                Err(err)
            }
        }
    }
}