1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
use std::io::ErrorKind as IoErrorKind;
use std::{path::PathBuf, sync::Arc};
use crate::{ErrorKind, Result};
use log::info;
use reqwest_cookie_store::{CookieStore as ReqwestCookieStore, CookieStoreMutex};
/// A wrapper around `reqwest_cookie_store::CookieStore`
///
/// We keep track of the file path of the cookie store and
/// implement `PartialEq` to compare cookie jars by their path
#[derive(Debug, Clone)]
pub struct CookieJar {
pub(crate) path: PathBuf,
pub(crate) inner: Arc<CookieStoreMutex>,
}
impl CookieJar {
/// Load a cookie store from a file
///
/// Currently only JSON files are supported
///
/// # Errors
///
/// This function will return an error if
/// - the file cannot be opened or
/// - if the file is not valid JSON
pub fn load(path: PathBuf) -> Result<Self> {
match std::fs::File::open(&path).map(std::io::BufReader::new) {
Ok(reader) => {
info!("Loading cookies from {}", path.display());
let inner = Arc::new(CookieStoreMutex::new(
ReqwestCookieStore::load_json(reader)
.map_err(|e| ErrorKind::Cookies(format!("Failed to load cookies: {e}")))?,
));
Ok(Self { path, inner })
}
// Create a new cookie store if the file does not exist
Err(e) if e.kind() == IoErrorKind::NotFound => Ok(Self {
path,
inner: Arc::new(CookieStoreMutex::new(ReqwestCookieStore::default())),
}),
// Propagate other IO errors (like permission denied) to the caller
Err(e) => Err(e.into()),
}
}
/// Save the cookie store to file as JSON
/// This will overwrite the file, which was loaded if any
///
/// # Errors
///
/// This function will return an error if
/// - the cookie store is locked or
/// - the file cannot be opened or
/// - if the file cannot be written to or
/// - if the file cannot be serialized to JSON
pub fn save(&self) -> Result<()> {
let mut file = std::fs::File::create(&self.path)?;
self.inner
.lock()
.map_err(|e| ErrorKind::Cookies(format!("Failed to lock cookie store: {e}")))?
.save_json(&mut file)
.map_err(|e| ErrorKind::Cookies(format!("Failed to save cookies: {e}")))
}
}
// Deref to inner cookie store
impl std::ops::Deref for CookieJar {
type Target = Arc<CookieStoreMutex>;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl PartialEq for CookieJar {
fn eq(&self, other: &Self) -> bool {
// Assume that the cookie jar is the same if the path is the same
// Comparing the cookie stores directly is not possible because the
// `CookieStore` struct does not implement `Eq`
self.path == other.path
}
}