use std::io;
use crate::path::{PRIVATE_DIR, RelPath};
use crate::vfs::{Kind, Vfs};
pub(crate) const TX_PREFIX: &str = "tx-";
pub(crate) const GC_PREFIX: &str = "gc-";
pub(crate) const PROBE_PREFIX: &str = "probe-";
pub(crate) fn private_dir() -> RelPath {
RelPath::from_parts([PRIVATE_DIR])
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum Marker {
Committed,
RollingBack,
RolledBack,
}
impl Marker {
fn name(self) -> &'static str {
match self {
Marker::Committed => "COMMITTED",
Marker::RollingBack => "ROLLING_BACK",
Marker::RolledBack => "ROLLED_BACK",
}
}
}
#[derive(Clone, Debug)]
pub(crate) struct TxDir {
pub name: String,
}
impl TxDir {
pub fn dir(&self) -> RelPath {
private_dir().join(&self.name)
}
pub fn staged(&self) -> RelPath {
self.dir().join("staged")
}
pub fn backup(&self) -> RelPath {
self.dir().join("backup")
}
pub fn journal(&self) -> RelPath {
self.dir().join("journal")
}
pub fn journal_tmp(&self) -> RelPath {
self.dir().join("journal.tmp")
}
pub fn progress(&self) -> RelPath {
self.dir().join("progress")
}
pub fn marker(&self, m: Marker) -> RelPath {
self.dir().join(m.name())
}
pub fn has_marker(&self, vfs: &dyn Vfs, m: Marker) -> io::Result<bool> {
Ok(vfs.stat(&self.marker(m))?.is_some())
}
pub fn set_marker(&self, vfs: &dyn Vfs, m: Marker) -> io::Result<()> {
match vfs.create_file(&self.marker(m), b"", None) {
Ok(_) => {}
Err(e) if e.kind() == io::ErrorKind::AlreadyExists => {}
Err(e) => return Err(e),
}
vfs.sync_dir(&self.dir())
}
pub fn gc(&self, vfs: &dyn Vfs) -> io::Result<()> {
let gc = private_dir().join(format!("{GC_PREFIX}{}", self.name));
vfs.rename_noreplace(&self.dir(), None, &gc, None)?;
vfs.sync_dir(&private_dir())?;
delete_tree(vfs, &gc)?;
vfs.sync_dir(&private_dir())
}
}
pub(crate) fn delete_tree(vfs: &dyn Vfs, p: &RelPath) -> io::Result<()> {
debug_assert!(p.is_private());
match vfs.stat(p)? {
None => Ok(()),
Some(m) if m.kind == Kind::Dir => {
for name in vfs.read_dir(p)? {
delete_tree(vfs, &p.join(name))?;
}
vfs.rmdir(p)
}
Some(_) => vfs.unlink(p, None),
}
}