cookie/
jar.rs

1use std::collections::HashSet;
2
3#[cfg(feature = "signed")] use crate::secure::SignedJar;
4#[cfg(feature = "private")] use crate::secure::PrivateJar;
5#[cfg(any(feature = "signed", feature = "private"))] use crate::secure::Key;
6
7use crate::delta::DeltaCookie;
8use crate::Cookie;
9
10/// A collection of cookies that tracks its modifications.
11///
12/// A `CookieJar` provides storage for any number of cookies. Any changes made
13/// to the jar are tracked; the changes can be retrieved via the
14/// [delta](#method.delta) method which returns an iterator over the changes.
15///
16/// # Usage
17///
18/// A jar's life begins via [new](#method.new) and calls to
19/// [`add_original`](#method.add_original):
20///
21/// ```rust
22/// use cookie::{Cookie, CookieJar};
23///
24/// let mut jar = CookieJar::new();
25/// jar.add_original(Cookie::new("name", "value"));
26/// jar.add_original(Cookie::new("second", "another"));
27/// ```
28///
29/// Cookies can be added via [add](#method.add) and removed via
30/// [remove](#method.remove). Finally, cookies can be looked up via
31/// [get](#method.get):
32///
33/// ```rust
34/// # use cookie::{Cookie, CookieJar};
35/// let mut jar = CookieJar::new();
36/// jar.add(Cookie::new("a", "one"));
37/// jar.add(Cookie::new("b", "two"));
38///
39/// assert_eq!(jar.get("a").map(|c| c.value()), Some("one"));
40/// assert_eq!(jar.get("b").map(|c| c.value()), Some("two"));
41///
42/// jar.remove(Cookie::named("b"));
43/// assert!(jar.get("b").is_none());
44/// ```
45///
46/// # Deltas
47///
48/// A jar keeps track of any modifications made to it over time. The
49/// modifications are recorded as cookies. The modifications can be retrieved
50/// via [delta](#method.delta). Any new `Cookie` added to a jar via `add`
51/// results in the same `Cookie` appearing in the `delta`; cookies added via
52/// `add_original` do not count towards the delta. Any _original_ cookie that is
53/// removed from a jar results in a "removal" cookie appearing in the delta. A
54/// "removal" cookie is a cookie that a server sends so that the cookie is
55/// removed from the client's machine.
56///
57/// Deltas are typically used to create `Set-Cookie` headers corresponding to
58/// the changes made to a cookie jar over a period of time.
59///
60/// ```rust
61/// # use cookie::{Cookie, CookieJar};
62/// let mut jar = CookieJar::new();
63///
64/// // original cookies don't affect the delta
65/// jar.add_original(Cookie::new("original", "value"));
66/// assert_eq!(jar.delta().count(), 0);
67///
68/// // new cookies result in an equivalent `Cookie` in the delta
69/// jar.add(Cookie::new("a", "one"));
70/// jar.add(Cookie::new("b", "two"));
71/// assert_eq!(jar.delta().count(), 2);
72///
73/// // removing an original cookie adds a "removal" cookie to the delta
74/// jar.remove(Cookie::named("original"));
75/// assert_eq!(jar.delta().count(), 3);
76///
77/// // removing a new cookie that was added removes that `Cookie` from the delta
78/// jar.remove(Cookie::named("a"));
79/// assert_eq!(jar.delta().count(), 2);
80/// ```
81#[derive(Default, Debug, Clone)]
82pub struct CookieJar {
83    original_cookies: HashSet<DeltaCookie>,
84    delta_cookies: HashSet<DeltaCookie>,
85}
86
87impl CookieJar {
88    /// Creates an empty cookie jar.
89    ///
90    /// # Example
91    ///
92    /// ```rust
93    /// use cookie::CookieJar;
94    ///
95    /// let jar = CookieJar::new();
96    /// assert_eq!(jar.iter().count(), 0);
97    /// ```
98    pub fn new() -> CookieJar {
99        CookieJar::default()
100    }
101
102    /// Returns a reference to the `Cookie` inside this jar with the name
103    /// `name`. If no such cookie exists, returns `None`.
104    ///
105    /// # Example
106    ///
107    /// ```rust
108    /// use cookie::{CookieJar, Cookie};
109    ///
110    /// let mut jar = CookieJar::new();
111    /// assert!(jar.get("name").is_none());
112    ///
113    /// jar.add(Cookie::new("name", "value"));
114    /// assert_eq!(jar.get("name").map(|c| c.value()), Some("value"));
115    /// ```
116    pub fn get(&self, name: &str) -> Option<&Cookie<'static>> {
117        self.delta_cookies
118            .get(name)
119            .or_else(|| self.original_cookies.get(name))
120            .and_then(|c| if c.removed { None } else { Some(&c.cookie) })
121    }
122
123    /// Adds an "original" `cookie` to this jar. If an original cookie with the
124    /// same name already exists, it is replaced with `cookie`. Cookies added
125    /// with `add` take precedence and are not replaced by this method.
126    ///
127    /// Adding an original cookie does not affect the [delta](#method.delta)
128    /// computation. This method is intended to be used to seed the cookie jar
129    /// with cookies received from a client's HTTP message.
130    ///
131    /// For accurate `delta` computations, this method should not be called
132    /// after calling `remove`.
133    ///
134    /// # Example
135    ///
136    /// ```rust
137    /// use cookie::{CookieJar, Cookie};
138    ///
139    /// let mut jar = CookieJar::new();
140    /// jar.add_original(Cookie::new("name", "value"));
141    /// jar.add_original(Cookie::new("second", "two"));
142    ///
143    /// assert_eq!(jar.get("name").map(|c| c.value()), Some("value"));
144    /// assert_eq!(jar.get("second").map(|c| c.value()), Some("two"));
145    /// assert_eq!(jar.iter().count(), 2);
146    /// assert_eq!(jar.delta().count(), 0);
147    /// ```
148    pub fn add_original(&mut self, cookie: Cookie<'static>) {
149        self.original_cookies.replace(DeltaCookie::added(cookie));
150    }
151
152    /// Adds `cookie` to this jar. If a cookie with the same name already
153    /// exists, it is replaced with `cookie`.
154    ///
155    /// # Example
156    ///
157    /// ```rust
158    /// use cookie::{CookieJar, Cookie};
159    ///
160    /// let mut jar = CookieJar::new();
161    /// jar.add(Cookie::new("name", "value"));
162    /// jar.add(Cookie::new("second", "two"));
163    ///
164    /// assert_eq!(jar.get("name").map(|c| c.value()), Some("value"));
165    /// assert_eq!(jar.get("second").map(|c| c.value()), Some("two"));
166    /// assert_eq!(jar.iter().count(), 2);
167    /// assert_eq!(jar.delta().count(), 2);
168    /// ```
169    pub fn add(&mut self, cookie: Cookie<'static>) {
170        self.delta_cookies.replace(DeltaCookie::added(cookie));
171    }
172
173    /// Removes `cookie` from this jar. If an _original_ cookie with the same
174    /// name as `cookie` is present in the jar, a _removal_ cookie will be
175    /// present in the `delta` computation. To properly generate the removal
176    /// cookie, `cookie` must contain the same `path` and `domain` as the cookie
177    /// that was initially set.
178    ///
179    /// A "removal" cookie is a cookie that has the same name as the original
180    /// cookie but has an empty value, a max-age of 0, and an expiration date
181    /// far in the past. See also [`Cookie::make_removal()`].
182    ///
183    /// # Example
184    ///
185    /// Removing an _original_ cookie results in a _removal_ cookie:
186    ///
187    /// ```rust
188    /// # extern crate cookie;
189    /// extern crate time;
190    ///
191    /// use cookie::{CookieJar, Cookie};
192    /// use time::Duration;
193    ///
194    /// # fn main() {
195    /// let mut jar = CookieJar::new();
196    ///
197    /// // Assume this cookie originally had a path of "/" and domain of "a.b".
198    /// jar.add_original(Cookie::new("name", "value"));
199    ///
200    /// // If the path and domain were set, they must be provided to `remove`.
201    /// jar.remove(Cookie::build("name", "").path("/").domain("a.b").finish());
202    ///
203    /// // The delta will contain the removal cookie.
204    /// let delta: Vec<_> = jar.delta().collect();
205    /// assert_eq!(delta.len(), 1);
206    /// assert_eq!(delta[0].name(), "name");
207    /// assert_eq!(delta[0].max_age(), Some(Duration::seconds(0)));
208    /// # }
209    /// ```
210    ///
211    /// Removing a new cookie does not result in a _removal_ cookie unless
212    /// there's an original cookie with the same name:
213    ///
214    /// ```rust
215    /// use cookie::{CookieJar, Cookie};
216    ///
217    /// let mut jar = CookieJar::new();
218    /// jar.add(Cookie::new("name", "value"));
219    /// assert_eq!(jar.delta().count(), 1);
220    ///
221    /// jar.remove(Cookie::named("name"));
222    /// assert_eq!(jar.delta().count(), 0);
223    ///
224    /// jar.add_original(Cookie::new("name", "value"));
225    /// jar.add(Cookie::new("name", "value"));
226    /// assert_eq!(jar.delta().count(), 1);
227    ///
228    /// jar.remove(Cookie::named("name"));
229    /// assert_eq!(jar.delta().count(), 1);
230    /// ```
231    pub fn remove(&mut self, mut cookie: Cookie<'static>) {
232        if self.original_cookies.contains(cookie.name()) {
233            cookie.make_removal();
234            self.delta_cookies.replace(DeltaCookie::removed(cookie));
235        } else {
236            self.delta_cookies.remove(cookie.name());
237        }
238    }
239
240    /// Removes `cookie` from this jar completely. This method differs from
241    /// `remove` in that no delta cookie is created under any condition. Neither
242    /// the `delta` nor `iter` methods will return a cookie that is removed
243    /// using this method.
244    ///
245    /// # Example
246    ///
247    /// Removing an _original_ cookie; no _removal_ cookie is generated:
248    ///
249    /// ```rust
250    /// # extern crate cookie;
251    /// extern crate time;
252    ///
253    /// use cookie::{CookieJar, Cookie};
254    /// use time::Duration;
255    ///
256    /// # fn main() {
257    /// let mut jar = CookieJar::new();
258    ///
259    /// // Add an original cookie and a new cookie.
260    /// jar.add_original(Cookie::new("name", "value"));
261    /// jar.add(Cookie::new("key", "value"));
262    /// assert_eq!(jar.delta().count(), 1);
263    /// assert_eq!(jar.iter().count(), 2);
264    ///
265    /// // Now force remove the original cookie.
266    /// jar.force_remove(&Cookie::named("name"));
267    /// assert_eq!(jar.delta().count(), 1);
268    /// assert_eq!(jar.iter().count(), 1);
269    ///
270    /// // Now force remove the new cookie.
271    /// jar.force_remove(&Cookie::named("key"));
272    /// assert_eq!(jar.delta().count(), 0);
273    /// assert_eq!(jar.iter().count(), 0);
274    /// # }
275    /// ```
276    pub fn force_remove<'a>(&mut self, cookie: &Cookie<'a>) {
277        self.original_cookies.remove(cookie.name());
278        self.delta_cookies.remove(cookie.name());
279    }
280
281    /// Removes all delta cookies, i.e. all cookies not added via
282    /// [`CookieJar::add_original()`], from this `CookieJar`. This undoes any
283    /// changes from [`CookieJar::add()`] and [`CookieJar::remove()`]
284    /// operations.
285    ///
286    /// # Example
287    ///
288    /// ```rust
289    /// use cookie::{CookieJar, Cookie};
290    ///
291    /// let mut jar = CookieJar::new();
292    ///
293    /// // Only original cookies will remain after calling `reset_delta`.
294    /// jar.add_original(Cookie::new("name", "value"));
295    /// jar.add_original(Cookie::new("language", "Rust"));
296    ///
297    /// // These operations, represented by delta cookies, will be reset.
298    /// jar.add(Cookie::new("language", "C++"));
299    /// jar.remove(Cookie::named("name"));
300    ///
301    /// // All is normal.
302    /// assert_eq!(jar.get("name"), None);
303    /// assert_eq!(jar.get("language").map(Cookie::value), Some("C++"));
304    /// assert_eq!(jar.iter().count(), 1);
305    /// assert_eq!(jar.delta().count(), 2);
306    ///
307    /// // Resetting undoes delta operations.
308    /// jar.reset_delta();
309    /// assert_eq!(jar.get("name").map(Cookie::value), Some("value"));
310    /// assert_eq!(jar.get("language").map(Cookie::value), Some("Rust"));
311    /// assert_eq!(jar.iter().count(), 2);
312    /// assert_eq!(jar.delta().count(), 0);
313    /// ```
314    pub fn reset_delta(&mut self) {
315        self.delta_cookies = HashSet::new();
316    }
317
318    /// Returns an iterator over cookies that represent the changes to this jar
319    /// over time. These cookies can be rendered directly as `Set-Cookie` header
320    /// values to affect the changes made to this jar on the client.
321    ///
322    /// # Example
323    ///
324    /// ```rust
325    /// use cookie::{CookieJar, Cookie};
326    ///
327    /// let mut jar = CookieJar::new();
328    /// jar.add_original(Cookie::new("name", "value"));
329    /// jar.add_original(Cookie::new("second", "two"));
330    ///
331    /// // Add new cookies.
332    /// jar.add(Cookie::new("new", "third"));
333    /// jar.add(Cookie::new("another", "fourth"));
334    /// jar.add(Cookie::new("yac", "fifth"));
335    ///
336    /// // Remove some cookies.
337    /// jar.remove(Cookie::named("name"));
338    /// jar.remove(Cookie::named("another"));
339    ///
340    /// // Delta contains two new cookies ("new", "yac") and a removal ("name").
341    /// assert_eq!(jar.delta().count(), 3);
342    /// ```
343    pub fn delta(&self) -> Delta {
344        Delta { iter: self.delta_cookies.iter() }
345    }
346
347    /// Returns an iterator over all of the cookies present in this jar.
348    ///
349    /// # Example
350    ///
351    /// ```rust
352    /// use cookie::{CookieJar, Cookie};
353    ///
354    /// let mut jar = CookieJar::new();
355    ///
356    /// jar.add_original(Cookie::new("name", "value"));
357    /// jar.add_original(Cookie::new("second", "two"));
358    ///
359    /// jar.add(Cookie::new("new", "third"));
360    /// jar.add(Cookie::new("another", "fourth"));
361    /// jar.add(Cookie::new("yac", "fifth"));
362    ///
363    /// jar.remove(Cookie::named("name"));
364    /// jar.remove(Cookie::named("another"));
365    ///
366    /// // There are three cookies in the jar: "second", "new", and "yac".
367    /// # assert_eq!(jar.iter().count(), 3);
368    /// for cookie in jar.iter() {
369    ///     match cookie.name() {
370    ///         "second" => assert_eq!(cookie.value(), "two"),
371    ///         "new" => assert_eq!(cookie.value(), "third"),
372    ///         "yac" => assert_eq!(cookie.value(), "fifth"),
373    ///         _ => unreachable!("there are only three cookies in the jar")
374    ///     }
375    /// }
376    /// ```
377    pub fn iter(&self) -> Iter {
378        Iter {
379            delta_cookies: self.delta_cookies.iter()
380                .chain(self.original_cookies.difference(&self.delta_cookies)),
381        }
382    }
383
384    /// Returns a read-only `PrivateJar` with `self` as its parent jar using the
385    /// key `key` to verify/decrypt cookies retrieved from the child jar. Any
386    /// retrievals from the child jar will be made from the parent jar.
387    ///
388    /// # Example
389    ///
390    /// ```rust
391    /// use cookie::{Cookie, CookieJar, Key};
392    ///
393    /// // Generate a secure key.
394    /// let key = Key::generate();
395    ///
396    /// // Add a private (signed + encrypted) cookie.
397    /// let mut jar = CookieJar::new();
398    /// jar.private_mut(&key).add(Cookie::new("private", "text"));
399    ///
400    /// // The cookie's contents are encrypted.
401    /// assert_ne!(jar.get("private").unwrap().value(), "text");
402    ///
403    /// // They can be decrypted and verified through the child jar.
404    /// assert_eq!(jar.private(&key).get("private").unwrap().value(), "text");
405    ///
406    /// // A tampered with cookie does not validate but still exists.
407    /// let mut cookie = jar.get("private").unwrap().clone();
408    /// jar.add(Cookie::new("private", cookie.value().to_string() + "!"));
409    /// assert!(jar.private(&key).get("private").is_none());
410    /// assert!(jar.get("private").is_some());
411    /// ```
412    #[cfg(feature = "private")]
413    #[cfg_attr(all(nightly, doc), doc(cfg(feature = "private")))]
414    pub fn private<'a>(&'a self, key: &Key) -> PrivateJar<&'a Self> {
415        PrivateJar::new(self, key)
416    }
417
418    /// Returns a read/write `PrivateJar` with `self` as its parent jar using
419    /// the key `key` to sign/encrypt and verify/decrypt cookies added/retrieved
420    /// from the child jar.
421    ///
422    /// Any modifications to the child jar will be reflected on the parent jar,
423    /// and any retrievals from the child jar will be made from the parent jar.
424    ///
425    /// # Example
426    ///
427    /// ```rust
428    /// use cookie::{Cookie, CookieJar, Key};
429    ///
430    /// // Generate a secure key.
431    /// let key = Key::generate();
432    ///
433    /// // Add a private (signed + encrypted) cookie.
434    /// let mut jar = CookieJar::new();
435    /// jar.private_mut(&key).add(Cookie::new("private", "text"));
436    ///
437    /// // Remove a cookie using the child jar.
438    /// jar.private_mut(&key).remove(Cookie::named("private"));
439    /// ```
440    #[cfg(feature = "private")]
441    #[cfg_attr(all(nightly, doc), doc(cfg(feature = "private")))]
442    pub fn private_mut<'a>(&'a mut self, key: &Key) -> PrivateJar<&'a mut Self> {
443        PrivateJar::new(self, key)
444    }
445
446    /// Returns a read-only `SignedJar` with `self` as its parent jar using the
447    /// key `key` to verify cookies retrieved from the child jar. Any retrievals
448    /// from the child jar will be made from the parent jar.
449    ///
450    /// # Example
451    ///
452    /// ```rust
453    /// use cookie::{Cookie, CookieJar, Key};
454    ///
455    /// // Generate a secure key.
456    /// let key = Key::generate();
457    ///
458    /// // Add a signed cookie.
459    /// let mut jar = CookieJar::new();
460    /// jar.signed_mut(&key).add(Cookie::new("signed", "text"));
461    ///
462    /// // The cookie's contents are signed but still in plaintext.
463    /// assert_ne!(jar.get("signed").unwrap().value(), "text");
464    /// assert!(jar.get("signed").unwrap().value().contains("text"));
465    ///
466    /// // They can be verified through the child jar.
467    /// assert_eq!(jar.signed(&key).get("signed").unwrap().value(), "text");
468    ///
469    /// // A tampered with cookie does not validate but still exists.
470    /// let mut cookie = jar.get("signed").unwrap().clone();
471    /// jar.add(Cookie::new("signed", cookie.value().to_string() + "!"));
472    /// assert!(jar.signed(&key).get("signed").is_none());
473    /// assert!(jar.get("signed").is_some());
474    /// ```
475    #[cfg(feature = "signed")]
476    #[cfg_attr(all(nightly, doc), doc(cfg(feature = "signed")))]
477    pub fn signed<'a>(&'a self, key: &Key) -> SignedJar<&'a Self> {
478        SignedJar::new(self, key)
479    }
480
481    /// Returns a read/write `SignedJar` with `self` as its parent jar using the
482    /// key `key` to sign/verify cookies added/retrieved from the child jar.
483    ///
484    /// Any modifications to the child jar will be reflected on the parent jar,
485    /// and any retrievals from the child jar will be made from the parent jar.
486    ///
487    /// # Example
488    ///
489    /// ```rust
490    /// use cookie::{Cookie, CookieJar, Key};
491    ///
492    /// // Generate a secure key.
493    /// let key = Key::generate();
494    ///
495    /// // Add a signed cookie.
496    /// let mut jar = CookieJar::new();
497    /// jar.signed_mut(&key).add(Cookie::new("signed", "text"));
498    ///
499    /// // Remove a cookie.
500    /// jar.signed_mut(&key).remove(Cookie::named("signed"));
501    /// ```
502    #[cfg(feature = "signed")]
503    #[cfg_attr(all(nightly, doc), doc(cfg(feature = "signed")))]
504    pub fn signed_mut<'a>(&'a mut self, key: &Key) -> SignedJar<&'a mut Self> {
505        SignedJar::new(self, key)
506    }
507}
508
509use std::collections::hash_set::Iter as HashSetIter;
510
511/// Iterator over the changes to a cookie jar.
512pub struct Delta<'a> {
513    iter: HashSetIter<'a, DeltaCookie>,
514}
515
516impl<'a> Iterator for Delta<'a> {
517    type Item = &'a Cookie<'static>;
518
519    fn next(&mut self) -> Option<&'a Cookie<'static>> {
520        self.iter.next().map(|c| &c.cookie)
521    }
522}
523
524use std::collections::hash_set::Difference;
525use std::collections::hash_map::RandomState;
526use std::iter::Chain;
527
528/// Iterator over all of the cookies in a jar.
529pub struct Iter<'a> {
530    delta_cookies: Chain<HashSetIter<'a, DeltaCookie>, Difference<'a, DeltaCookie, RandomState>>,
531}
532
533impl<'a> Iterator for Iter<'a> {
534    type Item = &'a Cookie<'static>;
535
536    fn next(&mut self) -> Option<&'a Cookie<'static>> {
537        for cookie in self.delta_cookies.by_ref() {
538            if !cookie.removed {
539                return Some(&*cookie);
540            }
541        }
542
543        None
544    }
545}
546
547#[cfg(test)]
548mod test {
549    use super::CookieJar;
550    use crate::Cookie;
551
552    #[test]
553    #[allow(deprecated)]
554    fn simple() {
555        let mut c = CookieJar::new();
556
557        c.add(Cookie::new("test", ""));
558        c.add(Cookie::new("test2", ""));
559        c.remove(Cookie::named("test"));
560
561        assert!(c.get("test").is_none());
562        assert!(c.get("test2").is_some());
563
564        c.add(Cookie::new("test3", ""));
565        c.remove(Cookie::named("test2"));
566        c.remove(Cookie::named("test3"));
567
568        assert!(c.get("test").is_none());
569        assert!(c.get("test2").is_none());
570        assert!(c.get("test3").is_none());
571    }
572
573    #[test]
574    fn jar_is_send() {
575        fn is_send<T: Send>(_: T) -> bool {
576            true
577        }
578
579        assert!(is_send(CookieJar::new()))
580    }
581
582    #[test]
583    #[cfg(all(feature = "signed", feature = "private"))]
584    fn iter() {
585        let key = crate::Key::generate();
586        let mut c = CookieJar::new();
587
588        c.add_original(Cookie::new("original", "original"));
589
590        c.add(Cookie::new("test", "test"));
591        c.add(Cookie::new("test2", "test2"));
592        c.add(Cookie::new("test3", "test3"));
593        assert_eq!(c.iter().count(), 4);
594
595        c.signed_mut(&key).add(Cookie::new("signed", "signed"));
596        c.private_mut(&key).add(Cookie::new("encrypted", "encrypted"));
597        assert_eq!(c.iter().count(), 6);
598
599        c.remove(Cookie::named("test"));
600        assert_eq!(c.iter().count(), 5);
601
602        c.remove(Cookie::named("signed"));
603        c.remove(Cookie::named("test2"));
604        assert_eq!(c.iter().count(), 3);
605
606        c.add(Cookie::new("test2", "test2"));
607        assert_eq!(c.iter().count(), 4);
608
609        c.remove(Cookie::named("test2"));
610        assert_eq!(c.iter().count(), 3);
611    }
612
613    #[test]
614    fn delta() {
615        use std::collections::HashMap;
616        use time::Duration;
617
618        let mut c = CookieJar::new();
619
620        c.add_original(Cookie::new("original", "original"));
621        c.add_original(Cookie::new("original1", "original1"));
622
623        c.add(Cookie::new("test", "test"));
624        c.add(Cookie::new("test2", "test2"));
625        c.add(Cookie::new("test3", "test3"));
626        c.add(Cookie::new("test4", "test4"));
627
628        c.remove(Cookie::named("test"));
629        c.remove(Cookie::named("original"));
630
631        assert_eq!(c.delta().count(), 4);
632
633        let names: HashMap<_, _> = c.delta()
634            .map(|c| (c.name(), c.max_age()))
635            .collect();
636
637        assert!(names.get("test2").unwrap().is_none());
638        assert!(names.get("test3").unwrap().is_none());
639        assert!(names.get("test4").unwrap().is_none());
640        assert_eq!(names.get("original").unwrap(), &Some(Duration::seconds(0)));
641    }
642
643    #[test]
644    fn replace_original() {
645        let mut jar = CookieJar::new();
646        jar.add_original(Cookie::new("original_a", "a"));
647        jar.add_original(Cookie::new("original_b", "b"));
648        assert_eq!(jar.get("original_a").unwrap().value(), "a");
649
650        jar.add(Cookie::new("original_a", "av2"));
651        assert_eq!(jar.get("original_a").unwrap().value(), "av2");
652    }
653
654    #[test]
655    fn empty_delta() {
656        let mut jar = CookieJar::new();
657        jar.add(Cookie::new("name", "val"));
658        assert_eq!(jar.delta().count(), 1);
659
660        jar.remove(Cookie::named("name"));
661        assert_eq!(jar.delta().count(), 0);
662
663        jar.add_original(Cookie::new("name", "val"));
664        assert_eq!(jar.delta().count(), 0);
665
666        jar.remove(Cookie::named("name"));
667        assert_eq!(jar.delta().count(), 1);
668
669        jar.add(Cookie::new("name", "val"));
670        assert_eq!(jar.delta().count(), 1);
671
672        jar.remove(Cookie::named("name"));
673        assert_eq!(jar.delta().count(), 1);
674    }
675
676    #[test]
677    fn add_remove_add() {
678        let mut jar = CookieJar::new();
679        jar.add_original(Cookie::new("name", "val"));
680        assert_eq!(jar.delta().count(), 0);
681
682        jar.remove(Cookie::named("name"));
683        assert_eq!(jar.delta().filter(|c| c.value().is_empty()).count(), 1);
684        assert_eq!(jar.delta().count(), 1);
685
686        // The cookie's been deleted. Another original doesn't change that.
687        jar.add_original(Cookie::new("name", "val"));
688        assert_eq!(jar.delta().filter(|c| c.value().is_empty()).count(), 1);
689        assert_eq!(jar.delta().count(), 1);
690
691        jar.remove(Cookie::named("name"));
692        assert_eq!(jar.delta().filter(|c| c.value().is_empty()).count(), 1);
693        assert_eq!(jar.delta().count(), 1);
694
695        jar.add(Cookie::new("name", "val"));
696        assert_eq!(jar.delta().filter(|c| !c.value().is_empty()).count(), 1);
697        assert_eq!(jar.delta().count(), 1);
698
699        jar.remove(Cookie::named("name"));
700        assert_eq!(jar.delta().filter(|c| c.value().is_empty()).count(), 1);
701        assert_eq!(jar.delta().count(), 1);
702    }
703
704    #[test]
705    fn replace_remove() {
706        let mut jar = CookieJar::new();
707        jar.add_original(Cookie::new("name", "val"));
708        assert_eq!(jar.delta().count(), 0);
709
710        jar.add(Cookie::new("name", "val"));
711        assert_eq!(jar.delta().count(), 1);
712        assert_eq!(jar.delta().filter(|c| !c.value().is_empty()).count(), 1);
713
714        jar.remove(Cookie::named("name"));
715        assert_eq!(jar.delta().filter(|c| c.value().is_empty()).count(), 1);
716    }
717
718    #[test]
719    fn remove_with_path() {
720        let mut jar = CookieJar::new();
721        jar.add_original(Cookie::build("name", "val").finish());
722        assert_eq!(jar.iter().count(), 1);
723        assert_eq!(jar.delta().count(), 0);
724        assert_eq!(jar.iter().filter(|c| c.path().is_none()).count(), 1);
725
726        jar.remove(Cookie::build("name", "").path("/").finish());
727        assert_eq!(jar.iter().count(), 0);
728        assert_eq!(jar.delta().count(), 1);
729        assert_eq!(jar.delta().filter(|c| c.value().is_empty()).count(), 1);
730        assert_eq!(jar.delta().filter(|c| c.path() == Some("/")).count(), 1);
731    }
732}