use std::sync::{Arc, Mutex};
use crate::cookie::CookieJar;
use crate::dns::DnsCache;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ShareType {
Dns,
Cookies,
}
#[derive(Debug, Clone)]
pub struct Share {
dns_cache: Option<Arc<Mutex<DnsCache>>>,
cookie_jar: Option<Arc<Mutex<CookieJar>>>,
}
impl Default for Share {
fn default() -> Self {
Self::new()
}
}
impl Share {
#[must_use]
pub const fn new() -> Self {
Self { dns_cache: None, cookie_jar: None }
}
pub fn add(&mut self, share_type: ShareType) {
match share_type {
ShareType::Dns => {
if self.dns_cache.is_none() {
self.dns_cache = Some(Arc::new(Mutex::new(DnsCache::new())));
}
}
ShareType::Cookies => {
if self.cookie_jar.is_none() {
self.cookie_jar = Some(Arc::new(Mutex::new(CookieJar::new())));
}
}
}
}
pub fn remove(&mut self, share_type: ShareType) {
match share_type {
ShareType::Dns => self.dns_cache = None,
ShareType::Cookies => self.cookie_jar = None,
}
}
#[must_use]
pub const fn dns_cache(&self) -> Option<&Arc<Mutex<DnsCache>>> {
self.dns_cache.as_ref()
}
#[must_use]
pub const fn cookie_jar(&self) -> Option<&Arc<Mutex<CookieJar>>> {
self.cookie_jar.as_ref()
}
#[must_use]
pub const fn shares_dns(&self) -> bool {
self.dns_cache.is_some()
}
#[must_use]
pub const fn shares_cookies(&self) -> bool {
self.cookie_jar.is_some()
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
use super::*;
#[test]
fn share_new_is_empty() {
let share = Share::new();
assert!(!share.shares_dns());
assert!(!share.shares_cookies());
}
#[test]
fn share_default_is_empty() {
let share = Share::default();
assert!(!share.shares_dns());
assert!(!share.shares_cookies());
}
#[test]
fn share_add_dns() {
let mut share = Share::new();
share.add(ShareType::Dns);
assert!(share.shares_dns());
assert!(!share.shares_cookies());
assert!(share.dns_cache().is_some());
}
#[test]
fn share_add_cookies() {
let mut share = Share::new();
share.add(ShareType::Cookies);
assert!(!share.shares_dns());
assert!(share.shares_cookies());
assert!(share.cookie_jar().is_some());
}
#[test]
fn share_add_both() {
let mut share = Share::new();
share.add(ShareType::Dns);
share.add(ShareType::Cookies);
assert!(share.shares_dns());
assert!(share.shares_cookies());
}
#[test]
fn share_remove() {
let mut share = Share::new();
share.add(ShareType::Dns);
share.add(ShareType::Cookies);
share.remove(ShareType::Dns);
assert!(!share.shares_dns());
assert!(share.shares_cookies());
}
#[test]
fn share_add_idempotent() {
let mut share = Share::new();
share.add(ShareType::Dns);
let ptr1 = Arc::as_ptr(share.dns_cache().unwrap());
share.add(ShareType::Dns);
let ptr2 = Arc::as_ptr(share.dns_cache().unwrap());
assert_eq!(ptr1, ptr2);
}
#[test]
fn share_clone_shares_same_state() {
let mut share = Share::new();
share.add(ShareType::Dns);
share.add(ShareType::Cookies);
let share2 = share.clone();
assert!(Arc::ptr_eq(share.dns_cache().unwrap(), share2.dns_cache().unwrap()));
assert!(Arc::ptr_eq(share.cookie_jar().unwrap(), share2.cookie_jar().unwrap()));
}
#[test]
fn share_dns_cache_is_functional() {
let mut share = Share::new();
share.add(ShareType::Dns);
{
let mut cache = share.dns_cache().unwrap().lock().unwrap();
cache.put(
"example.com",
80,
vec![std::net::SocketAddr::new(
std::net::IpAddr::V4(std::net::Ipv4Addr::new(1, 2, 3, 4)),
80,
)],
);
}
let share2 = share.clone();
assert!(share2.dns_cache().unwrap().lock().unwrap().get("example.com", 80).is_some());
}
#[test]
fn share_cookie_jar_is_functional() {
let mut share = Share::new();
share.add(ShareType::Cookies);
{
let mut jar = share.cookie_jar().unwrap().lock().unwrap();
jar.store_cookies(&["session=abc123"], "example.com", "/", true);
}
let share2 = share.clone();
let header =
share2.cookie_jar().unwrap().lock().unwrap().cookie_header("example.com", "/", false);
assert!(header.is_some());
assert!(header.unwrap().contains("session=abc123"));
}
}