use crate::{CookieJar, Error, Result};
use cookie_store::{Cookie as StoreCookie, CookieStore};
use http::{HeaderMap, HeaderValue};
use std::sync::{Arc, Mutex};
use url::Url;
#[derive(Clone)]
pub struct WindowsCookieStorage {
store: Arc<Mutex<CookieStore>>,
}
impl WindowsCookieStorage {
pub fn new() -> Result<Self> {
Ok(Self {
store: Arc::new(Mutex::new(CookieStore::default())),
})
}
pub fn add_cookie_from_header(&self, url: &Url, set_cookie_header: &str) -> Result<()> {
let mut store = self
.store
.lock()
.map_err(|e| Error::Internal(format!("Failed to lock cookie store: {}", e)))?;
if let Err(e) = store.parse(set_cookie_header, url) {
return Err(Error::Internal(format!("Failed to parse cookie: {}", e)));
}
Ok(())
}
pub fn add_cookie(&self, url: &Url, name: &str, value: &str) -> Result<()> {
let set_cookie_header = format!("{}={}", name, value);
self.add_cookie_from_header(url, &set_cookie_header)
}
pub fn process_response_headers(&self, url: &Url, headers: &HeaderMap) -> Result<()> {
for set_cookie_value in headers.get_all(http::header::SET_COOKIE) {
if let Ok(header_str) = set_cookie_value.to_str() {
if let Err(e) = self.add_cookie_from_header(url, header_str) {
eprintln!(
"Warning: Failed to process Set-Cookie header '{}': {}",
header_str, e
);
}
}
}
Ok(())
}
pub fn get_cookies_for_url(&self, url: &Url) -> Result<HeaderMap> {
let store = self
.store
.lock()
.map_err(|e| Error::Internal(format!("Failed to lock cookie store: {}", e)))?;
let mut headers = HeaderMap::new();
let cookie_pairs: Vec<String> = store
.get_request_values(url)
.map(|(name, value)| format!("{}={}", name, value))
.collect();
if !cookie_pairs.is_empty() {
let cookie_header = cookie_pairs.join("; ");
if let Ok(header_value) = HeaderValue::from_str(&cookie_header) {
headers.insert(http::header::COOKIE, header_value);
}
}
Ok(headers)
}
pub fn delete_cookie(&self, url: &Url, name: &str) -> Result<()> {
let mut store = self
.store
.lock()
.map_err(|e| Error::Internal(format!("Failed to lock cookie store: {}", e)))?;
let domain = url.host_str().unwrap_or("localhost");
let path = url.path();
let cookies_to_remove: Vec<StoreCookie> = store
.iter_any()
.filter(|cookie| {
cookie.name() == name
&& (cookie.domain().is_none() || cookie.domain() == Some(domain))
&& path.starts_with(cookie.path().unwrap_or("/"))
})
.cloned()
.collect();
for cookie in cookies_to_remove {
let expired_cookie = format!(
"{}=; Domain={}; Path={}; Expires=Thu, 01 Jan 1970 00:00:00 GMT",
cookie.name(),
cookie.domain().unwrap_or(domain),
cookie.path().unwrap_or("/")
);
let _ = store.parse(&expired_cookie, url);
}
Ok(())
}
pub fn clear_all_cookies(&self) -> Result<()> {
let mut store = self
.store
.lock()
.map_err(|e| Error::Internal(format!("Failed to lock cookie store: {}", e)))?;
*store = CookieStore::default();
Ok(())
}
pub fn get_all_cookies(&self) -> Result<Vec<StoreCookie<'_>>> {
let store = self
.store
.lock()
.map_err(|e| Error::Internal(format!("Failed to lock cookie store: {}", e)))?;
Ok(store.iter_any().cloned().collect())
}
}
impl From<WindowsCookieStorage> for CookieJar {
fn from(_storage: WindowsCookieStorage) -> Self {
CookieJar::new()
}
}