use std::collections::HashMap;
use scrapling_fetch::{Fetcher, FetcherSession, Response};
use tracing::debug;
use crate::error::{Result, SpiderError};
use crate::request::Request;
pub enum Session {
Fetcher(Fetcher),
FetcherSession(FetcherSession),
}
pub struct SessionManager {
sessions: HashMap<String, Session>,
default_session_id: Option<String>,
}
impl SessionManager {
pub fn new() -> Self {
Self {
sessions: HashMap::new(),
default_session_id: None,
}
}
pub fn add(
&mut self,
session_id: impl Into<String>,
session: Session,
default: bool,
) -> Result<()> {
let id = session_id.into();
if self.sessions.contains_key(&id) {
return Err(SpiderError::Session(format!(
"session '{id}' already exists"
)));
}
if default || self.default_session_id.is_none() {
self.default_session_id = Some(id.clone());
}
self.sessions.insert(id, session);
Ok(())
}
pub fn default_session_id(&self) -> Result<&str> {
self.default_session_id
.as_deref()
.ok_or_else(|| SpiderError::Session("no default session".into()))
}
pub fn session_ids(&self) -> Vec<&str> {
self.sessions.keys().map(|s| s.as_str()).collect()
}
pub fn get(&self, session_id: &str) -> Result<&Session> {
self.sessions.get(session_id).ok_or_else(|| {
SpiderError::Session(format!(
"session '{session_id}' not found; available: {:?}",
self.session_ids()
))
})
}
pub fn get_mut(&mut self, session_id: &str) -> Result<&mut Session> {
let ids = self.session_ids().join(", ");
self.sessions.get_mut(session_id).ok_or_else(|| {
SpiderError::Session(format!(
"session '{session_id}' not found; available: [{ids}]"
))
})
}
pub async fn fetch(&self, request: &Request) -> Result<Response> {
let sid = if request.sid.is_empty() {
self.default_session_id()?
} else {
&request.sid
};
let session = self.get(sid)?;
debug!(sid = sid, url = %request.url, "fetching via session");
let response = match session {
Session::Fetcher(f) => f.get(&request.url, None).await?,
Session::FetcherSession(fs) => fs.get(&request.url, None).await?,
};
Ok(response)
}
pub fn len(&self) -> usize {
self.sessions.len()
}
pub fn is_empty(&self) -> bool {
self.sessions.is_empty()
}
pub fn contains(&self, session_id: &str) -> bool {
self.sessions.contains_key(session_id)
}
}
impl Default for SessionManager {
fn default() -> Self {
Self::new()
}
}