// The response kinds that are an envelope rather than a body — FastAPI's
// `RedirectResponse` and `FileResponse`. `text` / `html` / `json` in `app.mbt`
// serialise a value; these two decide what the headers say about it.
///|
/// A redirect to `url` (← FastAPI's `RedirectResponse`). The body is empty and
/// `Location` carries the target.
///
/// `307` is the default because it is the redirect that keeps the request's
/// method and body — the one a `POST` can safely follow, which `302` historically
/// is not. Use `303` to send a client to a `GET` after a write, and `301` / `308`
/// for a move that is permanent.
///
/// The URL is percent-encoded over the characters a URI reserves for structure,
/// so an already-encoded URL passes through unchanged and a `\r\n` smuggled into
/// one cannot open a header of its own.
pub fn redirect(url : String, status? : Int = 307) -> @moonasgi.Response {
@moonasgi.Response::new(
status,
[("location", @mime.percent_encode(url[:], safe=":/%#?=@[]!$&'()*+,;"[:]))],
b"",
)
}
///|
/// A file download built from bytes already in hand (← FastAPI's `FileResponse`).
///
/// It takes the content rather than a path because moonapi has no filesystem of
/// its own — the same app runs on wasm, js and native, and only the server knows
/// how to read a file on any of them. What this adds is the envelope: a media
/// type guessed from `filename`'s extension unless `media_type` names one,
/// `Content-Length`, and a `Content-Disposition` that tells the browser to save
/// the file (or, with `inline`, to display it) under that name. An empty
/// `filename` leaves the disposition off entirely.
pub fn file_response(
content : Bytes,
filename? : String = "",
media_type? : String = "",
status? : Int = 200,
inline? : Bool = false,
) -> @moonasgi.Response {
let ct = if media_type != "" {
media_type
} else {
@media.type_of(filename[:])
}
let headers : Array[(String, String)] = [
("content-type", ct),
("content-length", content.length().to_string()),
]
if filename != "" {
headers.push(
("content-disposition", @media.disposition(filename[:], inline~)),
)
}
@moonasgi.Response::new(status, headers, content)
}