use std::collections::HashSet;
#[cfg(feature = "signed")] use crate::secure::SignedJar;
#[cfg(feature = "private")] use crate::secure::PrivateJar;
#[cfg(any(feature = "signed", feature = "private"))] use crate::secure::Key;
use crate::delta::DeltaCookie;
use crate::Cookie;
#[derive(Default, Debug, Clone)]
pub struct CookieJar {
original_cookies: HashSet<DeltaCookie>,
delta_cookies: HashSet<DeltaCookie>,
}
impl CookieJar {
pub fn new() -> CookieJar {
CookieJar::default()
}
pub fn get(&self, name: &str) -> Option<&Cookie<'static>> {
self.delta_cookies
.get(name)
.or_else(|| self.original_cookies.get(name))
.and_then(|c| if c.removed { None } else { Some(&c.cookie) })
}
pub fn add_original<C: Into<Cookie<'static>>>(&mut self, cookie: C) {
self.original_cookies.replace(DeltaCookie::added(cookie.into()));
}
pub fn add<C: Into<Cookie<'static>>>(&mut self, cookie: C) {
self.delta_cookies.replace(DeltaCookie::added(cookie.into()));
}
pub fn remove<C: Into<Cookie<'static>>>(&mut self, cookie: C) {
let mut cookie = cookie.into();
if self.original_cookies.contains(cookie.name()) {
cookie.make_removal();
self.delta_cookies.replace(DeltaCookie::removed(cookie));
} else {
self.delta_cookies.remove(cookie.name());
}
}
pub fn force_remove<N: AsRef<str>>(&mut self, name: N) {
self.original_cookies.remove(name.as_ref());
self.delta_cookies.remove(name.as_ref());
}
pub fn reset_delta(&mut self) {
self.delta_cookies = HashSet::new();
}
pub fn delta(&self) -> Delta {
Delta { iter: self.delta_cookies.iter() }
}
pub fn iter(&self) -> Iter {
Iter {
delta_cookies: self.delta_cookies.iter()
.chain(self.original_cookies.difference(&self.delta_cookies)),
}
}
#[cfg(feature = "private")]
#[cfg_attr(all(nightly, doc), doc(cfg(feature = "private")))]
pub fn private<'a>(&'a self, key: &Key) -> PrivateJar<&'a Self> {
PrivateJar::new(self, key)
}
#[cfg(feature = "private")]
#[cfg_attr(all(nightly, doc), doc(cfg(feature = "private")))]
pub fn private_mut<'a>(&'a mut self, key: &Key) -> PrivateJar<&'a mut Self> {
PrivateJar::new(self, key)
}
#[cfg(feature = "signed")]
#[cfg_attr(all(nightly, doc), doc(cfg(feature = "signed")))]
pub fn signed<'a>(&'a self, key: &Key) -> SignedJar<&'a Self> {
SignedJar::new(self, key)
}
#[cfg(feature = "signed")]
#[cfg_attr(all(nightly, doc), doc(cfg(feature = "signed")))]
pub fn signed_mut<'a>(&'a mut self, key: &Key) -> SignedJar<&'a mut Self> {
SignedJar::new(self, key)
}
}
use std::collections::hash_set::Iter as HashSetIter;
pub struct Delta<'a> {
iter: HashSetIter<'a, DeltaCookie>,
}
impl<'a> Iterator for Delta<'a> {
type Item = &'a Cookie<'static>;
fn next(&mut self) -> Option<&'a Cookie<'static>> {
self.iter.next().map(|c| &c.cookie)
}
}
use std::collections::hash_set::Difference;
use std::collections::hash_map::RandomState;
use std::iter::Chain;
pub struct Iter<'a> {
delta_cookies: Chain<HashSetIter<'a, DeltaCookie>, Difference<'a, DeltaCookie, RandomState>>,
}
impl<'a> Iterator for Iter<'a> {
type Item = &'a Cookie<'static>;
fn next(&mut self) -> Option<&'a Cookie<'static>> {
for cookie in self.delta_cookies.by_ref() {
if !cookie.removed {
return Some(&*cookie);
}
}
None
}
}
#[cfg(test)]
mod test {
use super::CookieJar;
use crate::Cookie;
#[test]
#[allow(deprecated)]
fn simple() {
let mut c = CookieJar::new();
c.add(("test", ""));
c.add(("test2", ""));
c.remove("test");
assert!(c.get("test").is_none());
assert!(c.get("test2").is_some());
c.add(("test3", ""));
c.remove("test2");
c.remove("test3");
assert!(c.get("test").is_none());
assert!(c.get("test2").is_none());
assert!(c.get("test3").is_none());
}
#[test]
fn jar_is_send() {
fn is_send<T: Send>(_: T) -> bool {
true
}
assert!(is_send(CookieJar::new()))
}
#[test]
#[cfg(all(feature = "signed", feature = "private"))]
fn iter() {
let key = crate::Key::generate();
let mut c = CookieJar::new();
c.add_original(Cookie::new("original", "original"));
c.add(Cookie::new("test", "test"));
c.add(Cookie::new("test2", "test2"));
c.add(Cookie::new("test3", "test3"));
assert_eq!(c.iter().count(), 4);
c.signed_mut(&key).add(Cookie::new("signed", "signed"));
c.private_mut(&key).add(Cookie::new("encrypted", "encrypted"));
assert_eq!(c.iter().count(), 6);
c.remove("test");
assert_eq!(c.iter().count(), 5);
c.remove("signed");
c.remove("test2");
assert_eq!(c.iter().count(), 3);
c.add(("test2", "test2"));
assert_eq!(c.iter().count(), 4);
c.remove("test2");
assert_eq!(c.iter().count(), 3);
}
#[test]
fn delta() {
use std::collections::HashMap;
use time::Duration;
let mut c = CookieJar::new();
c.add_original(Cookie::new("original", "original"));
c.add_original(Cookie::new("original1", "original1"));
c.add(Cookie::new("test", "test"));
c.add(Cookie::new("test2", "test2"));
c.add(Cookie::new("test3", "test3"));
c.add(Cookie::new("test4", "test4"));
c.remove("test");
c.remove("original");
assert_eq!(c.delta().count(), 4);
let names: HashMap<_, _> = c.delta()
.map(|c| (c.name(), c.max_age()))
.collect();
assert!(names.get("test2").unwrap().is_none());
assert!(names.get("test3").unwrap().is_none());
assert!(names.get("test4").unwrap().is_none());
assert_eq!(names.get("original").unwrap(), &Some(Duration::seconds(0)));
}
#[test]
fn replace_original() {
let mut jar = CookieJar::new();
jar.add_original(Cookie::new("original_a", "a"));
jar.add_original(Cookie::new("original_b", "b"));
assert_eq!(jar.get("original_a").unwrap().value(), "a");
jar.add(Cookie::new("original_a", "av2"));
assert_eq!(jar.get("original_a").unwrap().value(), "av2");
}
#[test]
fn empty_delta() {
let mut jar = CookieJar::new();
jar.add(Cookie::new("name", "val"));
assert_eq!(jar.delta().count(), 1);
jar.remove("name");
assert_eq!(jar.delta().count(), 0);
jar.add_original(Cookie::new("name", "val"));
assert_eq!(jar.delta().count(), 0);
jar.remove("name");
assert_eq!(jar.delta().count(), 1);
jar.add(Cookie::new("name", "val"));
assert_eq!(jar.delta().count(), 1);
jar.remove("name");
assert_eq!(jar.delta().count(), 1);
}
#[test]
fn add_remove_add() {
let mut jar = CookieJar::new();
jar.add_original(Cookie::new("name", "val"));
assert_eq!(jar.delta().count(), 0);
jar.remove("name");
assert_eq!(jar.delta().filter(|c| c.value().is_empty()).count(), 1);
assert_eq!(jar.delta().count(), 1);
jar.add_original(Cookie::new("name", "val"));
assert_eq!(jar.delta().filter(|c| c.value().is_empty()).count(), 1);
assert_eq!(jar.delta().count(), 1);
jar.remove("name");
assert_eq!(jar.delta().filter(|c| c.value().is_empty()).count(), 1);
assert_eq!(jar.delta().count(), 1);
jar.add(Cookie::new("name", "val"));
assert_eq!(jar.delta().filter(|c| !c.value().is_empty()).count(), 1);
assert_eq!(jar.delta().count(), 1);
jar.remove("name");
assert_eq!(jar.delta().filter(|c| c.value().is_empty()).count(), 1);
assert_eq!(jar.delta().count(), 1);
}
#[test]
fn replace_remove() {
let mut jar = CookieJar::new();
jar.add_original(Cookie::new("name", "val"));
assert_eq!(jar.delta().count(), 0);
jar.add(Cookie::new("name", "val"));
assert_eq!(jar.delta().count(), 1);
assert_eq!(jar.delta().filter(|c| !c.value().is_empty()).count(), 1);
jar.remove("name");
assert_eq!(jar.delta().filter(|c| c.value().is_empty()).count(), 1);
}
#[test]
fn remove_with_path() {
let mut jar = CookieJar::new();
jar.add_original(("name", "val"));
assert_eq!(jar.iter().count(), 1);
assert_eq!(jar.delta().count(), 0);
assert_eq!(jar.iter().filter(|c| c.path().is_none()).count(), 1);
jar.remove(Cookie::build("name").path("/"));
assert_eq!(jar.iter().count(), 0);
assert_eq!(jar.delta().count(), 1);
assert_eq!(jar.delta().filter(|c| c.value().is_empty()).count(), 1);
assert_eq!(jar.delta().filter(|c| c.path() == Some("/")).count(), 1);
}
}