use crate::actor::Page;
use crate::error::{BrowsingError, Result};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StorageEntry {
pub key: String,
pub value: String,
}
#[derive(Debug, Clone, Copy, Default)]
pub struct StorageManager;
impl StorageManager {
pub fn new() -> Self {
Self
}
pub async fn get_local_storage(&self, page: &Page, key: &str) -> Result<Option<String>> {
let script = format!(
"(function() {{ try {{ return window.localStorage.getItem({}) || ''; }} catch(e) {{ return ''; }} }})()",
serde_json::json!(key)
);
let result = page.evaluate(&script).await?;
if result.is_empty() {
Ok(None)
} else {
Ok(Some(result))
}
}
pub async fn set_local_storage(&self, page: &Page, key: &str, value: &str) -> Result<()> {
let script = format!(
"(function() {{ try {{ window.localStorage.setItem({}, {}); return 'ok'; }} catch(e) {{ return e.message; }} }})()",
serde_json::json!(key),
serde_json::json!(value)
);
let result = page.evaluate(&script).await?;
if result != "ok" {
return Err(BrowsingError::Browser(format!(
"localStorage setItem failed: {result}"
)));
}
Ok(())
}
pub async fn remove_local_storage(&self, page: &Page, key: &str) -> Result<()> {
let script = format!(
"(function() {{ try {{ window.localStorage.removeItem({}); return 'ok'; }} catch(e) {{ return e.message; }} }})()",
serde_json::json!(key)
);
page.evaluate(&script).await?;
Ok(())
}
pub async fn clear_local_storage(&self, page: &Page) -> Result<()> {
let script = "(function() { try { window.localStorage.clear(); return 'ok'; } catch(e) { return e.message; } })()";
let result = page.evaluate(script).await?;
if result != "ok" {
return Err(BrowsingError::Browser(format!(
"localStorage clear failed: {result}"
)));
}
Ok(())
}
pub async fn get_all_local_storage(&self, page: &Page) -> Result<Vec<StorageEntry>> {
let script = r#"
(function() {
try {
var entries = [];
for (var i = 0; i < window.localStorage.length; i++) {
var key = window.localStorage.key(i);
entries.push({ key: key, value: window.localStorage.getItem(key) });
}
return JSON.stringify(entries);
} catch(e) {
return '[]';
}
})()
"#;
let result = page.evaluate(script).await?;
let entries: Vec<StorageEntry> =
serde_json::from_str(&result).unwrap_or_default();
Ok(entries)
}
pub async fn get_session_storage(&self, page: &Page, key: &str) -> Result<Option<String>> {
let script = format!(
"(function() {{ try {{ return window.sessionStorage.getItem({}) || ''; }} catch(e) {{ return ''; }} }})()",
serde_json::json!(key)
);
let result = page.evaluate(&script).await?;
if result.is_empty() {
Ok(None)
} else {
Ok(Some(result))
}
}
pub async fn set_session_storage(&self, page: &Page, key: &str, value: &str) -> Result<()> {
let script = format!(
"(function() {{ try {{ window.sessionStorage.setItem({}, {}); return 'ok'; }} catch(e) {{ return e.message; }} }})()",
serde_json::json!(key),
serde_json::json!(value)
);
let result = page.evaluate(&script).await?;
if result != "ok" {
return Err(BrowsingError::Browser(format!(
"sessionStorage setItem failed: {result}"
)));
}
Ok(())
}
pub async fn remove_session_storage(&self, page: &Page, key: &str) -> Result<()> {
let script = format!(
"(function() {{ try {{ window.sessionStorage.removeItem({}); return 'ok'; }} catch(e) {{ return e.message; }} }})()",
serde_json::json!(key)
);
page.evaluate(&script).await?;
Ok(())
}
pub async fn clear_session_storage(&self, page: &Page) -> Result<()> {
let script = "(function() { try { window.sessionStorage.clear(); return 'ok'; } catch(e) { return e.message; } })()";
let result = page.evaluate(script).await?;
if result != "ok" {
return Err(BrowsingError::Browser(format!(
"sessionStorage clear failed: {result}"
)));
}
Ok(())
}
pub async fn get_all_session_storage(&self, page: &Page) -> Result<Vec<StorageEntry>> {
let script = r#"
(function() {
try {
var entries = [];
for (var i = 0; i < window.sessionStorage.length; i++) {
var key = window.sessionStorage.key(i);
entries.push({ key: key, value: window.sessionStorage.getItem(key) });
}
return JSON.stringify(entries);
} catch(e) {
return '[]';
}
})()
"#;
let result = page.evaluate(script).await?;
let entries: Vec<StorageEntry> =
serde_json::from_str(&result).unwrap_or_default();
Ok(entries)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_storage_entry_creation() {
let entry = StorageEntry {
key: "token".to_string(),
value: "abc123".to_string(),
};
assert_eq!(entry.key, "token");
assert_eq!(entry.value, "abc123");
}
#[test]
fn test_storage_entry_serialization() {
let entry = StorageEntry {
key: "foo".to_string(),
value: "bar".to_string(),
};
let json = serde_json::to_string(&entry).unwrap();
assert!(json.contains("foo"));
assert!(json.contains("bar"));
}
#[test]
fn test_storage_entry_deserialization() {
let json = r#"{"key":"foo","value":"bar"}"#;
let entry: StorageEntry = serde_json::from_str(json).unwrap();
assert_eq!(entry.key, "foo");
assert_eq!(entry.value, "bar");
}
#[test]
fn test_storage_manager_default() {
let manager = StorageManager::default();
let _ = std::format!("{:?}", manager);
}
}