rtsp_types/headers/
accept.rs

1// Copyright (C) 2020 Sebastian Dröge <sebastian@centricular.com>
2//
3// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>
4
5use super::*;
6
7use std::fmt;
8
9/// `Accept` header ([RFC 7826 section 18.1](https://tools.ietf.org/html/rfc7826#section-18.1)).
10#[derive(Debug, Clone)]
11#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
12pub struct Accept(Vec<MediaTypeRange>);
13
14/// Media type range.
15#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
16#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
17pub struct MediaTypeRange {
18    /// Media type.
19    ///
20    /// `None` for `*`.
21    pub type_: Option<MediaType>,
22    /// Optional media sub-type.
23    ///
24    /// `None` for `*`.
25    pub subtype: Option<String>,
26    /// Media type parameters.
27    pub params: Vec<(String, Option<String>)>,
28}
29
30/// Media type.
31#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
32#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
33pub enum MediaType {
34    Text,
35    Image,
36    Audio,
37    Video,
38    Application,
39    Message,
40    Multipart,
41    Extension(String),
42}
43
44impl MediaType {
45    pub fn as_str(&self) -> &str {
46        match self {
47            MediaType::Text => "text",
48            MediaType::Image => "image",
49            MediaType::Audio => "audio",
50            MediaType::Video => "video",
51            MediaType::Application => "application",
52            MediaType::Message => "message",
53            MediaType::Multipart => "Multipart",
54            MediaType::Extension(ref s) => s.as_str(),
55        }
56    }
57}
58
59impl fmt::Display for MediaType {
60    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61        f.write_str(self.as_str())
62    }
63}
64
65impl std::str::FromStr for MediaType {
66    type Err = HeaderParseError;
67
68    fn from_str(s: &str) -> Result<Self, HeaderParseError> {
69        match s {
70            "text" => Ok(MediaType::Text),
71            "image" => Ok(MediaType::Image),
72            "audio" => Ok(MediaType::Audio),
73            "video" => Ok(MediaType::Video),
74            "application" => Ok(MediaType::Application),
75            "message" => Ok(MediaType::Message),
76            "multipart" => Ok(MediaType::Multipart),
77            _ => Ok(MediaType::Extension(String::from(s))),
78        }
79    }
80}
81
82impl std::ops::Deref for Accept {
83    type Target = Vec<MediaTypeRange>;
84
85    fn deref(&self) -> &Self::Target {
86        &self.0
87    }
88}
89
90impl std::ops::DerefMut for Accept {
91    fn deref_mut(&mut self) -> &mut Self::Target {
92        &mut self.0
93    }
94}
95
96impl AsRef<Vec<MediaTypeRange>> for Accept {
97    fn as_ref(&self) -> &Vec<MediaTypeRange> {
98        &self.0
99    }
100}
101
102impl AsMut<Vec<MediaTypeRange>> for Accept {
103    fn as_mut(&mut self) -> &mut Vec<MediaTypeRange> {
104        &mut self.0
105    }
106}
107
108impl From<Vec<MediaTypeRange>> for Accept {
109    fn from(v: Vec<MediaTypeRange>) -> Self {
110        Accept(v)
111    }
112}
113
114impl<'a> From<&'a [MediaTypeRange]> for Accept {
115    fn from(v: &'a [MediaTypeRange]) -> Self {
116        Accept(v.to_vec())
117    }
118}
119
120impl Accept {
121    /// Creates a new `Accept` header builder.
122    pub fn builder() -> AcceptBuilder {
123        AcceptBuilder(Vec::new())
124    }
125}
126
127/// Builder for the 'Accept' header.
128#[derive(Debug, Clone)]
129pub struct AcceptBuilder(Vec<MediaTypeRange>);
130
131impl AcceptBuilder {
132    /// Add the provided media type to the `Accept` header.
133    pub fn media_type(mut self, media_type: MediaTypeRange) -> Self {
134        self.0.push(media_type);
135        self
136    }
137
138    /// Build the `Accept` header.
139    pub fn build(self) -> Accept {
140        Accept(self.0)
141    }
142}
143
144impl super::TypedHeader for Accept {
145    fn from_headers(headers: impl AsRef<Headers>) -> Result<Option<Self>, HeaderParseError> {
146        use super::parser_helpers::split_once;
147
148        let headers = headers.as_ref();
149
150        let header = match headers.get(&ACCEPT) {
151            None => return Ok(None),
152            Some(header) => header,
153        };
154
155        let mut media_types = Vec::new();
156        for media_type_range in header.as_str().split(',') {
157            let media_type_range = media_type_range.trim();
158
159            let mut iter = media_type_range.split(';');
160            let media_type = iter.next().ok_or(HeaderParseError)?.trim();
161            let (media_type, media_subtype) =
162                split_once(media_type, '/').ok_or(HeaderParseError)?;
163
164            let media_type = if media_type == "*" {
165                None
166            } else {
167                Some(media_type)
168            };
169            let media_subtype = if media_subtype == "*" {
170                None
171            } else {
172                Some(media_subtype)
173            };
174
175            let mut params = Vec::new();
176            for param in iter {
177                let param = param.trim();
178                if let Some((param, value)) = split_once(param, '=') {
179                    params.push((String::from(param), Some(String::from(value))));
180                } else {
181                    params.push((String::from(param), None));
182                }
183            }
184
185            media_types.push(MediaTypeRange {
186                type_: media_type
187                    .map(|s| s.parse())
188                    .transpose()
189                    .map_err(|_| HeaderParseError)?,
190                subtype: media_subtype.map(String::from),
191                params,
192            });
193        }
194
195        Ok(Some(Accept(media_types)))
196    }
197
198    fn insert_into(&self, mut headers: impl AsMut<Headers>) {
199        use std::fmt::Write;
200
201        let headers = headers.as_mut();
202
203        let mut media_types = String::new();
204        for media_type in &self.0 {
205            if !media_types.is_empty() {
206                media_types.push_str(", ");
207            }
208
209            if let Some(ref t) = media_type.type_ {
210                write!(&mut media_types, "{t}").unwrap();
211            } else {
212                media_types.push('*');
213            }
214            media_types.push('/');
215            if let Some(ref t) = media_type.subtype {
216                media_types.push_str(t);
217            } else {
218                media_types.push('*');
219            }
220
221            for param in &media_type.params {
222                media_types.push(';');
223                if let Some(ref value) = param.1 {
224                    write!(&mut media_types, "{}={}", param.0, value).unwrap();
225                } else {
226                    media_types.push_str(&param.0);
227                }
228            }
229        }
230
231        headers.insert(ACCEPT, media_types);
232    }
233}
234
235impl super::TypedAppendableHeader for Accept {
236    fn append_to(&self, mut headers: impl AsMut<Headers>) {
237        use std::fmt::Write;
238
239        let headers = headers.as_mut();
240
241        let mut media_types = String::new();
242        for media_type in &self.0 {
243            if !media_types.is_empty() {
244                media_types.push_str(", ");
245            }
246
247            if let Some(ref t) = media_type.type_ {
248                write!(&mut media_types, "{t}").unwrap();
249            } else {
250                media_types.push('*');
251            }
252            media_types.push('/');
253            if let Some(ref t) = media_type.subtype {
254                media_types.push_str(t);
255            } else {
256                media_types.push('*');
257            }
258
259            for param in &media_type.params {
260                media_types.push(';');
261                if let Some(ref value) = param.1 {
262                    write!(&mut media_types, "{}={}", param.0, value).unwrap();
263                } else {
264                    media_types.push_str(&param.0);
265                }
266            }
267        }
268
269        headers.append(ACCEPT, media_types);
270    }
271}