Skip to main content

canic_core/dto/
error.rs

1use crate::{InternalError, access::AccessError, dto::prelude::*};
2use std::fmt::{self, Display};
3
4//
5// Error
6//
7// Public API error payload.
8//
9
10#[derive(CandidType, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
11pub struct Error {
12    pub code: ErrorCode,
13    pub message: String,
14}
15
16impl Error {
17    #[must_use]
18    pub const fn new(code: ErrorCode, message: String) -> Self {
19        Self { code, message }
20    }
21
22    // 409 – Conflict with existing state or resource.
23    pub fn conflict(message: impl Into<String>) -> Self {
24        Self::new(ErrorCode::Conflict, message.into())
25    }
26
27    // 409 – Policy violation with a stable policy-specific code.
28    pub fn policy(code: ErrorCode, message: impl Into<String>) -> Self {
29        Self::new(code, message.into())
30    }
31
32    // 403 – Authenticated caller is not permitted to perform this action.
33    pub fn forbidden(message: impl Into<String>) -> Self {
34        Self::new(ErrorCode::Forbidden, message.into())
35    }
36
37    // 500 – Internal or unexpected failure.
38    pub fn internal(message: impl Into<String>) -> Self {
39        Self::new(ErrorCode::Internal, message.into())
40    }
41
42    // 400 – Invalid input or malformed request.
43    pub fn invalid(message: impl Into<String>) -> Self {
44        Self::new(ErrorCode::InvalidInput, message.into())
45    }
46
47    // 500 – Broken invariant or impossible internal state.
48    pub fn invariant(message: impl Into<String>) -> Self {
49        Self::new(ErrorCode::InvariantViolation, message.into())
50    }
51
52    // 429 / 507 – Resource, quota, or capacity exhaustion.
53    pub fn exhausted(message: impl Into<String>) -> Self {
54        Self::new(ErrorCode::ResourceExhausted, message.into())
55    }
56
57    // 404 – Requested resource was not found.
58    pub fn not_found(message: impl Into<String>) -> Self {
59        Self::new(ErrorCode::NotFound, message.into())
60    }
61
62    // 401 – Caller is unauthenticated or has an invalid identity.
63    pub fn unauthorized(message: impl Into<String>) -> Self {
64        Self::new(ErrorCode::Unauthorized, message.into())
65    }
66
67    // 503 – Service is temporarily unavailable due to runtime controls.
68    pub fn unavailable(message: impl Into<String>) -> Self {
69        Self::new(ErrorCode::Unavailable, message.into())
70    }
71}
72
73impl Display for Error {
74    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75        write!(f, "[{:?}] {}", self.code, self.message)
76    }
77}
78
79impl From<AccessError> for Error {
80    fn from(err: AccessError) -> Self {
81        Self::from(InternalError::from(err))
82    }
83}
84
85//
86// ErrorCode
87//
88// Stable public error codes.
89//
90
91#[derive(CandidType, Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
92#[non_exhaustive]
93#[remain::sorted]
94pub enum ErrorCode {
95    Conflict,
96    Forbidden,
97    Internal,
98    InvalidInput,
99    InvariantViolation,
100    NotFound,
101    PolicyReplicaRequiresSingletonWithScaling,
102    PolicyRoleAlreadyRegistered,
103    PolicyShardRequiresSingletonWithSharding,
104    PolicySingletonAlreadyRegisteredUnderParent,
105    PolicyTenantRequiresSingletonParent,
106    ResourceExhausted,
107    Unauthorized,
108    Unavailable,
109}