parse_net/owned.rs
1//! Owned forms (`alloc`): for callers that keep a parsed value beyond the
2//! input buffer. Each copies the view's bytes into one buffer (`to_owned`)
3//! and gives the same view back (`as_view`), so every accessor works on
4//! both. parse-net never uses them itself.
5//!
6//! # Examples
7//!
8//! ```
9//! use parse_net::{parse_media_type, OwnedMediaType};
10//!
11//! let owned: OwnedMediaType = {
12//! let input = b"text/html; charset=utf-8".to_vec();
13//! parse_media_type(&input)?.to_owned()
14//! };
15//! assert!(owned.as_view().is("text", "html"));
16//! assert!(owned.as_view().charset().is_some_and(|c| c.eq_bytes(b"utf-8")));
17//! # Ok::<(), parse_net::ParseError>(())
18//! ```
19
20use alloc::vec::Vec;
21use core::net::{Ipv4Addr, Ipv6Addr};
22
23use crate::{
24 etag::EntityTag,
25 host::{Host, HostPort},
26 limits::DuplicatePolicy,
27 media::MediaType,
28 param::Params,
29};
30
31/// An owned [`MediaType`].
32///
33/// # Examples
34///
35/// ```
36/// let owned: parse_net::OwnedMediaType = parse_net::parse_media_type(b"image/png")?.to_owned();
37/// assert!(owned.as_view().is("image", "png"));
38/// # Ok::<(), parse_net::ParseError>(())
39/// ```
40///
41/// `Debug` prints what the view prints: parameter values can be secrets.
42#[derive(Clone, PartialEq, Eq, Hash)]
43pub struct OwnedMediaType {
44 /// The type, the subtype, then the parameter text.
45 buf: Vec<u8>,
46 type_len: usize,
47 subtype_len: usize,
48 count: u32,
49 policy: DuplicatePolicy,
50}
51
52impl core::fmt::Debug for OwnedMediaType {
53 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
54 core::fmt::Debug::fmt(&self.as_view(), f)
55 }
56}
57
58impl MediaType<'_> {
59 /// Copies the media type into an [`OwnedMediaType`] (one allocation).
60 ///
61 /// # Examples
62 ///
63 /// ```
64 /// let input = b"application/json; charset=utf-8".to_vec();
65 /// let owned = parse_net::parse_media_type(&input)?.to_owned();
66 /// drop(input);
67 /// assert!(owned.as_view().is("application", "json"));
68 /// # Ok::<(), parse_net::ParseError>(())
69 /// ```
70 #[must_use]
71 pub fn to_owned(&self) -> OwnedMediaType {
72 let mut buf =
73 Vec::with_capacity(self.type_.len() + self.subtype.len() + self.params.text.len());
74 buf.extend_from_slice(self.type_);
75 buf.extend_from_slice(self.subtype);
76 buf.extend_from_slice(self.params.text);
77 OwnedMediaType {
78 buf,
79 type_len: self.type_.len(),
80 subtype_len: self.subtype.len(),
81 count: self.params.count,
82 policy: self.params.policy,
83 }
84 }
85}
86
87impl OwnedMediaType {
88 /// The view over the owned bytes.
89 ///
90 /// # Examples
91 ///
92 /// ```
93 /// let m = parse_net::parse_media_type(b"text/plain; format=flowed")?;
94 /// assert_eq!(m.to_owned().as_view(), m);
95 /// # Ok::<(), parse_net::ParseError>(())
96 /// ```
97 #[must_use]
98 pub fn as_view(&self) -> MediaType<'_> {
99 let (type_, rest) = self.buf.split_at(self.type_len.min(self.buf.len()));
100 let (subtype, text) = rest.split_at(self.subtype_len.min(rest.len()));
101 MediaType {
102 type_,
103 subtype,
104 params: Params {
105 text,
106 count: self.count,
107 policy: self.policy,
108 },
109 }
110 }
111}
112
113/// An owned [`EntityTag`].
114///
115/// # Examples
116///
117/// ```
118/// use parse_net::EntityTag;
119///
120/// let owned = EntityTag::parse(b"W/\"v1\"")?.to_owned();
121/// assert!(owned.as_view().is_weak() && owned.as_view().opaque() == b"v1");
122/// # Ok::<(), parse_net::ParseError>(())
123/// ```
124#[derive(Clone, Debug, PartialEq, Eq, Hash)]
125pub struct OwnedEntityTag {
126 weak: bool,
127 opaque: Vec<u8>,
128}
129
130impl EntityTag<'_> {
131 /// Copies the tag into an [`OwnedEntityTag`] (one allocation).
132 ///
133 /// # Examples
134 ///
135 /// ```
136 /// let input = b"\"abc\"".to_vec();
137 /// let owned = parse_net::EntityTag::parse(&input)?.to_owned();
138 /// drop(input);
139 /// assert!(!owned.as_view().is_weak() && owned.as_view().opaque() == b"abc");
140 /// # Ok::<(), parse_net::ParseError>(())
141 /// ```
142 #[must_use]
143 pub fn to_owned(&self) -> OwnedEntityTag {
144 OwnedEntityTag {
145 weak: self.weak,
146 opaque: self.opaque.to_vec(),
147 }
148 }
149}
150
151impl OwnedEntityTag {
152 /// The view over the owned bytes.
153 ///
154 /// # Examples
155 ///
156 /// ```
157 /// let tag = parse_net::EntityTag::parse(b"W/\"x\"")?;
158 /// assert_eq!(tag.to_owned().as_view(), tag);
159 /// # Ok::<(), parse_net::ParseError>(())
160 /// ```
161 #[must_use]
162 pub fn as_view(&self) -> EntityTag<'_> {
163 EntityTag {
164 weak: self.weak,
165 opaque: &self.opaque,
166 }
167 }
168}
169
170/// The host of an [`OwnedHostPort`].
171#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
172enum Kind {
173 Name,
174 V4(Ipv4Addr),
175 V6(Ipv6Addr),
176 Future,
177}
178
179/// An owned [`HostPort`].
180///
181/// # Examples
182///
183/// ```
184/// use parse_net::parse_host_port;
185///
186/// let owned = parse_host_port(b"Example.COM:8080")?.to_owned();
187/// assert_eq!(owned.as_view().port, Some(8080));
188/// assert!(owned.as_view().host.eq_ignore_ascii_case(&parse_host_port(b"example.com")?.host));
189/// # Ok::<(), parse_net::ParseError>(())
190/// ```
191#[derive(Clone, Debug, PartialEq, Eq, Hash)]
192pub struct OwnedHostPort {
193 kind: Kind,
194 /// The name or IPvFuture text; empty for an address.
195 text: Vec<u8>,
196 port: Option<u16>,
197 empty_port: bool,
198}
199
200impl HostPort<'_> {
201 /// Copies the value into an [`OwnedHostPort`] (one allocation for a name,
202 /// none for an address).
203 ///
204 /// # Examples
205 ///
206 /// ```
207 /// use parse_net::{parse_host_port, Host};
208 /// use std::net::Ipv4Addr;
209 ///
210 /// let owned = parse_host_port(b"192.0.2.1:443")?.to_owned();
211 /// assert_eq!(owned.as_view().port, Some(443));
212 /// assert_eq!(owned.as_view().host, Host::V4(Ipv4Addr::new(192, 0, 2, 1)));
213 /// # Ok::<(), parse_net::ParseError>(())
214 /// ```
215 #[must_use]
216 pub fn to_owned(&self) -> OwnedHostPort {
217 let (kind, text) = match self.host {
218 Host::Name(n) => (Kind::Name, n.to_vec()),
219 Host::V4(a) => (Kind::V4(a), Vec::new()),
220 Host::V6(a) => (Kind::V6(a), Vec::new()),
221 Host::Future(f) => (Kind::Future, f.to_vec()),
222 };
223 OwnedHostPort {
224 kind,
225 text,
226 port: self.port,
227 empty_port: self.empty_port,
228 }
229 }
230}
231
232impl OwnedHostPort {
233 /// The view over the owned bytes.
234 ///
235 /// # Examples
236 ///
237 /// ```
238 /// let hp = parse_net::parse_host_port(b"[::1]:8080")?;
239 /// assert_eq!(hp.to_owned().as_view(), hp);
240 /// # Ok::<(), parse_net::ParseError>(())
241 /// ```
242 #[must_use]
243 pub fn as_view(&self) -> HostPort<'_> {
244 let host = match self.kind {
245 Kind::Name => Host::Name(&self.text),
246 Kind::V4(a) => Host::V4(a),
247 Kind::V6(a) => Host::V6(a),
248 Kind::Future => Host::Future(&self.text),
249 };
250 HostPort {
251 host,
252 port: self.port,
253 empty_port: self.empty_port,
254 }
255 }
256}
257
258#[cfg(test)]
259mod tests {
260 use super::*;
261
262 #[test]
263 fn test_owned_forms_give_the_same_view() {
264 for v in [
265 &b"text/html"[..],
266 b"multipart/form-data; boundary=\"a b\"; x=1",
267 b"a/b;",
268 ] {
269 let m = crate::parse_media_type(v).unwrap();
270 assert_eq!(m.to_owned().as_view(), m);
271 }
272 for v in [&b"\"x\""[..], b"W/\"\"", b"\"\xff\""] {
273 let t = EntityTag::parse(v).unwrap();
274 assert_eq!(t.to_owned().as_view(), t);
275 }
276 for v in [
277 &b"a.example:1"[..],
278 b"[::1]",
279 b"192.0.2.1",
280 b"[v1.x]",
281 b"h:",
282 ] {
283 let h = crate::parse_host_port(v).unwrap();
284 assert_eq!(h.to_owned().as_view(), h);
285 }
286 }
287
288 #[test]
289 fn test_owned_outlives_the_input() {
290 let owned = {
291 let input = b"Text/Plain; Charset=\"UTF-8\"".to_vec();
292 crate::parse_media_type(&input).unwrap().to_owned()
293 };
294 let v = owned.as_view();
295 assert!(v.is("text", "plain") && v.charset().unwrap().eq_ignore_ascii_case(b"utf-8"));
296 assert_eq!(v.params().len(), 1);
297 }
298}