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}