1 use {
2 core::fmt,
3 log::debug,
4 serde::{Deserialize, Serialize},
5 serde_repr::{Deserialize_repr, Serialize_repr},
6 std::fmt::{Display, Formatter},
7 strum_macros::{EnumIter, IntoStaticStr},
8 thiserror::Error,
9 };
10
11 #[derive(Error, Serialize, Deserialize, Debug)]
12 #[serde(untagged)]
13 pub enum ErrorResponse {
14 Error {
15 error_code: ErrorCode,
16 message: String,
17 },
18 }
19
20 impl Display for ErrorResponse {
21 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
22 write!(f, "{:?}", self)
23 }
24 }
25
26 #[derive(Error, Serialize_repr, Deserialize_repr, Debug, EnumIter, IntoStaticStr)]
27 #[repr(i32)]
28 pub enum ErrorCode {
29 // Generic errors
30 #[error("Unknown error.")]
31 Unknown = 0,
32 #[error("Parse error.")]
33 ParseError = 1,
34 #[error("Network error. Please check your internet connection and try again.")]
35 NetworkError = 2,
36 #[error("Input error")]
37 InputError = 3,
38 #[error("Missing config file. Please regenerate with 'smb init'.")]
39 MissingConfig = 4,
40 // #[error("Missing id in repository. Please regenerate with 'smb init'.")]
41 // MissingId,
42 #[error("Cancel operation.")]
43 Cancel = 5,
44 // Account
45 #[error("Unauthorized access.")]
46 Unauthorized = 100,
47 #[error("Invalid params.")]
48 InvalidParams = 101,
49 // Account not ready errors.
50 #[error("Email not found.")]
51 EmailNotFound = 1000,
52 #[error("Email unverified.")]
53 EmailNotVerified = 1001,
54 #[error("Email confirmation failed.")]
55 EmailConfirmationFailed = 1002,
56 #[error("Password is unset.")]
57 PasswordNotSet = 1003,
58 #[error("GitHub email is not connected.")]
59 GitHubEmailNotConnected = 1004,
60 #[error("Email already exists.")]
61 EmailAlreadyExist = 1005,
62 #[error("Invalid password.")]
63 InvalidPassword = 1006,
64 // Projects
65 #[error("Project not found.")]
66 ProjectNotFound = 2000,
67 #[error("Runner not supported.")]
68 UnsupportedRunner = 2001,
69 }
70
71 impl ErrorCode {
72 /// Cannot expose the ErrorCode enum directly to Ruby,
73 /// so we need to get it from i32.
74 pub fn from_i32(value: i32) -> Self {
75 match value {
76 // Generic
77 100 => ErrorCode::Unauthorized,
78 101 => ErrorCode::InvalidParams,
79 // Account not ready errors
80 1000 => ErrorCode::EmailNotFound,
81 1001 => ErrorCode::EmailNotVerified,
82 1002 => ErrorCode::EmailConfirmationFailed,
83 1003 => ErrorCode::PasswordNotSet,
84 1004 => ErrorCode::GitHubEmailNotConnected,
85 1005 => ErrorCode::EmailAlreadyExist,
86 1006 => ErrorCode::InvalidPassword,
87 // Projects
88 2000 => ErrorCode::ProjectNotFound, // Projects
89 2001 => ErrorCode::UnsupportedRunner,
90 // Generic errors
91 5 => ErrorCode::Cancel,
92 4 => ErrorCode::MissingConfig,
93 3 => ErrorCode::InputError,
94 2 => ErrorCode::ParseError,
95 1 => ErrorCode::NetworkError,
96 // Fallback
97 _ => ErrorCode::Unknown,
98 }
99 }
100
101 // This could be better.
102 pub fn message(&self, l: Option<String>) -> &str {
103 debug!("Language code: {:?}, {}", l, self);
104 match self {
105 ErrorCode::Unknown => "Unknown error.",
106 // Networking
107 ErrorCode::ParseError => "Parse error.",
108 ErrorCode::NetworkError => {
109 "Network error. Please check your internet connection and try again."
110 }
111 // Generic
112 ErrorCode::Unauthorized => "Unauthorized access.",
113 ErrorCode::InvalidParams => "Invalid parameters.",
114 // Account not ready errors
115 ErrorCode::EmailNotFound => "Email not found.",
116 ErrorCode::EmailNotVerified => "Email not verified.",
117 ErrorCode::EmailConfirmationFailed => "Email confirmation faile.",
118 ErrorCode::PasswordNotSet => "Password is not set.",
119 ErrorCode::GitHubEmailNotConnected => "GitHub email is not connected.",
120 ErrorCode::EmailAlreadyExist => "Email already exists.",
121 ErrorCode::InvalidPassword => "Invalid password.",
122 // CLI Generic errors
123 ErrorCode::InputError => "Input error.",
124 ErrorCode::MissingConfig => "Missing config.",
125 ErrorCode::Cancel => "Cancelled operation.",
126 // Projects
127 ErrorCode::ProjectNotFound => "Project not found.",
128 ErrorCode::UnsupportedRunner => "Unsupported runner.",
129 }
130 }
131
132 pub fn rb_constant_name(&self) -> String {
133 // Using IntoStaticStr to get the variant name directly.
134 // self.into() will return a &'static str representing the variant name.
135 let variant_name_static_str: &'static str = self.into();
136 variant_name_static_str.to_string()
137 }
138 }