hook0_client/generated/
errors.rs1use super::models::Problem;
7use super::models::ProblemId;
8
9const LOWEST_SUCCESS: u16 = 200;
11
12const LOWEST_REDIRECTION: u16 = 300;
14
15const MAX_PREVIEW_BYTES: usize = 256;
20
21#[derive(Debug, Clone)]
27pub struct ProblemError {
28 pub status: u16,
30 pub kind: Option<ProblemId>,
32 pub problem: Option<Problem>,
34 pub detail: String,
36}
37
38impl std::fmt::Display for ProblemError {
39 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
40 formatter.write_str(&self.detail)
41 }
42}
43
44impl std::error::Error for ProblemError {}
45
46#[derive(Debug)]
48pub enum RequestError {
49 Transport(Box<dyn std::error::Error + Send + Sync>),
51 Api(Box<ProblemError>),
56 Unreadable {
58 status: u16,
60 detail: String,
62 },
63 Unwritable {
65 detail: String,
67 },
68}
69
70impl RequestError {
71 pub fn transport<E>(cause: E) -> Self
73 where
74 E: std::error::Error + Send + Sync + 'static,
75 {
76 Self::Transport(Box::new(cause))
77 }
78
79 pub fn unwritable(cause: serde_json::Error) -> Self {
81 Self::Unwritable {
82 detail: cause.to_string(),
83 }
84 }
85
86 pub fn unreadable(status: u16, payload: &[u8], cause: &serde_json::Error) -> Self {
88 let seen = preview(payload);
89 let unread = format!("the API answered {status} with an unreadable body");
90 let detail = format!("{unread}: {cause} ({seen})");
91 Self::Unreadable { status, detail }
92 }
93}
94
95impl std::fmt::Display for RequestError {
96 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
97 match self {
98 Self::Transport(cause) => write!(formatter, "the API was not reached: {cause}"),
99 Self::Api(failure) => failure.fmt(formatter),
100 Self::Unreadable { detail, .. } => formatter.write_str(detail),
101 Self::Unwritable { detail } => formatter.write_str(detail),
102 }
103 }
104}
105
106impl std::error::Error for RequestError {}
107
108pub fn problem_for(status: u16, payload: &[u8]) -> Option<ProblemError> {
110 if (LOWEST_SUCCESS..LOWEST_REDIRECTION).contains(&status) {
111 return None;
112 }
113
114 let problem: Option<Problem> = serde_json::from_slice(payload).ok();
115 let seen = preview(payload);
116 let kind = problem.as_ref().map(|problem| problem.id);
117
118 Some(ProblemError {
119 status,
120 kind,
121 problem,
122 detail: format!("the API answered {status}: {seen}"),
123 })
124}
125
126fn preview(payload: &[u8]) -> String {
128 let text = String::from_utf8_lossy(payload);
129 let mut kept = String::with_capacity(MAX_PREVIEW_BYTES);
130
131 for character in text.chars() {
132 if kept.len() + character.len_utf8() > MAX_PREVIEW_BYTES {
133 kept.push('…');
134 break;
135 }
136 kept.push(character);
137 }
138
139 kept
140}