refactor: allow custom location

This commit is contained in:
zhom
2026-02-21 16:32:46 +04:00
parent 1afc2ca5ff
commit 206be3ff12
17 changed files with 230 additions and 327 deletions
+1 -8
View File
@@ -1,4 +1,3 @@
use directories::BaseDirs;
use reqwest::Client;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
@@ -464,13 +463,7 @@ impl ApiClient {
}
fn get_cache_dir() -> Result<PathBuf, Box<dyn std::error::Error + Send + Sync>> {
let base_dirs = BaseDirs::new().ok_or("Failed to get base directories")?;
let app_name = if cfg!(debug_assertions) {
"DonutBrowserDev"
} else {
"DonutBrowser"
};
let cache_dir = base_dirs.cache_dir().join(app_name).join("version_cache");
let cache_dir = crate::app_dirs::cache_dir().join("version_cache");
fs::create_dir_all(&cache_dir)?;
Ok(cache_dir)
}
+185
View File
@@ -0,0 +1,185 @@
use directories::BaseDirs;
use std::path::PathBuf;
use std::sync::OnceLock;
static BASE_DIRS: OnceLock<BaseDirs> = OnceLock::new();
fn base_dirs() -> &'static BaseDirs {
BASE_DIRS.get_or_init(|| BaseDirs::new().expect("Failed to get base directories"))
}
pub fn app_name() -> &'static str {
if cfg!(debug_assertions) {
"DonutBrowserDev"
} else {
"DonutBrowser"
}
}
pub fn data_dir() -> PathBuf {
#[cfg(test)]
{
if let Some(dir) = TEST_DATA_DIR.with(|cell| cell.borrow().clone()) {
return dir;
}
}
if let Ok(dir) = std::env::var("DONUTBROWSER_DATA_DIR") {
return PathBuf::from(dir);
}
base_dirs().data_local_dir().join(app_name())
}
pub fn cache_dir() -> PathBuf {
#[cfg(test)]
{
if let Some(dir) = TEST_CACHE_DIR.with(|cell| cell.borrow().clone()) {
return dir;
}
}
if let Ok(dir) = std::env::var("DONUTBROWSER_CACHE_DIR") {
return PathBuf::from(dir);
}
base_dirs().cache_dir().join(app_name())
}
pub fn profiles_dir() -> PathBuf {
data_dir().join("profiles")
}
pub fn binaries_dir() -> PathBuf {
data_dir().join("binaries")
}
pub fn data_subdir() -> PathBuf {
data_dir().join("data")
}
pub fn settings_dir() -> PathBuf {
data_dir().join("settings")
}
pub fn proxies_dir() -> PathBuf {
data_dir().join("proxies")
}
#[cfg(test)]
thread_local! {
static TEST_DATA_DIR: std::cell::RefCell<Option<PathBuf>> = const { std::cell::RefCell::new(None) };
static TEST_CACHE_DIR: std::cell::RefCell<Option<PathBuf>> = const { std::cell::RefCell::new(None) };
}
#[cfg(test)]
pub struct TestDirGuard {
kind: TestDirKind,
}
#[cfg(test)]
enum TestDirKind {
Data,
Cache,
}
#[cfg(test)]
impl Drop for TestDirGuard {
fn drop(&mut self) {
match self.kind {
TestDirKind::Data => TEST_DATA_DIR.with(|cell| *cell.borrow_mut() = None),
TestDirKind::Cache => TEST_CACHE_DIR.with(|cell| *cell.borrow_mut() = None),
}
}
}
#[cfg(test)]
pub fn set_test_data_dir(dir: PathBuf) -> TestDirGuard {
TEST_DATA_DIR.with(|cell| *cell.borrow_mut() = Some(dir));
TestDirGuard {
kind: TestDirKind::Data,
}
}
#[cfg(test)]
pub fn set_test_cache_dir(dir: PathBuf) -> TestDirGuard {
TEST_CACHE_DIR.with(|cell| *cell.borrow_mut() = Some(dir));
TestDirGuard {
kind: TestDirKind::Cache,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_app_name() {
let name = app_name();
assert!(
name == "DonutBrowser" || name == "DonutBrowserDev",
"app_name should be DonutBrowser or DonutBrowserDev, got: {name}"
);
}
#[test]
fn test_data_dir_returns_path() {
let dir = data_dir();
assert!(
dir.to_string_lossy().contains(app_name()),
"data_dir should contain app_name"
);
}
#[test]
fn test_cache_dir_returns_path() {
let dir = cache_dir();
assert!(
dir.to_string_lossy().contains(app_name()),
"cache_dir should contain app_name"
);
}
#[test]
fn test_subdirectory_helpers() {
assert!(profiles_dir().ends_with("profiles"));
assert!(binaries_dir().ends_with("binaries"));
assert!(data_subdir().ends_with("data"));
assert!(settings_dir().ends_with("settings"));
assert!(proxies_dir().ends_with("proxies"));
}
#[test]
fn test_set_test_data_dir() {
let tmp = PathBuf::from("/tmp/test-donut-data");
let _guard = set_test_data_dir(tmp.clone());
assert_eq!(data_dir(), tmp);
assert_eq!(profiles_dir(), tmp.join("profiles"));
assert_eq!(binaries_dir(), tmp.join("binaries"));
}
#[test]
fn test_set_test_cache_dir() {
let tmp = PathBuf::from("/tmp/test-donut-cache");
let _guard = set_test_cache_dir(tmp.clone());
assert_eq!(cache_dir(), tmp);
}
#[test]
fn test_guard_cleanup() {
let original_data = data_dir();
let original_cache = cache_dir();
{
let _guard = set_test_data_dir(PathBuf::from("/tmp/test-cleanup-data"));
assert_eq!(data_dir(), PathBuf::from("/tmp/test-cleanup-data"));
}
assert_eq!(data_dir(), original_data);
{
let _guard = set_test_cache_dir(PathBuf::from("/tmp/test-cleanup-cache"));
assert_eq!(cache_dir(), PathBuf::from("/tmp/test-cleanup-cache"));
}
assert_eq!(cache_dir(), original_cache);
}
}
+1 -11
View File
@@ -6,13 +6,11 @@ use crate::platform_browser;
use crate::profile::{BrowserProfile, ProfileManager};
use crate::proxy_manager::PROXY_MANAGER;
use crate::wayfern_manager::{WayfernConfig, WayfernManager};
use directories::BaseDirs;
use serde::Serialize;
use std::path::PathBuf;
use std::time::{SystemTime, UNIX_EPOCH};
use sysinfo::System;
pub struct BrowserRunner {
base_dirs: BaseDirs,
pub profile_manager: &'static ProfileManager,
pub downloaded_browsers_registry: &'static DownloadedBrowsersRegistry,
auto_updater: &'static crate::auto_updater::AutoUpdater,
@@ -23,7 +21,6 @@ pub struct BrowserRunner {
impl BrowserRunner {
fn new() -> Self {
Self {
base_dirs: BaseDirs::new().expect("Failed to get base directories"),
profile_manager: ProfileManager::instance(),
downloaded_browsers_registry: DownloadedBrowsersRegistry::instance(),
auto_updater: crate::auto_updater::AutoUpdater::instance(),
@@ -37,14 +34,7 @@ impl BrowserRunner {
}
pub fn get_binaries_dir(&self) -> PathBuf {
let mut path = self.base_dirs.data_local_dir().to_path_buf();
path.push(if cfg!(debug_assertions) {
"DonutBrowserDev"
} else {
"DonutBrowser"
});
path.push("binaries");
path
crate::app_dirs::binaries_dir()
}
/// Get the executable path for a browser profile
+1 -11
View File
@@ -1,7 +1,6 @@
use crate::browser_runner::BrowserRunner;
use crate::camoufox::{CamoufoxConfigBuilder, GeoIPOption, ScreenConstraints};
use crate::profile::BrowserProfile;
use directories::BaseDirs;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::PathBuf;
@@ -74,7 +73,6 @@ struct CamoufoxManagerInner {
pub struct CamoufoxManager {
inner: Arc<AsyncMutex<CamoufoxManagerInner>>,
base_dirs: BaseDirs,
}
impl CamoufoxManager {
@@ -83,7 +81,6 @@ impl CamoufoxManager {
inner: Arc::new(AsyncMutex::new(CamoufoxManagerInner {
instances: HashMap::new(),
})),
base_dirs: BaseDirs::new().expect("Failed to get base directories"),
}
}
@@ -92,14 +89,7 @@ impl CamoufoxManager {
}
pub fn get_profiles_dir(&self) -> PathBuf {
let mut path = self.base_dirs.data_local_dir().to_path_buf();
path.push(if cfg!(debug_assertions) {
"DonutBrowserDev"
} else {
"DonutBrowser"
});
path.push("profiles");
path
crate::app_dirs::profiles_dir()
}
/// Generate Camoufox fingerprint configuration during profile creation
+4 -46
View File
@@ -1,4 +1,3 @@
use directories::BaseDirs;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::fs;
@@ -71,16 +70,7 @@ impl DownloadedBrowsersRegistry {
}
fn get_registry_path() -> Result<PathBuf, Box<dyn std::error::Error + Send + Sync>> {
let base_dirs = BaseDirs::new().ok_or("Failed to get base directories")?;
let mut path = base_dirs.data_local_dir().to_path_buf();
path.push(if cfg!(debug_assertions) {
"DonutBrowserDev"
} else {
"DonutBrowser"
});
path.push("data");
path.push("downloaded_browsers.json");
Ok(path)
Ok(crate::app_dirs::data_subdir().join("downloaded_browsers.json"))
}
pub fn add_browser(&self, info: DownloadedBrowserInfo) {
@@ -128,19 +118,7 @@ impl DownloadedBrowsersRegistry {
};
let browser_instance = create_browser(browser_type.clone());
// Get binaries directory
let binaries_dir = if let Some(base_dirs) = directories::BaseDirs::new() {
let mut path = base_dirs.data_local_dir().to_path_buf();
path.push(if cfg!(debug_assertions) {
"DonutBrowserDev"
} else {
"DonutBrowser"
});
path.push("binaries");
path
} else {
return false;
};
let binaries_dir = crate::app_dirs::binaries_dir();
let files_exist = browser_instance.is_version_downloaded(version, &binaries_dir);
@@ -535,15 +513,7 @@ impl DownloadedBrowsersRegistry {
browser: &str,
version: &str,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
// Get binaries directory path
let base_dirs = directories::BaseDirs::new().ok_or("Failed to get base directories")?;
let mut binaries_dir = base_dirs.data_local_dir().to_path_buf();
binaries_dir.push(if cfg!(debug_assertions) {
"DonutBrowserDev"
} else {
"DonutBrowser"
});
binaries_dir.push("binaries");
let binaries_dir = crate::app_dirs::binaries_dir();
let version_dir = binaries_dir.join(browser).join(version);
@@ -830,19 +800,7 @@ impl DownloadedBrowsersRegistry {
let browser = create_browser(browser_type.clone());
// Get binaries directory
let binaries_dir = if let Some(base_dirs) = directories::BaseDirs::new() {
let mut path = base_dirs.data_local_dir().to_path_buf();
path.push(if cfg!(debug_assertions) {
"DonutBrowserDev"
} else {
"DonutBrowser"
});
path.push("binaries");
path
} else {
return Err("Failed to get base directories".into());
};
let binaries_dir = crate::app_dirs::binaries_dir();
log::info!(
"binaries_dir: {binaries_dir:?} for profile: {}",
+1 -15
View File
@@ -681,21 +681,7 @@ impl Downloader {
// Use injected registry instance
// Get binaries directory - we need to get it from somewhere
// This is a bit tricky since we don't have access to BrowserRunner's get_binaries_dir
// We'll need to replicate this logic
let binaries_dir = if let Some(base_dirs) = directories::BaseDirs::new() {
let mut path = base_dirs.data_local_dir().to_path_buf();
path.push(if cfg!(debug_assertions) {
"DonutBrowserDev"
} else {
"DonutBrowser"
});
path.push("binaries");
path
} else {
return Err("Failed to get base directories".into());
};
let binaries_dir = crate::app_dirs::binaries_dir();
// Check if registry thinks it's downloaded, but also verify files actually exist
if self.registry.is_browser_downloaded(&browser_str, &version) {
+4 -39
View File
@@ -1,8 +1,6 @@
use directories::BaseDirs;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::fs;
use std::path::{Path, PathBuf};
use std::sync::Mutex;
use crate::events;
@@ -33,48 +31,15 @@ struct GroupsData {
groups: Vec<ProfileGroup>,
}
pub struct GroupManager {
base_dirs: BaseDirs,
data_dir_override: Option<PathBuf>,
}
pub struct GroupManager;
impl GroupManager {
pub fn new() -> Self {
Self {
base_dirs: BaseDirs::new().expect("Failed to get base directories"),
data_dir_override: std::env::var("DONUTBROWSER_DATA_DIR")
.ok()
.map(PathBuf::from),
}
Self
}
// Helper for tests to override data directory without global env var
#[allow(dead_code)]
pub fn with_data_dir_override(dir: &Path) -> Self {
Self {
base_dirs: BaseDirs::new().expect("Failed to get base directories"),
data_dir_override: Some(dir.to_path_buf()),
}
}
fn get_groups_file_path(&self) -> PathBuf {
if let Some(dir) = &self.data_dir_override {
let mut override_path = dir.clone();
// Ensure the directory exists before returning the path
let _ = fs::create_dir_all(&override_path);
override_path.push("groups.json");
return override_path;
}
let mut path = self.base_dirs.data_local_dir().to_path_buf();
path.push(if cfg!(debug_assertions) {
"DonutBrowserDev"
} else {
"DonutBrowser"
});
path.push("data");
path.push("groups.json");
path
fn get_groups_file_path(&self) -> std::path::PathBuf {
crate::app_dirs::data_subdir().join("groups.json")
}
fn load_groups_data(&self) -> Result<GroupsData, Box<dyn std::error::Error>> {
+2 -6
View File
@@ -11,6 +11,7 @@ static PENDING_URLS: Mutex<Vec<String>> = Mutex::new(Vec::new());
mod api_client;
mod api_server;
mod app_auto_updater;
pub mod app_dirs;
mod auto_updater;
mod browser;
mod browser_runner;
@@ -706,12 +707,7 @@ pub fn run() {
pending.push(url.clone());
}
// Configure logging plugin with separate logs for dev and production
let log_file_name = if cfg!(debug_assertions) {
"DonutBrowserDev"
} else {
"DonutBrowser"
};
let log_file_name = app_dirs::app_name();
tauri::Builder::default()
.plugin(
+2 -19
View File
@@ -6,13 +6,11 @@ use crate::events;
use crate::profile::types::{get_host_os, BrowserProfile};
use crate::proxy_manager::PROXY_MANAGER;
use crate::wayfern_manager::WayfernConfig;
use directories::BaseDirs;
use std::fs::{self, create_dir_all};
use std::path::{Path, PathBuf};
use sysinfo::{Pid, System};
pub struct ProfileManager {
base_dirs: BaseDirs,
camoufox_manager: &'static crate::camoufox_manager::CamoufoxManager,
wayfern_manager: &'static crate::wayfern_manager::WayfernManager,
}
@@ -20,7 +18,6 @@ pub struct ProfileManager {
impl ProfileManager {
fn new() -> Self {
Self {
base_dirs: BaseDirs::new().expect("Failed to get base directories"),
camoufox_manager: crate::camoufox_manager::CamoufoxManager::instance(),
wayfern_manager: crate::wayfern_manager::WayfernManager::instance(),
}
@@ -31,25 +28,11 @@ impl ProfileManager {
}
pub fn get_profiles_dir(&self) -> PathBuf {
let mut path = self.base_dirs.data_local_dir().to_path_buf();
path.push(if cfg!(debug_assertions) {
"DonutBrowserDev"
} else {
"DonutBrowser"
});
path.push("profiles");
path
crate::app_dirs::profiles_dir()
}
pub fn get_binaries_dir(&self) -> PathBuf {
let mut path = self.base_dirs.data_local_dir().to_path_buf();
path.push(if cfg!(debug_assertions) {
"DonutBrowserDev"
} else {
"DonutBrowser"
});
path.push("binaries");
path
crate::app_dirs::binaries_dir()
}
#[allow(clippy::too_many_arguments)]
+2 -21
View File
@@ -1,5 +1,4 @@
use chrono::Utc;
use directories::BaseDirs;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::collections::HashMap;
@@ -149,18 +148,15 @@ pub struct ProxyManager {
// Track active proxy IDs by profile name for targeted cleanup
profile_active_proxy_ids: Mutex<HashMap<String, String>>, // Maps profile name to proxy id
stored_proxies: Mutex<HashMap<String, StoredProxy>>, // Maps proxy ID to stored proxy
base_dirs: BaseDirs,
}
impl ProxyManager {
pub fn new() -> Self {
let base_dirs = BaseDirs::new().expect("Failed to get base directories");
let manager = Self {
active_proxies: Mutex::new(HashMap::new()),
profile_proxies: Mutex::new(HashMap::new()),
profile_active_proxy_ids: Mutex::new(HashMap::new()),
stored_proxies: Mutex::new(HashMap::new()),
base_dirs,
};
// Load stored proxies on initialization
@@ -171,27 +167,12 @@ impl ProxyManager {
manager
}
// Get the path to the proxies directory
fn get_proxies_dir(&self) -> PathBuf {
let mut path = self.base_dirs.data_local_dir().to_path_buf();
path.push(if cfg!(debug_assertions) {
"DonutBrowserDev"
} else {
"DonutBrowser"
});
path.push("proxies");
path
crate::app_dirs::proxies_dir()
}
// Get the path to the proxy check cache directory
fn get_proxy_check_cache_dir(&self) -> Result<PathBuf, Box<dyn std::error::Error>> {
let mut path = self.base_dirs.cache_dir().to_path_buf();
path.push(if cfg!(debug_assertions) {
"DonutBrowserDev"
} else {
"DonutBrowser"
});
path.push("proxy_checks");
let path = crate::app_dirs::cache_dir().join("proxy_checks");
fs::create_dir_all(&path)?;
Ok(path)
}
+1 -10
View File
@@ -1,4 +1,3 @@
use directories::BaseDirs;
use serde::{Deserialize, Serialize};
use std::fs;
use std::path::PathBuf;
@@ -35,15 +34,7 @@ impl ProxyConfig {
}
pub fn get_storage_dir() -> PathBuf {
let base_dirs = BaseDirs::new().expect("Failed to get base directories");
let mut path = base_dirs.data_local_dir().to_path_buf();
path.push(if cfg!(debug_assertions) {
"DonutBrowserDev"
} else {
"DonutBrowser"
});
path.push("proxies");
path
crate::app_dirs::proxies_dir()
}
pub fn save_proxy_config(config: &ProxyConfig) -> Result<(), Box<dyn std::error::Error>> {
+17 -57
View File
@@ -1,4 +1,3 @@
use directories::BaseDirs;
use serde::{Deserialize, Serialize};
use std::fs::{self, create_dir_all};
use std::path::PathBuf;
@@ -91,27 +90,11 @@ impl Default for AppSettings {
}
}
pub struct SettingsManager {
base_dirs: BaseDirs,
data_dir_override: Option<PathBuf>,
}
pub struct SettingsManager;
impl SettingsManager {
fn new() -> Self {
Self {
base_dirs: BaseDirs::new().expect("Failed to get base directories"),
data_dir_override: std::env::var("DONUTBROWSER_DATA_DIR")
.ok()
.map(PathBuf::from),
}
}
#[cfg(test)]
fn with_data_dir_override(dir: &std::path::Path) -> Self {
Self {
base_dirs: BaseDirs::new().expect("Failed to get base directories"),
data_dir_override: Some(dir.to_path_buf()),
}
pub(crate) fn new() -> Self {
Self
}
pub fn instance() -> &'static SettingsManager {
@@ -119,18 +102,7 @@ impl SettingsManager {
}
pub fn get_settings_dir(&self) -> PathBuf {
if let Some(dir) = &self.data_dir_override {
return dir.join("settings");
}
let mut path = self.base_dirs.data_local_dir().to_path_buf();
path.push(if cfg!(debug_assertions) {
"DonutBrowserDev"
} else {
"DonutBrowser"
});
path.push("settings");
path
crate::app_dirs::settings_dir()
}
pub fn get_settings_file(&self) -> PathBuf {
@@ -950,16 +922,16 @@ mod tests {
use super::*;
use tempfile::TempDir;
fn create_test_settings_manager() -> (SettingsManager, TempDir) {
fn create_test_settings_manager() -> (SettingsManager, TempDir, crate::app_dirs::TestDirGuard) {
let temp_dir = TempDir::new().expect("Failed to create temp directory");
let manager = SettingsManager::with_data_dir_override(temp_dir.path());
(manager, temp_dir)
let guard = crate::app_dirs::set_test_data_dir(temp_dir.path().to_path_buf());
let manager = SettingsManager::new();
(manager, temp_dir, guard)
}
#[test]
fn test_settings_manager_creation() {
let (_manager, _temp_dir) = create_test_settings_manager();
// Test passes if no panic occurs
let (_manager, _temp_dir, _guard) = create_test_settings_manager();
}
#[test]
@@ -992,7 +964,7 @@ mod tests {
#[test]
fn test_load_settings_nonexistent_file() {
let (manager, _temp_dir) = create_test_settings_manager();
let (manager, _temp_dir, _guard) = create_test_settings_manager();
let result = manager.load_settings();
assert!(
@@ -1010,7 +982,7 @@ mod tests {
#[test]
fn test_save_and_load_settings() {
let (manager, _temp_dir) = create_test_settings_manager();
let (manager, _temp_dir, _guard) = create_test_settings_manager();
let test_settings = AppSettings {
set_as_default_browser: true,
@@ -1029,11 +1001,9 @@ mod tests {
language: None,
};
// Save settings
let save_result = manager.save_settings(&test_settings);
assert!(save_result.is_ok(), "Should save settings successfully");
// Load settings back
let load_result = manager.load_settings();
assert!(load_result.is_ok(), "Should load settings successfully");
@@ -1050,7 +1020,7 @@ mod tests {
#[test]
fn test_load_table_sorting_nonexistent_file() {
let (manager, _temp_dir) = create_test_settings_manager();
let (manager, _temp_dir, _guard) = create_test_settings_manager();
let result = manager.load_table_sorting();
assert!(
@@ -1065,18 +1035,16 @@ mod tests {
#[test]
fn test_save_and_load_table_sorting() {
let (manager, _temp_dir) = create_test_settings_manager();
let (manager, _temp_dir, _guard) = create_test_settings_manager();
let test_sorting = TableSortingSettings {
column: "browser".to_string(),
direction: "desc".to_string(),
};
// Save sorting
let save_result = manager.save_table_sorting(&test_sorting);
assert!(save_result.is_ok(), "Should save sorting successfully");
// Load sorting back
let load_result = manager.load_table_sorting();
assert!(load_result.is_ok(), "Should load sorting successfully");
@@ -1093,45 +1061,37 @@ mod tests {
#[test]
fn test_should_show_launch_on_login_prompt() {
let (manager, _temp_dir) = create_test_settings_manager();
let (manager, _temp_dir, _guard) = create_test_settings_manager();
let result = manager.should_show_launch_on_login_prompt();
assert!(result.is_ok(), "Should not fail");
// By default, should show prompt (not declined, autostart not enabled)
let _should_show = result.unwrap();
// Note: The actual value depends on system autostart state, so we just test it doesn't fail
}
#[test]
fn test_decline_launch_on_login() {
let (manager, _temp_dir) = create_test_settings_manager();
let (manager, _temp_dir, _guard) = create_test_settings_manager();
// Initially not declined
let settings = manager.load_settings().unwrap();
assert!(!settings.launch_on_login_declined);
// Decline
manager.decline_launch_on_login().unwrap();
// Should be declined now
let settings = manager.load_settings().unwrap();
assert!(settings.launch_on_login_declined);
}
#[test]
fn test_load_corrupted_settings_file() {
let (manager, _temp_dir) = create_test_settings_manager();
let (manager, _temp_dir, _guard) = create_test_settings_manager();
// Create settings directory
let settings_dir = manager.get_settings_dir();
fs::create_dir_all(&settings_dir).expect("Should create settings directory");
// Write corrupted JSON
let settings_file = manager.get_settings_file();
fs::write(&settings_file, "{ invalid json }").expect("Should write corrupted file");
// Should handle corrupted file gracefully
let result = manager.load_settings();
assert!(
result.is_ok(),
@@ -1151,7 +1111,7 @@ mod tests {
#[test]
fn test_settings_file_paths() {
let (manager, _temp_dir) = create_test_settings_manager();
let (manager, _temp_dir, _guard) = create_test_settings_manager();
let settings_dir = manager.get_settings_dir();
let settings_file = manager.get_settings_file();
+4 -38
View File
@@ -1,56 +1,22 @@
use crate::profile::BrowserProfile;
use directories::BaseDirs;
use serde::{Deserialize, Serialize};
use std::collections::BTreeSet;
use std::fs;
use std::path::{Path, PathBuf};
#[derive(Debug, Serialize, Deserialize, Default, Clone)]
struct TagsData {
tags: Vec<String>,
}
pub struct TagManager {
base_dirs: BaseDirs,
data_dir_override: Option<PathBuf>,
}
pub struct TagManager;
impl TagManager {
pub fn new() -> Self {
Self {
base_dirs: BaseDirs::new().expect("Failed to get base directories"),
data_dir_override: std::env::var("DONUTBROWSER_DATA_DIR")
.ok()
.map(PathBuf::from),
}
Self
}
// Helper for tests to override data directory without global env var
#[allow(dead_code)]
pub fn with_data_dir_override(dir: &Path) -> Self {
Self {
base_dirs: BaseDirs::new().expect("Failed to get base directories"),
data_dir_override: Some(dir.to_path_buf()),
}
}
fn get_tags_file_path(&self) -> PathBuf {
if let Some(dir) = &self.data_dir_override {
let mut override_path = dir.clone();
let _ = fs::create_dir_all(&override_path);
override_path.push("tags.json");
return override_path;
}
let mut path = self.base_dirs.data_local_dir().to_path_buf();
path.push(if cfg!(debug_assertions) {
"DonutBrowserDev"
} else {
"DonutBrowser"
});
path.push("data");
path.push("tags.json");
path
fn get_tags_file_path(&self) -> std::path::PathBuf {
crate::app_dirs::data_subdir().join("tags.json")
}
fn load_tags_data(&self) -> Result<TagsData, Box<dyn std::error::Error>> {
+1 -11
View File
@@ -1,4 +1,3 @@
use directories::BaseDirs;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::fs;
@@ -328,17 +327,8 @@ fn acquire_file_lock(lock_path: &PathBuf) -> Result<FileLockGuard, Box<dyn std::
Ok(FileLockGuard { _file: file })
}
/// Get the traffic stats storage directory
pub fn get_traffic_stats_dir() -> PathBuf {
let base_dirs = BaseDirs::new().expect("Failed to get base directories");
let mut path = base_dirs.cache_dir().to_path_buf();
path.push(if cfg!(debug_assertions) {
"DonutBrowserDev"
} else {
"DonutBrowser"
});
path.push("traffic_stats");
path
crate::app_dirs::cache_dir().join("traffic_stats")
}
/// Get the storage key for traffic stats (profile_id if available, otherwise proxy_id)
+1 -8
View File
@@ -1,4 +1,3 @@
use directories::BaseDirs;
use serde::{Deserialize, Serialize};
use std::fs;
use std::path::PathBuf;
@@ -67,13 +66,7 @@ impl VersionUpdater {
}
fn get_cache_dir() -> Result<PathBuf, Box<dyn std::error::Error>> {
let base_dirs = BaseDirs::new().ok_or("Failed to get base directories")?;
let app_name = if cfg!(debug_assertions) {
"DonutBrowserDev"
} else {
"DonutBrowser"
};
let cache_dir = base_dirs.cache_dir().join(app_name).join("version_cache");
let cache_dir = crate::app_dirs::cache_dir().join("version_cache");
fs::create_dir_all(&cache_dir)?;
Ok(cache_dir)
}
+2 -20
View File
@@ -1,6 +1,5 @@
use crate::browser_runner::BrowserRunner;
use crate::profile::BrowserProfile;
use directories::BaseDirs;
use reqwest::Client;
use serde::{Deserialize, Serialize};
use serde_json::json;
@@ -72,8 +71,6 @@ struct WayfernManagerInner {
pub struct WayfernManager {
inner: Arc<AsyncMutex<WayfernManagerInner>>,
#[allow(dead_code)]
base_dirs: BaseDirs,
http_client: Client,
}
@@ -91,7 +88,6 @@ impl WayfernManager {
inner: Arc::new(AsyncMutex::new(WayfernManagerInner {
instances: HashMap::new(),
})),
base_dirs: BaseDirs::new().expect("Failed to get base directories"),
http_client: Client::new(),
}
}
@@ -102,26 +98,12 @@ impl WayfernManager {
#[allow(dead_code)]
pub fn get_profiles_dir(&self) -> PathBuf {
let mut path = self.base_dirs.data_local_dir().to_path_buf();
path.push(if cfg!(debug_assertions) {
"DonutBrowserDev"
} else {
"DonutBrowser"
});
path.push("profiles");
path
crate::app_dirs::profiles_dir()
}
#[allow(dead_code)]
fn get_binaries_dir(&self) -> PathBuf {
let mut path = self.base_dirs.data_local_dir().to_path_buf();
path.push(if cfg!(debug_assertions) {
"DonutBrowserDev"
} else {
"DonutBrowser"
});
path.push("binaries");
path
crate::app_dirs::binaries_dir()
}
async fn find_free_port() -> Result<u16, Box<dyn std::error::Error + Send + Sync>> {
+1 -7
View File
@@ -516,13 +516,7 @@ async fn test_traffic_tracking() -> Result<(), Box<dyn std::error::Error + Send
// Wait for traffic stats to be flushed (happens every second)
sleep(Duration::from_secs(2)).await;
// Verify traffic was tracked by checking traffic stats file exists
// Note: Traffic stats are stored in the cache directory
let cache_dir = directories::BaseDirs::new()
.expect("Failed to get base directories")
.cache_dir()
.to_path_buf();
let traffic_stats_dir = cache_dir.join("DonutBrowserDev").join("traffic_stats");
let traffic_stats_dir = donutbrowser_lib::app_dirs::cache_dir().join("traffic_stats");
let stats_file = traffic_stats_dir.join(format!("{}.json", proxy_id));
if stats_file.exists() {