Skip to content

Commit

Permalink
Provide conversion utilities around Result<T, ServerFnError<E>>
Browse files Browse the repository at this point in the history
This adds 3 new traits:
- `ConvertServerFnResult` provides easy conversion from
  `Result<T, ServerFnError<SourceCustErr>>` to
  `Result<T, ServerFnError<TargetCustError>>` when `TargetCustError`
  implements `From<SourceCustErr>`

- `ConvertDefaultServerFnResult` provides easy conversion from
  `Result<T, ServerFnError<NoCustomError>` to
  `Result<T, ServerFnError<TargetCustError>>`

- `IntoServerFnResult` provides easy conversion from `Result<T, E>` to
  `Result<T, ServerFnError::ServerError>` when `E` implements
  `std::error::Error`

Fixes #3153 and #3155
  • Loading branch information
nicolas-guichard committed Nov 17, 2024
1 parent 3d4b681 commit a9ad221
Showing 1 changed file with 189 additions and 0 deletions.
189 changes: 189 additions & 0 deletions server_fn/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,195 @@ impl<E: std::error::Error> From<E> for ServerFnError {
}
}

/// Helper trait to convert from `Result<T, ServerFnError<FromCustErr>>` to `Result<T, ServerFnError<CustErr>>`
///
/// This is meant to ease the use of the `?` operator:
/// ```
/// use server_fn::{error::ConvertServerFnResult, ServerFnError};
///
/// enum Helper1Error {
/// ErrorCase1,
/// }
/// fn helper1() -> Result<u8, ServerFnError<Helper1Error>> {
/// Err(ServerFnError::WrappedServerError(Helper1Error::ErrorCase1))
/// }
///
/// enum Helper2Error {
/// ErrorCase2,
/// }
/// fn helper2() -> Result<u8, ServerFnError<Helper2Error>> {
/// Err(ServerFnError::WrappedServerError(Helper2Error::ErrorCase2))
/// }
///
/// enum FnError {
/// ErrorCase1,
/// ErrorCase2,
/// }
/// fn server_fn() -> Result<u8, ServerFnError<FnError>> {
/// Ok(helper1().convert_custom_error()?
/// + helper2().convert_custom_error()?)
/// }
///
/// impl From<Helper1Error> for FnError {
/// fn from(e: Helper1Error) -> Self {
/// match e {
/// Helper1Error::ErrorCase1 => Self::ErrorCase1,
/// }
/// }
/// }
///
/// impl From<Helper2Error> for FnError {
/// fn from(e: Helper2Error) -> Self {
/// match e {
/// Helper2Error::ErrorCase2 => Self::ErrorCase2,
/// }
/// }
/// }
/// ```
///
/// See also [`ConvertDefaultServerFnResult`] for conversion from the default [`ServerFnError<NoCustomError>`]
/// and [`ConvertServerFnResult`] for conversion between different custom error types.
pub trait ConvertServerFnResult<T, CustErr> {
/// Converts a `Result<T, ServerFnError<FromCustErr>>` into a `Result<T, ServerFnError<CustError>>`.
///
/// `FromCustErr` must implement `Into<CustErr>`.
///
/// See the [trait-level documentation](ConvertServerFnResult) for usage examples.
fn convert_custom_error(self) -> Result<T, ServerFnError<CustErr>>;
}

impl<FromCustErr, CustErr, T> ConvertServerFnResult<T, CustErr>
for Result<T, ServerFnError<FromCustErr>>
where
FromCustErr: Into<CustErr>,
{
fn convert_custom_error(self) -> Result<T, ServerFnError<CustErr>> {
self.map_err(|err| match err {
ServerFnError::<FromCustErr>::MissingArg(s) => {
ServerFnError::<CustErr>::MissingArg(s)
}
ServerFnError::<FromCustErr>::Args(s) => {
ServerFnError::<CustErr>::Args(s)
}
ServerFnError::<FromCustErr>::Serialization(s) => {
ServerFnError::<CustErr>::Serialization(s)
}
ServerFnError::<FromCustErr>::ServerError(s) => {
ServerFnError::<CustErr>::ServerError(s)
}
ServerFnError::<FromCustErr>::Response(s) => {
ServerFnError::<CustErr>::Response(s)
}
ServerFnError::<FromCustErr>::Registration(s) => {
ServerFnError::<CustErr>::Registration(s)
}
ServerFnError::<FromCustErr>::Request(s) => {
ServerFnError::<CustErr>::Request(s)
}
ServerFnError::<FromCustErr>::Deserialization(s) => {
ServerFnError::<CustErr>::Deserialization(s)
}
ServerFnError::<FromCustErr>::WrappedServerError(o) => {
ServerFnError::<CustErr>::WrappedServerError(o.into())
}
})
}
}

