use crate::{ObjectId, TreeChange, TreeCompareLimits, TreeValue, index};
#[derive(Clone, Copy, Debug)]
#[non_exhaustive]
pub enum Baseline {
Head,
Tree(Option<ObjectId>),
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum Untracked {
Omit,
RawFilesWithoutIgnores,
}
#[derive(Clone, Copy, Debug)]
#[non_exhaustive]
pub struct Limits {
pub index: index::Limits,
pub trees: TreeCompareLimits,
pub packs: crate::PackLimits,
pub read: crate::ReadLimits,
pub max_object_bytes: usize,
pub max_file_bytes: usize,
pub max_worktree_bytes: usize,
pub max_directory_entries: usize,
pub max_path_bytes: usize,
pub max_depth: usize,
}
impl Limits {
#[must_use]
pub fn with_index(mut self, index: index::Limits) -> Self {
self.index = index;
self
}
#[must_use]
pub fn with_trees(mut self, trees: TreeCompareLimits) -> Self {
self.trees = trees;
self
}
#[must_use]
pub fn with_packs(mut self, packs: crate::PackLimits) -> Self {
self.packs = packs;
self
}
#[must_use]
pub fn with_read(mut self, read: crate::ReadLimits) -> Self {
self.read = read;
self
}
#[must_use]
pub fn with_max_object_bytes(mut self, max_object_bytes: usize) -> Self {
self.max_object_bytes = max_object_bytes;
self
}
#[must_use]
pub fn with_max_file_bytes(mut self, max_file_bytes: usize) -> Self {
self.max_file_bytes = max_file_bytes;
self
}
#[must_use]
pub fn with_max_worktree_bytes(mut self, max_worktree_bytes: usize) -> Self {
self.max_worktree_bytes = max_worktree_bytes;
self
}
#[must_use]
pub fn with_max_directory_entries(mut self, max_directory_entries: usize) -> Self {
self.max_directory_entries = max_directory_entries;
self
}
#[must_use]
pub fn with_max_path_bytes(mut self, max_path_bytes: usize) -> Self {
self.max_path_bytes = max_path_bytes;
self
}
#[must_use]
pub fn with_max_depth(mut self, max_depth: usize) -> Self {
self.max_depth = max_depth;
self
}
}
impl Default for Limits {
fn default() -> Self {
Self {
index: index::Limits::default(),
trees: TreeCompareLimits::default(),
packs: crate::PackLimits::default(),
read: crate::ReadLimits::default(),
max_object_bytes: 256 * 1024 * 1024,
max_file_bytes: 16 * 1024 * 1024,
max_worktree_bytes: 256 * 1024 * 1024,
max_directory_entries: 1_000_000,
max_path_bytes: 64 * 1024 * 1024,
max_depth: 64,
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum Comparison {
RawBytesAndPosixModes,
}
#[derive(Debug)]
#[non_exhaustive]
pub struct Report {
pub comparison: Comparison,
pub baseline_tree: Option<ObjectId>,
pub index_missing: bool,
pub staged: Vec<TreeChange>,
pub unstaged: Vec<WorktreeChange>,
pub unmerged: Vec<index::Entry>,
pub unchecked_gitlinks: Vec<index::Entry>,
pub untracked_policy: Untracked,
pub raw_untracked: Vec<Vec<u8>>,
pub boundaries: Vec<Vec<u8>>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub struct WorktreeChange {
pub path: Vec<u8>,
pub index: TreeValue,
pub change: Change,
}
#[derive(Clone, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum Change {
Deleted,
Modified(TreeValue),
Obstructed {
path: Vec<u8>,
},
}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum Error {
#[error("unsupported status observation at {path:?}: {reason}")]
Unsupported {
path: Vec<u8>,
reason: &'static str,
},
#[error("status limit exceeded: {0}")]
Limit(&'static str),
#[error("status cancelled")]
Cancelled,
#[error("concurrent status change at {0:?}")]
Changed(Vec<u8>),
#[error("status I/O at {path:?}")]
Io {
path: Vec<u8>,
#[source]
source: std::io::Error,
},
#[error(transparent)]
Index(#[from] index::StorageError),
#[error(transparent)]
Reference(#[from] crate::refs::ReferenceError),
#[error(transparent)]
Tree(#[from] crate::TreeCompareError),
#[error("cannot open status object reader")]
Objects(#[source] crate::ObjectReadError),
#[error("reading status object {id} at {path:?}")]
Object {
id: ObjectId,
path: Vec<u8>,
#[source]
source: Box<crate::ObjectReadError>,
},
#[error("invalid status object {id} at {path:?}: {reason}")]
InvalidObject {
id: ObjectId,
path: Vec<u8>,
reason: &'static str,
},
#[error("invalid HEAD commit")]
Commit(#[from] crate::CommitError),
}
pub(super) fn check(cancel: &std::sync::atomic::AtomicBool) -> Result<(), Error> {
if cancel.load(std::sync::atomic::Ordering::Relaxed) {
Err(Error::Cancelled)
} else {
Ok(())
}
}
#[cfg(any(target_os = "linux", target_os = "macos"))]
pub(super) fn charge(
remaining: &mut usize,
count: usize,
label: &'static str,
) -> Result<(), Error> {
*remaining = remaining.checked_sub(count).ok_or(Error::Limit(label))?;
Ok(())
}
#[cfg(any(target_os = "linux", target_os = "macos"))]
pub(super) fn value(entry: &index::Entry) -> TreeValue {
TreeValue {
id: entry.id,
mode: match entry.mode {
index::Mode::Regular => crate::EntryMode::Blob,
index::Mode::Executable => crate::EntryMode::Executable,
index::Mode::Symlink => crate::EntryMode::Symlink,
index::Mode::SparseDirectory => crate::EntryMode::Tree,
index::Mode::Gitlink => crate::EntryMode::Gitlink,
},
}
}