use crate::error::Error;
use async_trait::async_trait;
use std::fs::File;
use std::path::PathBuf;
use std::sync::Arc;
use tokio::sync::Semaphore;
use super::{BlockRm, BlockRmError, Column, DataStore, Lock, LockError, RepoCid};
mod pinstore;
mod blocks;
pub use blocks::FsBlockStore;
mod paths;
use paths::{block_path, filestem_to_block_cid, filestem_to_pin_cid, pin_path};
#[derive(Debug)]
pub struct FsDataStore {
path: PathBuf,
lock: Arc<Semaphore>,
}
#[async_trait]
impl DataStore for FsDataStore {
fn new(root: PathBuf) -> Self {
FsDataStore {
path: root,
lock: Arc::new(Semaphore::new(1)),
}
}
async fn init(&self) -> Result<(), Error> {
tokio::fs::create_dir_all(&self.path).await?;
Ok(())
}
async fn open(&self) -> Result<(), Error> {
Ok(())
}
async fn contains(&self, _col: Column, _key: &[u8]) -> Result<bool, Error> {
Err(anyhow::anyhow!("not implemented"))
}
async fn get(&self, _col: Column, _key: &[u8]) -> Result<Option<Vec<u8>>, Error> {
Err(anyhow::anyhow!("not implemented"))
}
async fn put(&self, _col: Column, _key: &[u8], _value: &[u8]) -> Result<(), Error> {
Err(anyhow::anyhow!("not implemented"))
}
async fn remove(&self, _col: Column, _key: &[u8]) -> Result<(), Error> {
Err(anyhow::anyhow!("not implemented"))
}
async fn wipe(&self) {}
}
#[derive(Debug)]
pub struct FsLock {
file: Option<File>,
path: PathBuf,
state: State,
}
#[derive(Debug)]
enum State {
Unlocked,
Exclusive,
}
impl Lock for FsLock {
fn new(path: PathBuf) -> Self {
Self {
file: None,
path,
state: State::Unlocked,
}
}
fn try_exclusive(&mut self) -> Result<(), LockError> {
use fs2::FileExt;
use std::fs::OpenOptions;
let file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(&self.path)?;
file.try_lock_exclusive()?;
self.state = State::Exclusive;
self.file = Some(file);
Ok(())
}
}
#[cfg(test)]
crate::pinstore_interface_tests!(common_tests, crate::repo::fs::FsDataStore::new);
#[cfg(test)]
mod tests {
use super::{FsLock, Lock};
#[test]
fn creates_an_exclusive_repo_lock() {
let temp_dir = std::env::temp_dir();
let lockfile_path = temp_dir.join("repo_lock");
let mut lock = FsLock::new(lockfile_path.clone());
let result = lock.try_exclusive();
assert!(result.is_ok());
let mut failing_lock = FsLock::new(lockfile_path.clone());
let result = failing_lock.try_exclusive();
assert!(result.is_err());
std::fs::remove_file(lockfile_path).unwrap();
}
}