use crate::errors::CookieError;
use reqwest::cookie::Jar;
use std::path::Path;
use std::sync::Arc;
pub struct CookieJarLoader;
impl CookieJarLoader {
pub fn load_cookie_jar(cookie_path: &Path) -> Result<Jar, CookieError> {
if !cookie_path.exists() {
return Err(CookieError::PathInvalid(cookie_path.display().to_string()));
}
let content = std::fs::read_to_string(cookie_path)
.map_err(|_| CookieError::PathInvalid(cookie_path.display().to_string()))?;
if content.trim().is_empty() {
return Err(CookieError::Invalid(cookie_path.display().to_string()));
}
let jar = Jar::default();
let cookie_lines = content
.lines()
.filter(|line| !line.starts_with('#') && !line.trim().is_empty());
let mut has_cookies = false;
for line in cookie_lines {
let parts: Vec<&str> = line.split('\t').collect();
if parts.len() >= 7 {
let domain = parts[0];
let path = parts[2];
let secure = parts[3] == "TRUE";
let name = parts[5];
let value = parts[6];
let cookie = format!("{}={}", name, value);
let url = format!(
"{}://{}{}",
if secure { "https" } else { "http" },
domain,
path
);
jar.add_cookie_str(&cookie, &url.parse().unwrap());
has_cookies = true;
}
}
if !has_cookies {
return Err(CookieError::Invalid(cookie_path.display().to_string()));
}
Ok(jar)
}
pub fn create_cookie_jar(cookie_path: &Path) -> Result<Arc<Jar>, CookieError> {
let jar = Self::load_cookie_jar(cookie_path)?;
Ok(Arc::new(jar))
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::Write;
use tempfile::NamedTempFile;
fn create_temp_file(content: &str) -> NamedTempFile {
let mut file = NamedTempFile::new().unwrap();
file.write_all(content.as_bytes()).unwrap();
file.flush().unwrap();
file
}
#[test]
fn test_load_valid_cookie_file() {
let content = r#"# Netscape HTTP Cookie File
# This is a generated cookie file
.youtube.com TRUE / TRUE 1723157402 SID TestSessionId123
.youtube.com TRUE / TRUE 1723231432 HSID TestHash456
"#;
let file = create_temp_file(content);
let result = CookieJarLoader::load_cookie_jar(file.path());
assert!(result.is_ok(), "Should successfully load valid cookie file");
}
#[test]
fn test_load_nonexistent_file() {
let non_existent_path = Path::new("/this/path/does/not/exist.txt");
let result = CookieJarLoader::load_cookie_jar(non_existent_path);
assert!(result.is_err(), "Should fail with non-existent file");
match result {
Err(CookieError::PathInvalid(_)) => {
}
_ => panic!("Expected PathInvalid error for non-existent file"),
}
}
#[test]
fn test_load_empty_file() {
let file = create_temp_file("");
let result = CookieJarLoader::load_cookie_jar(file.path());
assert!(result.is_err(), "Should fail with empty file");
match result {
Err(CookieError::Invalid(_)) => {
}
_ => panic!("Expected Invalid error for empty file"),
}
}
#[test]
fn test_load_file_with_only_comments() {
let content = r#"# Netscape HTTP Cookie File
# This file only contains comments
# No actual cookies here
# Another comment line
"#;
let file = create_temp_file(content);
let result = CookieJarLoader::load_cookie_jar(file.path());
assert!(
result.is_err(),
"Should fail with file containing only comments"
);
match result {
Err(CookieError::Invalid(_)) => {
}
_ => panic!("Expected Invalid error for file with only comments"),
}
}
#[test]
fn test_load_malformed_cookie_file() {
let content = r#"# Netscape HTTP Cookie File
# This is a malformed cookie file
.youtube.com MISSING_FIELDS
invalid_format_line
.google.com TRUE / TRUE MISSING_NAME_AND_VALUE
"#;
let file = create_temp_file(content);
let result = CookieJarLoader::load_cookie_jar(file.path());
assert!(result.is_err(), "Should fail with malformed cookie file");
match result {
Err(CookieError::Invalid(_)) => {
}
_ => panic!("Expected Invalid error for malformed cookie file"),
}
}
#[test]
fn test_load_mixed_valid_invalid_cookies() {
let content = r#"# Netscape HTTP Cookie File
# This file has mixed valid and invalid cookies
.youtube.com TRUE / TRUE 1723157402 SID ValidCookie123
invalid_line_with_no_tabs
.google.com INVALID FORMAT MISSING_FIELDS
.example.com TRUE / TRUE 1723157402 TEST AnotherValidCookie
"#;
let file = create_temp_file(content);
let result = CookieJarLoader::load_cookie_jar(file.path());
assert!(
result.is_ok(),
"Should load file with at least some valid cookies"
);
}
#[test]
fn test_create_cookie_jar() {
let content = r#"# Netscape HTTP Cookie File
.youtube.com TRUE / TRUE 1723157402 SID TestSessionId123
"#;
let file = create_temp_file(content);
let result = CookieJarLoader::create_cookie_jar(file.path());
assert!(
result.is_ok(),
"Should successfully create Arc-wrapped cookie jar"
);
let _jar = result.unwrap();
}
#[test]
fn test_create_cookie_jar_invalid_path() {
let non_existent_path = Path::new("/this/path/does/not/exist.txt");
let result = CookieJarLoader::create_cookie_jar(non_existent_path);
assert!(result.is_err(), "Should fail with non-existent file");
match result {
Err(CookieError::PathInvalid(_)) => {
}
_ => panic!("Expected PathInvalid error for non-existent file"),
}
}
#[test]
fn test_secure_vs_insecure_cookies() {
let content = r#"# Netscape HTTP Cookie File
.example.com TRUE / TRUE 1723157402 SECURE SecureCookieValue
.example.com TRUE / FALSE 1723157402 INSECURE InsecureCookieValue
"#;
let file = create_temp_file(content);
let result = CookieJarLoader::load_cookie_jar(file.path());
assert!(result.is_ok(), "Should successfully load cookie file");
}
}