use std::path::PathBuf;
use crate::error::Result;
use crate::recover::{Decision, Pos, classify, decide};
use crate::state::{GC_PREFIX, PROBE_PREFIX, TX_PREFIX, TxDir, private_dir};
use crate::vfs::{FileId, Kind, Vfs};
#[derive(Clone, Debug, Default, PartialEq, Eq)]
#[non_exhaustive]
pub struct Inspection {
pub transactions: Vec<TxInspection>,
pub garbage: Vec<String>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub struct TxInspection {
pub name: String,
pub action: RecoveryAction,
pub reason: Option<String>,
pub tokens: Vec<TokenInspection>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum RecoveryAction {
Discard,
CleanUpCommitted,
CleanUpRolledBack,
RollBack,
RecoveryRequired,
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub struct TokenInspection {
pub kind: Kind,
pub id: FileId,
pub locations: Vec<PathBuf>,
pub position: std::result::Result<TokenPosition, String>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct TokenPosition {
pub index: usize,
pub both_names: bool,
}
pub(crate) fn inspect_on(vfs: &dyn Vfs) -> Result<Inspection> {
let mut out = Inspection::default();
if vfs.stat(&private_dir())?.is_none() {
return Ok(out);
}
let _lock = vfs.lock(false)?;
let mut names: Vec<String> = vfs
.read_dir(&private_dir())?
.into_iter()
.filter_map(|n| n.into_string().ok())
.collect();
names.sort();
for name in names {
if name.starts_with(GC_PREFIX) || name.starts_with(PROBE_PREFIX) {
out.garbage.push(name);
continue;
}
if !name.starts_with(TX_PREFIX) {
continue;
}
let tx = TxDir { name: name.clone() };
let (action, reason, tokens) = match decide(vfs, &tx)? {
Decision::Discard => (RecoveryAction::Discard, None, Vec::new()),
Decision::Committed => (RecoveryAction::CleanUpCommitted, None, Vec::new()),
Decision::RolledBack => (RecoveryAction::CleanUpRolledBack, None, Vec::new()),
Decision::Required(r) => (RecoveryAction::RecoveryRequired, Some(r), Vec::new()),
Decision::RollBack(j) => {
let classes = classify(vfs, &j.plan)?;
let mut reason = None;
let tokens = j
.plan
.tokens
.iter()
.zip(classes)
.map(|(t, c)| TokenInspection {
kind: t.kind,
id: t.id,
locations: t.locs.iter().map(|l| l.path.to_path_buf()).collect(),
position: c
.map(|p| match p {
Pos::At(i) => TokenPosition {
index: i,
both_names: false,
},
Pos::Both(i) => TokenPosition {
index: i,
both_names: true,
},
})
.inspect_err(|e| {
reason.get_or_insert_with(|| e.clone());
}),
})
.collect();
let action = if reason.is_some() {
RecoveryAction::RecoveryRequired
} else {
RecoveryAction::RollBack
};
(action, reason, tokens)
}
};
out.transactions.push(TxInspection {
name,
action,
reason,
tokens,
});
}
Ok(out)
}