shared/error/
account.rs

1use crate::domain::billing::{SchoolNameId, SchoolNameValue};
2use crate::error::billing::BillingError;
3use crate::error::{ServiceError, TransientError};
4use serde::{Deserialize, Serialize};
5use thiserror::Error;
6
7#[cfg(feature = "backend")]
8use actix_web::{body::BoxBody, HttpResponse, ResponseError};
9#[cfg(feature = "backend")]
10use stripe::StripeError;
11
12#[allow(missing_docs)]
13#[derive(Debug, Error, Serialize, Deserialize)]
14pub enum AccountError {
15    #[cfg_attr(feature = "backend", error(transparent))]
16    #[cfg_attr(not(feature = "backend"), error("Internal server error"))]
17    InternalServerError(
18        #[serde(skip)]
19        #[from]
20        TransientError<anyhow::Error>,
21    ),
22    #[error(transparent)]
23    Service(ServiceError),
24    #[cfg_attr(feature = "backend", error(transparent))]
25    #[cfg_attr(not(feature = "backend"), error("Stripe error"))]
26    Stripe(
27        #[cfg(feature = "backend")]
28        #[serde(skip)]
29        #[from]
30        TransientError<StripeError>,
31    ),
32    #[error("User already has an existing account")]
33    UserHasAccount,
34    #[error("A school name of {0} already exists")]
35    SchoolNameExists(SchoolNameValue),
36    #[error("A school using a name with ID {0} already exists")]
37    SchoolExists(SchoolNameId),
38    #[error("{0}")]
39    NotFound(String),
40    #[error("Forbidden")]
41    Forbidden,
42    #[error("{0}")]
43    BadRequest(String),
44}
45
46#[cfg(feature = "backend")]
47impl From<StripeError> for AccountError {
48    fn from(value: StripeError) -> Self {
49        Self::from(TransientError::from(value))
50    }
51}
52
53impl From<ServiceError> for AccountError {
54    fn from(err: ServiceError) -> Self {
55        Self::Service(err)
56    }
57}
58
59impl From<AccountError> for BillingError {
60    fn from(value: AccountError) -> Self {
61        match value {
62            AccountError::Forbidden => Self::Forbidden,
63            AccountError::InternalServerError(error) => Self::InternalServerError(error),
64            error => TransientError::from(anyhow::Error::from(error)).into(),
65        }
66    }
67}
68
69impl From<anyhow::Error> for AccountError {
70    fn from(e: anyhow::Error) -> Self {
71        Self::InternalServerError(e.into())
72    }
73}
74
75#[cfg(feature = "backend")]
76impl ResponseError for AccountError {
77    fn status_code(&self) -> http::StatusCode {
78        match self {
79            Self::InternalServerError { .. } | Self::Stripe { .. } => {
80                http::StatusCode::INTERNAL_SERVER_ERROR
81            }
82            Self::Service(service) => service.status_code(),
83            Self::NotFound(_) => http::StatusCode::NOT_FOUND,
84            Self::Forbidden => http::StatusCode::FORBIDDEN,
85            _ => http::StatusCode::BAD_REQUEST,
86        }
87    }
88
89    fn error_response(&self) -> HttpResponse<BoxBody> {
90        HttpResponse::build(self.status_code()).json(self)
91    }
92}