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}