use std::collections::HashMap;
use std::path::PathBuf;
use std::sync::{Arc, Mutex, OnceLock};
use anyhow::{Context, Result};
use crate::persist::global::{DatabaseConfig, DatabaseService};
fn opened() -> &'static Mutex<HashMap<PathBuf, Arc<DatabaseService>>> {
static OPENED: OnceLock<Mutex<HashMap<PathBuf, Arc<DatabaseService>>>> = OnceLock::new();
OPENED.get_or_init(Default::default)
}
pub fn path() -> Result<PathBuf> {
Ok(wsb_prj::database_path()?)
}
pub fn older_databases() -> Result<Vec<PathBuf>> {
let store = path()?;
let machine = crate::daemon::machine_dir().map_err(anyhow::Error::msg)?;
let mut old = vec![machine.join("machine.db"), machine.join("wsb.db")];
if let Ok(workshop) = wsb_prj::Workshop::from_env() {
old.push(workshop.state_dir().join("github.db"));
}
Ok(old.into_iter().filter(|db| *db != store && db.exists()).collect())
}
pub fn require_current_layout() -> Result<()> {
let old = older_databases()?;
if !old.is_empty() {
anyhow::bail!(
"an older wsb's databases are still here ({}): stop the daemon (`wsb daemon stop`) and run \
`wsb migrate` to bring them into {}",
old.iter().map(|db| db.display().to_string()).collect::<Vec<_>>().join(", "),
path()?.display()
);
}
Ok(())
}
pub fn open() -> Result<Arc<DatabaseService>> {
require_current_layout()?;
open_for_migration()
}
pub fn open_for_migration() -> Result<Arc<DatabaseService>> {
let path = path()?;
let mut held = opened().lock().map_err(|_| anyhow::anyhow!("the database registry was poisoned"))?;
if let Some(service) = held.get(&path) {
return Ok(service.clone());
}
let service = Arc::new(
DatabaseService::new(&DatabaseConfig::new(&path))
.with_context(|| format!("opening the workshop's database at {}", path.display()))?,
);
held.insert(path, service.clone());
Ok(service)
}
#[cfg(test)]
mod tests {
use crate::test_environment::Environment;
#[test]
fn test20136_the_machines_own_folder_shares_the_workshops_database() {
let temp = tempfile::tempdir().unwrap();
let prj = crate::platform::canonicalize(temp.path()).unwrap().join("prj");
let apart = prj.parent().unwrap().join("apart");
let home = prj.parent().unwrap().join("home");
for dir in [&prj, &apart, &home] {
std::fs::create_dir_all(dir).unwrap();
}
let workshop = wsb_prj::Workshop::at(&prj).unwrap();
let host = workshop.host_dir().unwrap();
std::fs::create_dir_all(&host).unwrap();
let ours = prj.join(".state/wsb.db");
let mut env = Environment::set(&[("PRJ", &prj.to_string_lossy()), ("HOME", &home.to_string_lossy())]);
env.unset(crate::daemon::MACHINE_DIR_ENV);
assert_eq!(super::path().unwrap(), ours);
env.change(&[(crate::daemon::MACHINE_DIR_ENV, &host.to_string_lossy())]);
assert_eq!(super::path().unwrap(), ours, "the machine's own folder kept a database of its own");
env.change(&[(crate::daemon::MACHINE_DIR_ENV, &format!("{}/../{}", host.display(), host.file_name().unwrap().to_string_lossy()))]);
assert_eq!(super::path().unwrap(), ours);
env.change(&[(crate::daemon::MACHINE_DIR_ENV, &apart.to_string_lossy())]);
assert_eq!(super::path().unwrap(), apart.join("wsb.db"), "a machine set apart shares the workshop's database");
env.unset(crate::daemon::MACHINE_DIR_ENV);
env.unset("PRJ");
assert_eq!(super::path().unwrap(), home.join(".wsb/wsb.db"));
}
}