rws_http/error/
problem.rs1use crate::response::{Response, StatusCodeReasonPhrase};
8
9pub const PROBLEM_JSON: &str = "application/problem+json";
11
12pub fn text_errors() -> bool {
14 crate::server_config::ServerConfig::current().error_format_text
15}
16
17pub fn problem(status: &StatusCodeReasonPhrase, detail: &str) -> Response {
20 let text = if detail.is_empty() {
21 format!("{} {}", status.status_code, status.reason_phrase)
22 } else {
23 format!("{} {}: {detail}", status.status_code, status.reason_phrase)
24 };
25 problem_with_text(status, detail, &text)
26}
27
28pub fn problem_with_text(status: &StatusCodeReasonPhrase, detail: &str, text: &str) -> Response {
30 with_headers(status, detail, text, None)
31}
32
33#[doc(hidden)]
34pub fn with_headers(status: &StatusCodeReasonPhrase, detail: &str, text: &str, headers: Option<Vec<crate::header::Header>>) -> Response {
35 let as_text = text_errors();
36 if as_text && text.is_empty() {
37 return Response::get_response(status, headers, None);
38 }
39 let (body, content_type) = body(status, detail, text, as_text);
40 let content_range = crate::range::Range::get_content_range(body, content_type.to_string());
41 Response::get_response(status, headers, Some(vec![content_range]))
42}
43
44#[doc(hidden)]
47pub fn body(status: &StatusCodeReasonPhrase, detail: &str, text: &str, as_text: bool) -> (Vec<u8>, &'static str) {
48 if as_text {
49 return (text.as_bytes().to_vec(), crate::mime_type::MimeType::TEXT_PLAIN);
50 }
51 let mut json = format!(r#"{{"type":"about:blank","title":{},"status":{}"#, quote(status.reason_phrase), status.status_code);
52 if !detail.is_empty() {
53 json.push_str(r#","detail":"#);
54 json.push_str("e(detail));
55 }
56 json.push('}');
57 (json.into_bytes(), PROBLEM_JSON)
58}
59
60#[doc(hidden)]
64pub fn from_text(status: &StatusCodeReasonPhrase, text: &str) -> (Vec<u8>, &'static str) {
65 let line = format!("{} {}", status.status_code, status.reason_phrase);
66 let detail = text.strip_prefix(&line).map(|rest| rest.trim_start_matches(':').trim()).unwrap_or(text);
67 body(status, detail, text, text_errors())
68}
69
70fn quote(s: &str) -> String {
71 let mut out = String::with_capacity(s.len() + 2);
72 out.push('"');
73 for c in s.chars() {
74 match c {
75 '"' => out.push_str("\\\""),
76 '\\' => out.push_str("\\\\"),
77 '\n' => out.push_str("\\n"),
78 c if (c as u32) < 0x20 => out.push_str(&format!("\\u{:04x}", c as u32)),
79 c => out.push(c),
80 }
81 }
82 out.push('"');
83 out
84}