summaryrefslogtreecommitdiff
path: root/src/error/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/error/mod.rs')
-rw-r--r--src/error/mod.rs79
1 files changed, 79 insertions, 0 deletions
diff --git a/src/error/mod.rs b/src/error/mod.rs
new file mode 100644
index 0000000..606e344
--- /dev/null
+++ b/src/error/mod.rs
@@ -0,0 +1,79 @@
+use std::{error, fmt, io};
+
+use axum::{
+ http::StatusCode,
+ response::{IntoResponse, Response},
+};
+
+pub mod chain;
+pub mod failed;
+
+// I'm making an effort to avoid `anyhow` here, as that crate is _enormously_
+// complex (though very usable). We don't need to be overly careful about
+// allocations on errors in this app, so this is fine for most "general
+// failure" cases.
+type BoxedError = Box<dyn error::Error + Send + Sync>;
+
+// Returns a 500 Internal Server Error to the client. Meant to be used via the
+// `?` operator; _does not_ return the originating error to the client.
+#[derive(Debug)]
+pub struct Internal(Id, BoxedError);
+
+impl<E> From<E> for Internal
+where
+ E: Into<BoxedError>,
+{
+ fn from(error: E) -> Self {
+ let id = Id::generate();
+ Self(id, error.into())
+ }
+}
+
+impl fmt::Display for Internal {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let Self(id, _) = self;
+ writeln!(f, "internal server error")?;
+ writeln!(f, "error id: {id}")?;
+ Ok(())
+ }
+}
+
+impl IntoResponse for Internal {
+ fn into_response(self) -> Response {
+ let Self(id, error) = &self;
+ chain::format_with_id(&mut io::stderr().lock(), id, error.as_ref())
+ .expect("write to stderr");
+ (StatusCode::INTERNAL_SERVER_ERROR, self.to_string()).into_response()
+ }
+}
+
+pub type Id = crate::id::Id<InternalError>;
+
+#[derive(Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
+pub struct InternalError;
+
+impl crate::id::Prefix for InternalError {
+ fn prefix(&self) -> &'static str {
+ "E"
+ }
+}
+
+pub struct Unauthorized;
+
+impl IntoResponse for Unauthorized {
+ fn into_response(self) -> Response {
+ (StatusCode::UNAUTHORIZED, "unauthorized").into_response()
+ }
+}
+
+pub struct NotFound<E>(pub E);
+
+impl<E> IntoResponse for NotFound<E>
+where
+ E: std::error::Error,
+{
+ fn into_response(self) -> Response {
+ let Self(response) = self;
+ (StatusCode::NOT_FOUND, response.to_string()).into_response()
+ }
+}