Skip to main content

rustack_cloudfront_model/
error.rs

1//! CloudFront error type.
2//!
3//! Unlike S3, CloudFront wraps error responses in `<ErrorResponse>`. The HTTP
4//! layer is responsible for emitting the wire format; this module only
5//! classifies errors and carries their HTTP status plus AWS error code.
6
7use thiserror::Error;
8
9/// Top-level CloudFront service error.
10///
11/// Each variant maps to an AWS error `Code` and an HTTP status.
12#[derive(Debug, Error)]
13pub enum CloudFrontError {
14    /// The requested resource does not exist.
15    #[error("{code}: {message}")]
16    NoSuchResource {
17        /// AWS error code, e.g. `NoSuchDistribution`.
18        code: &'static str,
19        /// Human-readable message.
20        message: String,
21    },
22
23    /// The caller submitted an invalid argument.
24    #[error("InvalidArgument: {0}")]
25    InvalidArgument(String),
26
27    /// The request references an argument that is missing.
28    #[error("MissingArgument: {0}")]
29    MissingArgument(String),
30
31    /// The resource is already in use.
32    #[error("ResourceInUse: {0}")]
33    ResourceInUse(String),
34
35    /// The distribution (or similar config resource) is not currently disabled
36    /// and therefore cannot be deleted.
37    #[error("DistributionNotDisabled: {0}")]
38    DistributionNotDisabled(String),
39
40    /// If-Match ETag does not match the current resource ETag.
41    #[error("PreconditionFailed: {0}")]
42    PreconditionFailed(String),
43
44    /// If-Match header was required but not provided.
45    #[error("InvalidIfMatchVersion: {0}")]
46    InvalidIfMatchVersion(String),
47
48    /// The submitted configuration is invalid (referential integrity, shape).
49    #[error("InvalidArgument: {0}")]
50    MalformedInput(String),
51
52    /// Attempt to create a resource that already exists.
53    #[error("{code}: {message}")]
54    AlreadyExists {
55        /// AWS error code, e.g. `DistributionAlreadyExists`.
56        code: &'static str,
57        /// Human-readable message.
58        message: String,
59    },
60
61    /// Feature not supported / not implemented in Rustack.
62    #[error("NotImplemented: {0}")]
63    NotImplemented(String),
64
65    /// Access denied.
66    #[error("AccessDenied: {0}")]
67    AccessDenied(String),
68
69    /// Generic internal error.
70    #[error("InternalServerError: {0}")]
71    Internal(String),
72}
73
74impl CloudFrontError {
75    /// AWS error code string for the wire `<Code>` element.
76    #[must_use]
77    pub fn code(&self) -> &'static str {
78        match self {
79            Self::NoSuchResource { code, .. } | Self::AlreadyExists { code, .. } => code,
80            Self::InvalidArgument(_) | Self::MalformedInput(_) => "InvalidArgument",
81            Self::MissingArgument(_) => "MissingArgument",
82            Self::ResourceInUse(_) => "ResourceInUse",
83            Self::DistributionNotDisabled(_) => "DistributionNotDisabled",
84            Self::PreconditionFailed(_) => "PreconditionFailed",
85            Self::InvalidIfMatchVersion(_) => "InvalidIfMatchVersion",
86            Self::NotImplemented(_) => "NotImplemented",
87            Self::AccessDenied(_) => "AccessDenied",
88            Self::Internal(_) => "InternalServerError",
89        }
90    }
91
92    /// HTTP status code for this error variant.
93    #[must_use]
94    pub fn http_status(&self) -> u16 {
95        match self {
96            Self::NoSuchResource { .. } => 404,
97            Self::InvalidArgument(_)
98            | Self::MalformedInput(_)
99            | Self::MissingArgument(_)
100            | Self::InvalidIfMatchVersion(_) => 400,
101            Self::ResourceInUse(_)
102            | Self::AlreadyExists { .. }
103            | Self::DistributionNotDisabled(_) => 409,
104            Self::PreconditionFailed(_) => 412,
105            Self::NotImplemented(_) => 501,
106            Self::AccessDenied(_) => 403,
107            Self::Internal(_) => 500,
108        }
109    }
110
111    /// Human-readable message.
112    #[must_use]
113    pub fn message(&self) -> String {
114        match self {
115            Self::NoSuchResource { message, .. } | Self::AlreadyExists { message, .. } => {
116                message.clone()
117            }
118            Self::InvalidArgument(m)
119            | Self::MissingArgument(m)
120            | Self::ResourceInUse(m)
121            | Self::DistributionNotDisabled(m)
122            | Self::PreconditionFailed(m)
123            | Self::InvalidIfMatchVersion(m)
124            | Self::MalformedInput(m)
125            | Self::NotImplemented(m)
126            | Self::AccessDenied(m)
127            | Self::Internal(m) => m.clone(),
128        }
129    }
130}
131
132/// Constructors for convenience.
133impl CloudFrontError {
134    /// "NoSuchDistribution" shortcut.
135    #[must_use]
136    pub fn no_such_distribution(id: impl Into<String>) -> Self {
137        Self::NoSuchResource {
138            code: "NoSuchDistribution",
139            message: format!("The specified distribution does not exist: {}", id.into()),
140        }
141    }
142
143    /// "NoSuchInvalidation" shortcut.
144    #[must_use]
145    pub fn no_such_invalidation(id: impl Into<String>) -> Self {
146        Self::NoSuchResource {
147            code: "NoSuchInvalidation",
148            message: format!("The specified invalidation does not exist: {}", id.into()),
149        }
150    }
151
152    /// "NoSuchOriginAccessControl" shortcut.
153    #[must_use]
154    pub fn no_such_origin_access_control(id: impl Into<String>) -> Self {
155        Self::NoSuchResource {
156            code: "NoSuchOriginAccessControl",
157            message: format!(
158                "The specified origin access control does not exist: {}",
159                id.into()
160            ),
161        }
162    }
163
164    /// "NoSuchCloudFrontOriginAccessIdentity" shortcut.
165    #[must_use]
166    pub fn no_such_oai(id: impl Into<String>) -> Self {
167        Self::NoSuchResource {
168            code: "NoSuchCloudFrontOriginAccessIdentity",
169            message: format!(
170                "The specified origin access identity does not exist: {}",
171                id.into()
172            ),
173        }
174    }
175
176    /// "NoSuchCachePolicy" shortcut.
177    #[must_use]
178    pub fn no_such_cache_policy(id: impl Into<String>) -> Self {
179        Self::NoSuchResource {
180            code: "NoSuchCachePolicy",
181            message: format!("The specified cache policy does not exist: {}", id.into()),
182        }
183    }
184
185    /// "NoSuchOriginRequestPolicy" shortcut.
186    #[must_use]
187    pub fn no_such_origin_request_policy(id: impl Into<String>) -> Self {
188        Self::NoSuchResource {
189            code: "NoSuchOriginRequestPolicy",
190            message: format!(
191                "The specified origin request policy does not exist: {}",
192                id.into()
193            ),
194        }
195    }
196
197    /// "NoSuchResponseHeadersPolicy" shortcut.
198    #[must_use]
199    pub fn no_such_response_headers_policy(id: impl Into<String>) -> Self {
200        Self::NoSuchResource {
201            code: "NoSuchResponseHeadersPolicy",
202            message: format!(
203                "The specified response headers policy does not exist: {}",
204                id.into()
205            ),
206        }
207    }
208
209    /// "NoSuchPublicKey" shortcut.
210    #[must_use]
211    pub fn no_such_public_key(id: impl Into<String>) -> Self {
212        Self::NoSuchResource {
213            code: "NoSuchPublicKey",
214            message: format!("The specified public key does not exist: {}", id.into()),
215        }
216    }
217
218    /// "NoSuchResource" generic shortcut.
219    #[must_use]
220    pub fn no_such_resource(kind: &'static str, id: impl Into<String>) -> Self {
221        Self::NoSuchResource {
222            code: "NoSuchResource",
223            message: format!("The specified {kind} does not exist: {}", id.into()),
224        }
225    }
226}