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