use crate::response::{Response, StatusCodeReasonPhrase};
pub const PROBLEM_JSON: &str = "application/problem+json";
pub fn text_errors() -> bool {
crate::server_config::ServerConfig::current().error_format_text
}
pub fn problem(status: &StatusCodeReasonPhrase, detail: &str) -> Response {
let text = if detail.is_empty() {
format!("{} {}", status.status_code, status.reason_phrase)
} else {
format!("{} {}: {detail}", status.status_code, status.reason_phrase)
};
problem_with_text(status, detail, &text)
}
pub fn problem_with_text(status: &StatusCodeReasonPhrase, detail: &str, text: &str) -> Response {
with_headers(status, detail, text, None)
}
pub(crate) fn with_headers(status: &StatusCodeReasonPhrase, detail: &str, text: &str, headers: Option<Vec<crate::header::Header>>) -> Response {
let as_text = text_errors();
if as_text && text.is_empty() {
return Response::get_response(status, headers, None);
}
let (body, content_type) = body(status, detail, text, as_text);
let content_range = crate::range::Range::get_content_range(body, content_type.to_string());
Response::get_response(status, headers, Some(vec![content_range]))
}
pub(crate) fn body(status: &StatusCodeReasonPhrase, detail: &str, text: &str, as_text: bool) -> (Vec<u8>, &'static str) {
if as_text {
return (text.as_bytes().to_vec(), crate::mime_type::MimeType::TEXT_PLAIN);
}
let mut json = format!(r#"{{"type":"about:blank","title":{},"status":{}"#, quote(status.reason_phrase), status.status_code);
if !detail.is_empty() {
json.push_str(r#","detail":"#);
json.push_str("e(detail));
}
json.push('}');
(json.into_bytes(), PROBLEM_JSON)
}
pub(crate) fn from_text(status: &StatusCodeReasonPhrase, text: &str) -> (Vec<u8>, &'static str) {
let line = format!("{} {}", status.status_code, status.reason_phrase);
let detail = text.strip_prefix(&line).map(|rest| rest.trim_start_matches(':').trim()).unwrap_or(text);
body(status, detail, text, text_errors())
}
fn quote(s: &str) -> String {
let mut out = String::with_capacity(s.len() + 2);
out.push('"');
for c in s.chars() {
match c {
'"' => out.push_str("\\\""),
'\\' => out.push_str("\\\\"),
'\n' => out.push_str("\\n"),
c if (c as u32) < 0x20 => out.push_str(&format!("\\u{:04x}", c as u32)),
c => out.push(c),
}
}
out.push('"');
out
}