oddity_rtsp_protocol/
response.rs1use std::fmt;
2
3use super::{
4 message::{
5 status_to_code, status_to_reason, Bytes, Headers, Message, Status, StatusCategory,
6 StatusCode, Version,
7 },
8 request::Request,
9 rtp_info::RtpInfo,
10};
11
12#[derive(Clone, Debug)]
13pub struct Response {
14 pub version: Version,
15 pub status: StatusCode,
16 pub reason: String,
17 pub headers: Headers,
18 pub body: Option<Bytes>,
19}
20
21impl Message for Response {
22 type Metadata = ResponseMetadata;
23
24 fn new(metadata: ResponseMetadata, headers: Headers, body: Option<Bytes>) -> Self {
25 Self {
26 version: metadata.version,
27 status: metadata.status,
28 reason: metadata.reason,
29 headers,
30 body,
31 }
32 }
33}
34
35impl Response {
36 pub fn ok() -> ResponseBuilder {
37 ResponseBuilder::ok()
38 }
39
40 pub fn error(status: Status) -> ResponseBuilder {
41 ResponseBuilder::error(status)
42 }
43
44 pub fn status(&self) -> StatusCategory {
45 match self.status {
46 s if s >= 600 => StatusCategory::Unknown,
47 s if s >= 500 => StatusCategory::ServerError,
48 s if s >= 400 => StatusCategory::ClientError,
49 s if s >= 300 => StatusCategory::Redirection,
50 s if s >= 200 => StatusCategory::Success,
51 s if s >= 100 => StatusCategory::Informational,
52 _ => StatusCategory::Unknown,
53 }
54 }
55}
56
57impl fmt::Display for Response {
58 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
59 write!(
60 f,
61 "Version: {}, Status Code: {}, Reason Phrase: {}",
62 self.version, self.status, &self.reason
63 )?;
64
65 if !self.headers.is_empty() {
66 writeln!(f, "\nHeaders:")?;
67 for (var, val) in &self.headers {
68 writeln!(f, " - {}: {}", &var, &val)?;
69 }
70 }
71
72 if let Some(body) = &self.body {
73 writeln!(f, "[{} bytes]", body.len())?;
74 }
75
76 Ok(())
77 }
78}
79
80#[derive(Clone, Debug)]
81pub struct ResponseMetadata {
82 version: Version,
83 status: StatusCode,
84 reason: String,
85}
86
87impl ResponseMetadata {
88 pub(super) fn new(version: Version, status: StatusCode, reason: String) -> Self {
89 Self {
90 version,
91 status,
92 reason,
93 }
94 }
95}
96
97pub struct ResponseBuilder {
98 response: Response,
99}
100
101impl ResponseBuilder {
102 pub fn from_status(status: Status) -> ResponseBuilder {
103 ResponseBuilder {
104 response: Response {
105 version: Default::default(),
106 status: status_to_code(status),
107 reason: status_to_reason(status).to_string(),
108 headers: Default::default(),
109 body: Default::default(),
110 },
111 }
112 }
113
114 pub fn ok() -> ResponseBuilder {
115 Self::from_status(Status::Ok)
116 }
117
118 pub fn error(status: Status) -> ResponseBuilder {
119 Self::from_status(status)
120 }
121
122 pub fn with_cseq_of(mut self, request: &Request) -> ResponseBuilder {
123 if let Some(cseq) = request.headers.get("CSeq") {
124 self.response
125 .headers
126 .insert("CSeq".to_string(), cseq.to_string());
127 }
128 self
129 }
130
131 pub fn with_header(mut self, var: impl ToString, val: impl ToString) -> ResponseBuilder {
132 self.response
133 .headers
134 .insert(var.to_string(), val.to_string());
135 self
136 }
137
138 pub fn with_rtp_info(mut self, rtp_info: impl IntoIterator<Item = RtpInfo>) -> ResponseBuilder {
139 self.response.headers.insert(
140 "RTP-Info".to_string(),
141 rtp_info
142 .into_iter()
143 .map(|item| item.to_string())
144 .collect::<Vec<_>>()
145 .join(","),
146 );
147 self
148 }
149
150 pub fn with_body(mut self, body: Bytes, content_type: &str) -> ResponseBuilder {
151 self = self
152 .with_header("Content-Length", body.len())
153 .with_header("Content-Type", content_type);
154 self.response.body = Some(body);
155 self
156 }
157
158 pub fn with_sdp(self, contents: String) -> ResponseBuilder {
159 self.with_body(Bytes::from(contents), "application/sdp")
160 }
161
162 pub fn build(self) -> Response {
163 self.response
164 }
165}