const DEFAULT_CONTENT_THRESHOLD: u64 = 10 * 1024;
const MAX_COMPRESS_SIZE: u64 = 100 * 1024;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Default)]
pub struct FileSnapshot {
pub path: String,
pub checksum: String,
pub size: u64,
pub content: Option<String>,
pub compressed_content: Option<String>,
pub exists: bool,
}
impl FileSnapshot {
#[must_use]
pub fn new(path: &str, checksum: String, size: u64, exists: bool) -> Self {
Self {
path: path.to_string(),
checksum,
size,
content: None,
compressed_content: None,
exists,
}
}
pub fn from_workspace_default(
workspace: &dyn Workspace,
path: &str,
checksum: String,
size: u64,
exists: bool,
) -> Self {
Self::from_workspace(
workspace,
path,
checksum,
size,
exists,
DEFAULT_CONTENT_THRESHOLD,
)
}
pub fn from_workspace(
workspace: &dyn Workspace,
path: &str,
checksum: String,
size: u64,
exists: bool,
max_size: u64,
) -> Self {
let content = if exists && size < max_size {
workspace.read(Path::new(path)).ok()
} else {
None
};
let compressed_content = if exists
&& (path.contains("PROMPT.md")
|| path.contains("PLAN.md")
|| path.contains("ISSUES.md")
|| path.contains("NOTES.md"))
&& size >= max_size
&& size < MAX_COMPRESS_SIZE
{
workspace.read_bytes(Path::new(path)).ok().and_then(|data| {
crate::checkpoint::execution_history::compression::compress(&data).ok()
})
} else {
None
};
Self {
path: path.to_string(),
checksum,
size,
content,
compressed_content,
exists,
}
}
#[must_use]
pub fn get_content(&self) -> Option<String> {
self.content.clone().or_else(|| {
self.compressed_content.as_ref().and_then(|compressed| {
crate::checkpoint::execution_history::compression::decompress(compressed).ok()
})
})
}
#[must_use]
pub fn not_found(path: &str) -> Self {
Self {
path: path.to_string(),
checksum: String::new(),
size: 0,
content: None,
compressed_content: None,
exists: false,
}
}
pub fn verify_with_workspace(&self, workspace: &dyn Workspace) -> bool {
let path = Path::new(&self.path);
if !self.exists {
return !workspace.exists(path);
}
let Ok(content) = workspace.read_bytes(path) else {
return false;
};
if content.len() as u64 != self.size {
return false;
}
let checksum = crate::checkpoint::state::calculate_checksum_from_bytes(&content);
checksum == self.checksum
}
}