/// Helper trait to convert from `Result<T, ServerFnError>` to `Result<T, ServerFnError<CustErr>>`
///
/// This is meant to ease the use of the `?` operator:
/// ```
/// use server_fn::{error::ConvertDefaultServerFnResult, ServerFnError};
///
/// fn helper() -> Result<u8, ServerFnError> {
/// Err(ServerFnError::ServerError(String::from("Server error")))
/// }
///
/// enum FnError {
/// TypedError,
/// }
/// fn server_fn() -> Result<u8, ServerFnError<FnError>> {
/// Ok(helper().convert_custom_error()?)
/// }
/// ```
///
/// See also [`ConvertServerFnResult`] for conversion between different custom error types
/// and [`IntoServerFnResult`] for string-based conversion from any [`std::error::Error`].
pub trait ConvertDefaultServerFnResult<T, CustErr> {
/// Converts a `Result<T, ServerFnError>` into a `Result<T, ServerFnError<CustError>>`.
///
/// See the [trait-level documentation](ConvertDefaultServerFnResult) for usage examples.
fn convert_custom_error(self) -> Result<T, ServerFnError<CustErr>>;
}
impl<CustErr, T> ConvertDefaultServerFnResult<T, CustErr>
for Result<T, ServerFnError>
{
fn convert_custom_error(self) -> Result<T, ServerFnError<CustErr>> {
self.map_err(|err| match err {
ServerFnError::MissingArg(s) => {
ServerFnError::<CustErr>::MissingArg(s)
}
ServerFnError::Args(s) => ServerFnError::<CustErr>::Args(s),
ServerFnError::Serialization(s) => {
ServerFnError::<CustErr>::Serialization(s)
}
ServerFnError::ServerError(s) => {
ServerFnError::<CustErr>::ServerError(s)
}
ServerFnError::Response(s) => ServerFnError::<CustErr>::Response(s),
ServerFnError::Registration(s) => {
ServerFnError::<CustErr>::Registration(s)
}
ServerFnError::Request(s) => ServerFnError::<CustErr>::Request(s),
ServerFnError::Deserialization(s) => {
ServerFnError::<CustErr>::Deserialization(s)
}
})
}
}

/// Helper trait to convert from `Result<T, E>` to `Result<T, ServerFnError<CustErr>>`
///
/// This is meant to ease the use of the `?` operator:
/// ```
/// use server_fn::{error::IntoServerFnResult, ServerFnError};
///
/// fn helper() -> Result<u8, std::num::ParseIntError> {
/// "3".parse()
/// }
///
/// enum FnError {
/// ErrorCase1,
/// }
/// fn server_fn() -> Result<u8, ServerFnError<FnError>> {
/// Ok(helper().into_server_fn_result()?)
/// }
/// ```
///
/// See also [`ConvertDefaultServerFnResult`] for conversion from the default [`ServerFnError<NoCustomError>`]
/// and [`ConvertServerFnResult`] for conversion between different custom error types.
pub trait IntoServerFnResult<T, CustErr> {
/// Converts a `Result<T, E>` into a `Result<T, ServerFnError<CustError>>`.
///
/// Maps the error to [`ServerFnError::ServerError()`] using [`Error::to_string()`].
///
/// When using the default [`NoCustomError`], one can directly use [`Into`] instead.
///
/// See the [trait-level documentation](IntoServerFnResult) for usage examples.
fn into_server_fn_result(self) -> Result<T, ServerFnError<CustErr>>;
}

impl<CustErr, T, E: std::error::Error> IntoServerFnResult<T, CustErr>
for Result<T, E>
{
fn into_server_fn_result(self) -> Result<T, ServerFnError<CustErr>> {
self.map_err(|err| {
ServerFnError::<CustErr>::ServerError(err.to_string())
})
}
}

impl<CustErr> Display for ServerFnError<CustErr>
where
CustErr: Display,
Expand Down

0 comments on commit a9ad221

Please sign in to comment.