cookie/builder.rs
1use std::borrow::{Cow, Borrow, BorrowMut};
2
3use crate::{Cookie, SameSite, Expiration};
4
5/// Structure that follows the builder pattern for building `Cookie` structs.
6///
7/// To construct a cookie:
8///
9/// 1. Call [`Cookie::build()`] to start building.
10/// 2. Use any of the builder methods to set fields in the cookie.
11///
12/// The resulting `CookieBuilder` can be passed directly into methods expecting
13/// a `T: Into<Cookie>`:
14///
15/// ```rust
16/// use cookie::{Cookie, CookieJar};
17///
18/// let mut jar = CookieJar::new();
19/// jar.add(Cookie::build(("key", "value")).secure(true).path("/"));
20/// jar.remove(Cookie::build("key").path("/"));
21/// ```
22///
23/// You can also call [`CookieBuilder::build()`] directly to get a `Cookie`:
24///
25/// ```rust
26/// use cookie::Cookie;
27/// use cookie::time::Duration;
28///
29/// let cookie: Cookie = Cookie::build(("name", "value"))
30/// .domain("www.rust-lang.org")
31/// .path("/")
32/// .secure(true)
33/// .http_only(true)
34/// .max_age(Duration::days(1))
35/// .build();
36/// ```
37#[derive(Debug, Clone, PartialEq)]
38pub struct CookieBuilder<'c> {
39 /// The cookie being built.
40 cookie: Cookie<'c>,
41}
42
43impl<'c> CookieBuilder<'c> {
44 /// Creates a new `CookieBuilder` instance from the given name and value.
45 ///
46 /// This method is typically called indirectly via [`Cookie::build()`].
47 ///
48 /// # Example
49 ///
50 /// ```rust
51 /// use cookie::Cookie;
52 ///
53 /// // These two snippets are equivalent:
54 ///
55 /// let c = Cookie::build(("foo", "bar"));
56 /// assert_eq!(c.inner().name_value(), ("foo", "bar"));
57 ///
58 /// let c = Cookie::new("foo", "bar");
59 /// assert_eq!(c.name_value(), ("foo", "bar"));
60 /// ```
61 pub fn new<N, V>(name: N, value: V) -> Self
62 where N: Into<Cow<'c, str>>,
63 V: Into<Cow<'c, str>>
64 {
65 CookieBuilder { cookie: Cookie::new(name, value) }
66 }
67
68 /// Sets the `expires` field in the cookie being built.
69 ///
70 /// See [`Expiration`] for conversions.
71 ///
72 /// # Example
73 ///
74 /// ```rust
75 /// # extern crate cookie;
76 /// use cookie::{Cookie, Expiration};
77 /// use cookie::time::OffsetDateTime;
78 ///
79 /// # fn main() {
80 /// let c = Cookie::build(("foo", "bar")).expires(OffsetDateTime::now_utc());
81 /// assert!(c.inner().expires().is_some());
82 ///
83 /// let c = Cookie::build(("foo", "bar")).expires(None);
84 /// assert_eq!(c.inner().expires(), Some(Expiration::Session));
85 /// # }
86 /// ```
87 #[inline]
88 pub fn expires<E: Into<Expiration>>(mut self, when: E) -> Self {
89 self.cookie.set_expires(when);
90 self
91 }
92
93 /// Sets the `max_age` field in the cookie being built.
94 ///
95 /// # Example
96 ///
97 /// ```rust
98 /// use cookie::Cookie;
99 /// use cookie::time::Duration;
100 ///
101 /// let c = Cookie::build(("foo", "bar")).max_age(Duration::minutes(30));
102 /// assert_eq!(c.inner().max_age(), Some(Duration::seconds(30 * 60)));
103 /// ```
104 #[inline]
105 pub fn max_age(mut self, value: time::Duration) -> Self {
106 self.cookie.set_max_age(value);
107 self
108 }
109
110 /// Sets the `domain` field in the cookie being built.
111 ///
112 /// # Example
113 ///
114 /// ```rust
115 /// use cookie::Cookie;
116 ///
117 /// let c = Cookie::build(("foo", "bar")).domain("www.rust-lang.org");
118 /// assert_eq!(c.inner().domain(), Some("www.rust-lang.org"));
119 /// ```
120 pub fn domain<D: Into<Cow<'c, str>>>(mut self, value: D) -> Self {
121 self.cookie.set_domain(value);
122 self
123 }
124
125 /// Sets the `path` field in the cookie being built.
126 ///
127 /// # Example
128 ///
129 /// ```rust
130 /// use cookie::Cookie;
131 ///
132 /// let c = Cookie::build(("foo", "bar")).path("/");
133 /// assert_eq!(c.inner().path(), Some("/"));
134 /// ```
135 pub fn path<P: Into<Cow<'c, str>>>(mut self, path: P) -> Self {
136 self.cookie.set_path(path);
137 self
138 }
139
140 /// Sets the `secure` field in the cookie being built.
141 ///
142 /// # Example
143 ///
144 /// ```rust
145 /// use cookie::Cookie;
146 ///
147 /// let c = Cookie::build(("foo", "bar")).secure(true);
148 /// assert_eq!(c.inner().secure(), Some(true));
149 /// ```
150 #[inline]
151 pub fn secure(mut self, value: bool) -> Self {
152 self.cookie.set_secure(value);
153 self
154 }
155
156 /// Sets the `http_only` field in the cookie being built.
157 ///
158 /// # Example
159 ///
160 /// ```rust
161 /// use cookie::Cookie;
162 ///
163 /// let c = Cookie::build(("foo", "bar")).http_only(true);
164 /// assert_eq!(c.inner().http_only(), Some(true));
165 /// ```
166 #[inline]
167 pub fn http_only(mut self, value: bool) -> Self {
168 self.cookie.set_http_only(value);
169 self
170 }
171
172 /// Sets the `same_site` field in the cookie being built.
173 ///
174 /// # Example
175 ///
176 /// ```rust
177 /// use cookie::{Cookie, SameSite};
178 ///
179 /// let c = Cookie::build(("foo", "bar")).same_site(SameSite::Strict);
180 /// assert_eq!(c.inner().same_site(), Some(SameSite::Strict));
181 /// ```
182 #[inline]
183 pub fn same_site(mut self, value: SameSite) -> Self {
184 self.cookie.set_same_site(value);
185 self
186 }
187
188 /// Sets the `partitioned` field in the cookie being built.
189 ///
190 /// **Note:** _Partitioned_ cookies require the `Secure` attribute to be
191 /// set. As such, `Partitioned` cookies are always rendered with the
192 /// `Secure` attribute, irrespective of the `Secure` attribute's setting.
193 ///
194 /// **Note:** This cookie attribute is an [HTTP draft]! Its meaning and
195 /// definition are not standardized and therefore subject to change.
196 ///
197 /// [HTTP draft]: https://www.ietf.org/id/draft-cutler-httpbis-partitioned-cookies-01.html
198 ///
199 /// # Example
200 ///
201 /// ```rust
202 /// use cookie::Cookie;
203 ///
204 /// let c = Cookie::build(("foo", "bar")).partitioned(true);
205 /// assert_eq!(c.inner().partitioned(), Some(true));
206 /// assert!(c.to_string().contains("Secure"));
207 /// ```
208 #[inline]
209 pub fn partitioned(mut self, value: bool) -> Self {
210 self.cookie.set_partitioned(value);
211 self
212 }
213
214 /// Makes the cookie being built 'permanent' by extending its expiration and
215 /// max age 20 years into the future. See also [`Cookie::make_permanent()`].
216 ///
217 /// # Example
218 ///
219 /// ```rust
220 /// # extern crate cookie;
221 /// use cookie::Cookie;
222 /// use cookie::time::Duration;
223 ///
224 /// # fn main() {
225 /// let c = Cookie::build(("foo", "bar")).permanent();
226 /// assert_eq!(c.inner().max_age(), Some(Duration::days(365 * 20)));
227 /// # assert!(c.inner().expires().is_some());
228 /// # }
229 /// ```
230 #[inline]
231 pub fn permanent(mut self) -> Self {
232 self.cookie.make_permanent();
233 self
234 }
235
236 /// Makes the cookie being built 'removal' by clearing its value, setting a
237 /// max-age of `0`, and setting an expiration date far in the past. See also
238 /// [`Cookie::make_removal()`].
239 ///
240 /// # Example
241 ///
242 /// ```rust
243 /// # extern crate cookie;
244 /// use cookie::Cookie;
245 /// use cookie::time::Duration;
246 ///
247 /// # fn main() {
248 /// let mut builder = Cookie::build("foo").removal();
249 /// assert_eq!(builder.inner().max_age(), Some(Duration::ZERO));
250 ///
251 /// let mut builder = Cookie::build(("name", "value")).removal();
252 /// assert_eq!(builder.inner().value(), "");
253 /// assert_eq!(builder.inner().max_age(), Some(Duration::ZERO));
254 /// # }
255 /// ```
256 #[inline]
257 pub fn removal(mut self) -> Self {
258 self.cookie.make_removal();
259 self
260 }
261
262 /// Returns a borrow to the cookie currently being built.
263 ///
264 /// # Example
265 ///
266 /// ```rust
267 /// use cookie::Cookie;
268 ///
269 /// let builder = Cookie::build(("name", "value"))
270 /// .domain("www.rust-lang.org")
271 /// .path("/")
272 /// .http_only(true);
273 ///
274 /// assert_eq!(builder.inner().name_value(), ("name", "value"));
275 /// assert_eq!(builder.inner().domain(), Some("www.rust-lang.org"));
276 /// assert_eq!(builder.inner().path(), Some("/"));
277 /// assert_eq!(builder.inner().http_only(), Some(true));
278 /// assert_eq!(builder.inner().secure(), None);
279 /// ```
280 #[inline]
281 pub fn inner(&self) -> &Cookie<'c> {
282 &self.cookie
283 }
284
285 /// Returns a mutable borrow to the cookie currently being built.
286 ///
287 /// # Example
288 ///
289 /// ```rust
290 /// use cookie::Cookie;
291 ///
292 /// let mut builder = Cookie::build(("name", "value"))
293 /// .domain("www.rust-lang.org")
294 /// .path("/")
295 /// .http_only(true);
296 ///
297 /// assert_eq!(builder.inner().http_only(), Some(true));
298 ///
299 /// builder.inner_mut().set_http_only(false);
300 /// assert_eq!(builder.inner().http_only(), Some(false));
301 /// ```
302 #[inline]
303 pub fn inner_mut(&mut self) -> &mut Cookie<'c> {
304 &mut self.cookie
305 }
306
307 /// Finishes building and returns the built `Cookie`.
308 ///
309 /// This method usually does not need to be called directly. This is because
310 /// `CookieBuilder` implements `Into<Cookie>`, so a value of `CookieBuilder`
311 /// can be passed directly into any method that expects a `C: Into<Cookie>`.
312 ///
313 /// # Example
314 ///
315 /// ```rust
316 /// use cookie::{Cookie, CookieJar};
317 ///
318 /// // We don't usually need to use `build()`. Inspect with `inner()`, and
319 /// // pass the builder directly into methods expecting `T: Into<Cookie>`.
320 /// let c = Cookie::build(("foo", "bar"))
321 /// .domain("crates.io")
322 /// .path("/");
323 ///
324 /// // Use `inner()` and inspect the cookie.
325 /// assert_eq!(c.inner().name_value(), ("foo", "bar"));
326 /// assert_eq!(c.inner().domain(), Some("crates.io"));
327 /// assert_eq!(c.inner().path(), Some("/"));
328 ///
329 /// // Add the cookie to a jar. Note the automatic conversion.
330 /// CookieJar::new().add(c);
331 ///
332 /// // We could use `build()` to get a `Cookie` when needed.
333 /// let c = Cookie::build(("foo", "bar"))
334 /// .domain("crates.io")
335 /// .path("/")
336 /// .build();
337 ///
338 /// // Inspect the built cookie.
339 /// assert_eq!(c.name_value(), ("foo", "bar"));
340 /// assert_eq!(c.domain(), Some("crates.io"));
341 /// assert_eq!(c.path(), Some("/"));
342 ///
343 /// // Add the cookie to a jar.
344 /// CookieJar::new().add(c);
345 /// ```
346 #[inline]
347 pub fn build(self) -> Cookie<'c> {
348 self.cookie
349 }
350
351 /// Deprecated. Convert `self` into a `Cookie`.
352 ///
353 /// Instead of using this method, pass a `CookieBuilder` directly into
354 /// methods expecting a `T: Into<Cookie>`. For other cases, use
355 /// [`CookieBuilder::build()`].
356 #[deprecated(since="0.18.0", note="`CookieBuilder` can be passed in to methods expecting a `Cookie`; for other cases, use `CookieBuilder::build()`")]
357 pub fn finish(self) -> Cookie<'c> {
358 self.cookie
359 }
360}
361
362impl std::fmt::Display for CookieBuilder<'_> {
363 #[inline(always)]
364 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
365 self.cookie.fmt(f)
366 }
367}
368
369// NOTE: We don't implement `Deref` or `DerefMut` because there are tons of name
370// collisions with builder methods.
371impl<'a> Borrow<Cookie<'a>> for CookieBuilder<'a> {
372 fn borrow(&self) -> &Cookie<'a> {
373 &self.cookie
374 }
375}
376
377impl<'a> BorrowMut<Cookie<'a>> for CookieBuilder<'a> {
378 fn borrow_mut(&mut self) -> &mut Cookie<'a> {
379 &mut self.cookie
380 }
381}
382
383impl<'a> AsRef<Cookie<'a>> for CookieBuilder<'a> {
384 fn as_ref(&self) -> &Cookie<'a> {
385 &self.cookie
386 }
387}
388
389impl<'a> AsMut<Cookie<'a>> for CookieBuilder<'a> {
390 fn as_mut(&mut self) -> &mut Cookie<'a> {
391 &mut self.cookie
392 }
393}
394
395impl<'a, 'b> PartialEq<Cookie<'b>> for CookieBuilder<'a> {
396 fn eq(&self, other: &Cookie<'b>) -> bool {
397 &self.cookie == other
398 }
399}
400
401impl<'a, 'b> PartialEq<CookieBuilder<'b>> for Cookie<'a> {
402 fn eq(&self, other: &CookieBuilder<'b>) -> bool {
403 self == &other.cookie
404 }
405}
406
407impl<'c> From<Cookie<'c>> for CookieBuilder<'c> {
408 fn from(cookie: Cookie<'c>) -> Self {
409 CookieBuilder { cookie }
410 }
411}