use std::path::{Path, PathBuf};
use std::sync::Arc;
use anyhow::{Context, Result, anyhow, bail};
use mnem_backend_redb::open_or_init;
use mnem_core::repo::ReadonlyRepo;
use mnem_core::store::{Blockstore, OpHeadsStore};
pub(crate) const MNEM_DIR: &str = ".mnem";
pub(crate) fn locate_data_dir(override_path: Option<&Path>) -> Result<PathBuf> {
if let Some(p) = override_path {
let data = if p.ends_with(MNEM_DIR) {
p.to_path_buf()
} else {
p.join(MNEM_DIR)
};
if !data.is_dir() {
bail!("no mnem repository at {}", data.display());
}
return Ok(data);
}
let cwd = std::env::current_dir().context("cwd unreadable")?;
let mut cursor: &Path = &cwd;
loop {
let candidate = cursor.join(MNEM_DIR);
if candidate.is_dir() {
return Ok(candidate);
}
match cursor.parent() {
Some(parent) => cursor = parent,
None => bail!(
"no mnem repository found in {} or any parent. Run `mnem init` first.",
cwd.display()
),
}
}
}
pub(crate) fn db_path(data_dir: &Path) -> PathBuf {
data_dir.join("repo.redb")
}
pub(crate) fn open_stores(data_dir: &Path) -> Result<(Arc<dyn Blockstore>, Arc<dyn OpHeadsStore>)> {
let db = db_path(data_dir);
if !db.exists() {
bail!(
"redb file missing at {}; the repo exists but its storage is gone",
db.display()
);
}
let (bs, ohs, _) = open_or_init(&db)?;
Ok((bs, ohs))
}
pub(crate) fn create_or_open_stores(
data_dir: &Path,
) -> Result<(Arc<dyn Blockstore>, Arc<dyn OpHeadsStore>)> {
std::fs::create_dir_all(data_dir)
.with_context(|| format!("creating {}", data_dir.display()))?;
let db = db_path(data_dir);
let (bs, ohs, _) = open_or_init(&db)?;
Ok((bs, ohs))
}
pub(crate) fn open_repo(override_path: Option<&Path>) -> Result<ReadonlyRepo> {
let (_dir, repo, _bs, _ohs) = open_all(override_path)?;
Ok(repo)
}
pub(crate) fn open_all(
override_path: Option<&Path>,
) -> Result<(
PathBuf,
ReadonlyRepo,
Arc<dyn Blockstore>,
Arc<dyn OpHeadsStore>,
)> {
let data_dir = locate_data_dir(override_path)?;
let (bs, ohs) = open_stores(&data_dir)?;
let repo = ReadonlyRepo::open(bs.clone(), ohs.clone()).or_else(|e| {
if e.is_uninitialized() {
ReadonlyRepo::init(bs.clone(), ohs.clone()).map_err(anyhow::Error::from)
} else {
Err(anyhow!(e))
}
})?;
Ok((data_dir, repo, bs, ohs))
}