1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
//! `AdminError` — the single enum every admin handler returns to.
//!
//! Maps cleanly onto an HTTP status via `IntoResponse`. Carries a string
//! payload for the diagnostic message; the response body never leaks the
//! underlying `sqlx::Error` text (just a generic "database error") so the
//! error message can't divulge schema details.
use umbral::orm::write::WriteError;
use umbral::web::{IntoResponse, Response, StatusCode};
#[derive(Debug)]
pub(crate) enum AdminError {
NotFound(String),
Render(String),
Sqlx(sqlx::Error),
/// gaps2 #12: structured umbral-validator failure with per-field
/// + non-field error accessors. Lets `sanitise_form_error`
/// render the specific message (FK target missing, validator
/// rule failure, required-field miss) instead of flattening
/// every write-time failure to "database error".
Write(WriteError),
BadInput(String),
}
impl From<sqlx::Error> for AdminError {
fn from(e: sqlx::Error) -> Self {
Self::Sqlx(e)
}
}
impl From<WriteError> for AdminError {
fn from(e: WriteError) -> Self {
Self::Write(e)
}
}
impl From<umbral::orm::DynError> for AdminError {
fn from(e: umbral::orm::DynError) -> Self {
// gaps2 #12: route the two-arm DynError to the matching
// AdminError variant so the structured `WriteError` keeps
// its per-field map all the way to the form template.
match e {
umbral::orm::DynError::Write(w) => Self::Write(w),
umbral::orm::DynError::Sqlx(s) => Self::Sqlx(s),
}
}
}
impl IntoResponse for AdminError {
fn into_response(self) -> Response {
match self {
AdminError::NotFound(msg) => (StatusCode::NOT_FOUND, msg).into_response(),
AdminError::Render(msg) => (StatusCode::INTERNAL_SERVER_ERROR, msg).into_response(),
AdminError::Sqlx(e) => {
tracing::error!(error = %e, "admin: database error");
(StatusCode::INTERNAL_SERVER_ERROR, "database error").into_response()
}
AdminError::Write(e) => {
// Display impl carries the human-readable
// "umbral::orm::write: <message>" form already; the
// form-submit path uses `sanitise_form_error` to
// render per-field instead of this generic
// response, so this arm is only hit when an admin
// handler returns a `WriteError` outside the form
// submit flow (rare).
tracing::error!(error = %e, "admin: validator error");
(StatusCode::BAD_REQUEST, e.to_string()).into_response()
}
AdminError::BadInput(msg) => (StatusCode::BAD_REQUEST, msg).into_response(),
}
}
}