mirror of
https://github.com/mikedilger/gossip.git
synced 2024-09-29 08:21:47 +00:00
120 lines
3.7 KiB
Rust
120 lines
3.7 KiB
Rust
use crate::error::Error;
|
|
use std::env;
|
|
use std::ffi::OsStr;
|
|
use std::fs;
|
|
use std::path::PathBuf;
|
|
use std::sync::RwLock;
|
|
|
|
lazy_static! {
|
|
static ref CURRENT: RwLock<Option<Profile>> = RwLock::new(None);
|
|
}
|
|
|
|
/// Storage paths
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub struct Profile {
|
|
/// The base directory for all gossip data
|
|
pub base_dir: PathBuf,
|
|
|
|
/// The directory for cache
|
|
pub cache_dir: PathBuf,
|
|
|
|
/// The profile directory (could be the same as the base_dir if default)
|
|
pub profile_dir: PathBuf,
|
|
|
|
/// The LMDB directory (within the profile directory)
|
|
pub lmdb_dir: PathBuf,
|
|
}
|
|
|
|
impl Profile {
|
|
fn new() -> Result<Profile, Error> {
|
|
// Get system standard directory for user data
|
|
let data_dir = dirs::data_dir()
|
|
.ok_or::<Error>("Cannot find a directory to store application data.".into())?;
|
|
|
|
// Canonicalize (follow symlinks, resolve ".." paths)
|
|
let data_dir = fs::canonicalize(data_dir)?;
|
|
|
|
// Push "gossip" to data_dir, or override with GOSSIP_DIR
|
|
let base_dir = match env::var("GOSSIP_DIR") {
|
|
Ok(dir) => {
|
|
tracing::info!("Using GOSSIP_DIR: {}", dir);
|
|
// Note, this must pre-exist
|
|
fs::canonicalize(PathBuf::from(dir))?
|
|
}
|
|
Err(_) => {
|
|
let mut base_dir = data_dir;
|
|
base_dir.push("gossip");
|
|
fs::canonicalize(base_dir)? // because gossip might be a link
|
|
}
|
|
};
|
|
|
|
let cache_dir = {
|
|
let mut cache_dir = base_dir.clone();
|
|
cache_dir.push("cache");
|
|
cache_dir
|
|
};
|
|
|
|
// optional profile name, if specified the the user data is stored in a subdirectory
|
|
let profile_dir = match env::var("GOSSIP_PROFILE") {
|
|
Ok(profile) => {
|
|
if "cache".eq_ignore_ascii_case(profile.as_str()) {
|
|
return Err(Error::from("Profile name 'cache' is reserved."));
|
|
}
|
|
|
|
// Check that it doesn't corrupt the expected path
|
|
let mut dir = base_dir.clone();
|
|
dir.push(&profile);
|
|
match dir.file_name() {
|
|
Some(filename) => {
|
|
if filename != OsStr::new(&profile) {
|
|
return Err(Error::from(format!(
|
|
"Profile is not a simple filename: {}",
|
|
profile
|
|
)));
|
|
}
|
|
}
|
|
None => {
|
|
return Err(Error::from(format!("Profile is invalid: {}", profile)));
|
|
}
|
|
};
|
|
|
|
dir
|
|
}
|
|
Err(_) => base_dir.clone(),
|
|
};
|
|
|
|
let lmdb_dir = {
|
|
let mut lmdb_dir = base_dir.clone();
|
|
lmdb_dir.push("lmdb");
|
|
lmdb_dir
|
|
};
|
|
|
|
// Create all these directories if missing
|
|
fs::create_dir_all(&base_dir)?;
|
|
fs::create_dir_all(&cache_dir)?;
|
|
fs::create_dir_all(&profile_dir)?;
|
|
fs::create_dir_all(&lmdb_dir)?;
|
|
|
|
Ok(Profile {
|
|
base_dir,
|
|
profile_dir,
|
|
cache_dir,
|
|
lmdb_dir,
|
|
})
|
|
}
|
|
|
|
pub fn current() -> Result<Profile, Error> {
|
|
{
|
|
// create a new scope to drop the read lock before we try to create a new profile if it doesn't exist
|
|
let current = CURRENT.read().unwrap();
|
|
if current.is_some() {
|
|
return Ok(current.as_ref().unwrap().clone());
|
|
}
|
|
}
|
|
let created = Profile::new()?;
|
|
let mut w = CURRENT.write().unwrap();
|
|
*w = Some(created.clone());
|
|
Ok(created)
|
|
}
|
|
}
|