lychee_lib/types/
response.rs1use std::{fmt::Display, time::Duration};
2
3use http::StatusCode;
4use serde::Serialize;
5
6use crate::{InputSource, Status, Uri, types::uri::raw::RawUriSpan};
7
8use super::redirect_history::Redirects;
9use crate::remap::Remap;
10
11#[derive(Debug)]
20pub struct Response {
21 input_source: InputSource,
22 response_body: ResponseBody,
23}
24
25impl Response {
26 #[inline]
27 #[must_use]
28 pub const fn new(
30 uri: Uri,
31 status: Status,
32 redirects: Option<Redirects>,
33 remap: Option<Remap>,
34 input_source: InputSource,
35 span: Option<RawUriSpan>,
36 duration: Option<Duration>,
37 ) -> Self {
38 Response {
39 input_source,
40 response_body: ResponseBody {
41 uri,
42 status,
43 redirects,
44 remap,
45 span,
46 duration,
47 },
48 }
49 }
50
51 #[inline]
52 #[must_use]
53 pub const fn status(&self) -> &Status {
55 &self.response_body.status
56 }
57
58 #[inline]
59 #[must_use]
60 pub const fn redirects(&self) -> Option<&Redirects> {
62 self.response_body.redirects.as_ref()
63 }
64
65 #[inline]
66 #[must_use]
67 pub const fn remap(&self) -> Option<&Remap> {
69 self.response_body.remap.as_ref()
70 }
71
72 #[inline]
73 #[must_use]
74 pub const fn source(&self) -> &InputSource {
77 &self.input_source
78 }
79
80 #[inline]
81 #[must_use]
82 pub const fn body(&self) -> &ResponseBody {
84 &self.response_body
85 }
86
87 #[inline]
88 #[must_use]
89 pub fn into_body(self) -> ResponseBody {
91 self.response_body
92 }
93}
94
95impl Display for Response {
96 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
97 <ResponseBody as Display>::fmt(&self.response_body, f)
98 }
99}
100
101impl Serialize for Response {
102 fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
103 where
104 S: serde::Serializer,
105 {
106 <ResponseBody as Serialize>::serialize(&self.response_body, s)
107 }
108}
109
110#[allow(clippy::module_name_repetitions)]
112#[derive(Debug, Serialize, Hash, PartialEq, Eq)]
113pub struct ResponseBody {
114 #[serde(flatten)]
115 pub uri: Uri,
117 pub status: Status,
119 #[serde(skip_serializing_if = "Option::is_none")]
121 pub redirects: Option<Redirects>,
122 #[serde(skip_serializing_if = "Option::is_none")]
124 pub remap: Option<Remap>,
125 #[serde(skip_serializing_if = "Option::is_none")]
127 pub span: Option<RawUriSpan>,
128 #[serde(skip_serializing_if = "Option::is_none")]
130 pub duration: Option<Duration>,
131}
132
133impl Display for ResponseBody {
138 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
139 write!(f, "{}", self.uri)?;
141
142 if let Some(span) = self.span {
143 write!(f, " (at {span})")?;
144 }
145
146 if matches!(self.status, Status::Ok(StatusCode::OK))
148 && self.redirects.is_none()
149 && self.remap.is_none()
150 {
151 return Ok(());
152 }
153
154 let details = self.status.details();
155 write!(f, " | {details}")?;
156
157 if let Some(remap) = &self.remap {
158 write!(f, " | Remaps: {remap}")?;
159 }
160
161 if let Some(redirects) = &self.redirects {
162 let count = redirects.count();
163 let noun = if count == 1 { "redirect" } else { "redirects" };
164 write!(f, " | Followed {count} {noun}. Redirects: {redirects}")?;
165 }
166
167 Ok(())
168 }
169}