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}