use super::*;
pub(super) enum NamedEntry {
Absent,
Importable(fs::Metadata),
Rejected(String),
}
#[derive(Clone, Copy)]
pub(super) struct NamedSessionStore {
harness: &'static str,
stored: &'static str,
}
pub(super) const CLAUDE_STORE: NamedSessionStore = NamedSessionStore {
harness: "Claude",
stored: "transcripts",
};
pub(super) const CODEX_STORE: NamedSessionStore = NamedSessionStore {
harness: "Codex",
stored: "rollouts",
};
pub(super) const KIMI_STORE: NamedSessionStore = NamedSessionStore {
harness: "Kimi",
stored: "session directories",
};
pub(super) const GROK_STORE: NamedSessionStore = NamedSessionStore {
harness: "Grok Build",
stored: "session directories",
};
impl NamedSessionStore {
pub fn file(self, path: &Path) -> NamedEntry {
self.classify(path, false)
}
pub fn directory(self, path: &Path) -> NamedEntry {
self.classify(path, true)
}
fn classify(self, path: &Path, want_directory: bool) -> NamedEntry {
let metadata = match fs::symlink_metadata(path) {
Ok(metadata) => metadata,
Err(error) if error.kind() == std::io::ErrorKind::NotFound => {
return NamedEntry::Absent;
}
Err(error) => {
return NamedEntry::Rejected(format!("{} cannot be read: {error}", path.display()));
}
};
if metadata.file_type().is_symlink() {
return NamedEntry::Rejected(format!(
"{} is a symlink; {}",
path.display(),
self.only_stored_directly()
));
}
if want_directory {
if !metadata.is_dir() {
return NamedEntry::Rejected(format!("{} is not a directory", path.display()));
}
} else if !metadata.is_file() {
return NamedEntry::Rejected(format!("{} is not a regular file", path.display()));
}
NamedEntry::Importable(metadata)
}
pub fn symlinked_container(self, path: &Path, described_as: &str) -> String {
format!(
"{} is a symlinked {described_as}; {}",
path.display(),
self.only_stored_directly()
)
}
pub fn no_cwd(self, path: &Path) -> String {
format!("{} session {} has no cwd", self.harness, path.display())
}
pub fn cannot_import(self, native_session_id: &str, rejected: &[String]) -> anyhow::Error {
anyhow::anyhow!(
"{} session {native_session_id:?} cannot be imported: {}",
self.harness,
rejected.join("; ")
)
}
fn only_stored_directly(self) -> String {
format!(
"Mjolnir imports only {} stored directly in the {} home",
self.stored, self.harness
)
}
}