// Copyright 2026 Leo Cheng
// SPDX-License-Identifier: Apache-2.0
///|
/// What an application's failure becomes, as Starlette's `ServerErrorMiddleware`: a 500 when the
/// response has not started, then the error raised on to the server, which logs it. `debug` puts
/// the error in the body. `handler` writes the answer instead: `(status, headers, body)` for the
/// error.
pub(all) struct ServerErrors {
debug : Bool
handler : ((Error) -> (Int32, Array[(String, String)], String))?
}
///|
/// Starlette's defaults: no detail in the body, the plain 500.
pub let server_errors : ServerErrors = { debug: false, handler: None, }
///|
pub fn ServerErrors::new(
debug~ : Bool = server_errors.debug,
handler? : (Error) -> (Int32, Array[(String, String)], String),
) -> ServerErrors {
{ debug, handler, }
}
///|
pub impl Layer for ServerErrors with fn wrap(self, app) {
(scope, receive, send) => {
guard scope is Http(_) else {
app(scope, receive, send)
return
}
let started = Ref(false)
let failure : Ref[Error?] = Ref(None)
app(scope, receive, event => {
if event is HttpResponseStart(..) {
started.val = true
}
send(event)
}) catch {
e => failure.val = Some(e)
}
guard failure.val is Some(e) else { return }
if !started.val {
let (status, headers, body) = match self.handler {
Some(f) => f(e)
None if self.debug => (500, [], "Internal Server Error\n\n\{e}")
None => (500, [], "Internal Server Error")
}
respond(send, status, headers, body)
}
raise e
}
}
///|
pub extend ServerErrors with Layer::{wrap}