use std::fmt::{Debug, Display};
use rustolio_utils::{http::StatusCode, prelude::*};
#[derive(Debug, Encode, Decode)]
pub enum NoCustomError {}
impl std::fmt::Display for NoCustomError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "self:?")
}
}
impl std::error::Error for NoCustomError {}
#[derive(Debug, Encode, Decode)]
pub enum ServerFnError<E = NoCustomError> {
RpcError(E),
NetworkError(String),
#[encode(skip)]
HttpError(StatusCode, String),
SerializationError(String),
DeserializationError(String),
}
impl<E> ServerFnError<E> {
pub fn http_error(
status_code: impl TryInto<StatusCode, Error: Debug>,
msg: impl Display,
) -> Self {
ServerFnError::HttpError(
status_code.try_into().expect("Could not parse StatusCode"),
msg.to_string(),
)
}
}
impl<E> ServerFnError<E> {
pub fn from(value: ServerFnError<NoCustomError>) -> Self {
match value {
ServerFnError::RpcError(_) => {
unreachable!("NoCustomError cannot be constructed")
}
ServerFnError::NetworkError(msg) => ServerFnError::NetworkError(msg),
ServerFnError::HttpError(status, msg) => ServerFnError::HttpError(status, msg),
ServerFnError::SerializationError(msg) => ServerFnError::SerializationError(msg),
ServerFnError::DeserializationError(msg) => ServerFnError::DeserializationError(msg),
}
}
}
impl ServerFnError<NoCustomError> {
pub fn into<E>(self) -> ServerFnError<E> {
ServerFnError::from(self)
}
}
impl<E> From<E> for ServerFnError<E> {
fn from(value: E) -> Self {
ServerFnError::RpcError(value)
}
}
pub trait IntoServerResult<T, E> {
fn server_result(self) -> std::result::Result<T, ServerFnError<E>>;
}
impl<T, E, C> IntoServerResult<T, C> for std::result::Result<T, E>
where
E: Into<C>,
{
fn server_result(self) -> std::result::Result<T, ServerFnError<C>> {
match self {
Ok(v) => Ok(v),
Err(e) => Err(ServerFnError::RpcError(e.into())),
}
}
}
impl<E> std::fmt::Display for ServerFnError<E> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "self:?")
}
}
impl<E> std::error::Error for ServerFnError<E> where E: std::error::Error {}
#[cfg(test)]
mod tests {
use super::*;
#[allow(unused)]
struct MyCustomError;
impl Into<ServerFnError<String>> for MyCustomError {
fn into(self) -> ServerFnError<String> {
ServerFnError::HttpError(StatusCode::BAD_REQUEST, "MyCustomError".to_string())
}
}
fn _test_server_fn_error_from_conversion() -> std::result::Result<(), ServerFnError<String>> {
Err("Custom error".to_string())?;
Err(ServerFnError::NetworkError("Network issue".to_string()))?;
if let Err(e) = Err::<(), _>(MyCustomError) {
return Err(e.into());
}
if let Err(e) = Err::<(), _>(String::from("Custom error")) {
return Err(e.into());
}
if let Err(e) =
Err::<(), ServerFnError>(ServerFnError::NetworkError("Network issue".to_string()))
{
return Err(e.into());
}
Ok(())
}
fn _rpc_endpoint() -> std::result::Result<String, ServerFnError<String>> {
let _ = Ok::<_, String>(String::from("Ok"))?;
let _ = Err(String::from("Err"))?;
let _ = Err("Err").server_result()?;
unreachable!()
}
}