Skip to main content

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}