oddity_rtsp_protocol/
response.rs

1use 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}