use std::sync::atomic::AtomicBool;
use super::{HunkResult, Limits, Patch, TreeOptions};
use crate::{Repository, filter};
#[cfg(any(target_os = "linux", target_os = "macos"))]
mod unix;
#[derive(Clone, Copy, Debug)]
#[non_exhaustive]
pub struct WorktreeLimits {
pub content: Limits,
pub index: crate::index::Limits,
pub max_retained_bytes: usize,
pub max_directory_entries: usize,
pub max_directories: usize,
pub max_path_bytes: usize,
pub max_depth: usize,
}
impl Default for WorktreeLimits {
fn default() -> Self {
Self {
content: Limits::default(),
index: crate::index::Limits::default(),
max_retained_bytes: 64 * 1024 * 1024,
max_directory_entries: 1_000_000,
max_directories: 10_000,
max_path_bytes: 16 * 1024 * 1024,
max_depth: 128,
}
}
}
#[derive(Debug)]
pub struct WorktreeFile {
pub path: Vec<u8>,
pub source_path: Option<Vec<u8>>,
pub hunks: Vec<HunkResult>,
pub unfiltered: bool,
pub crlf_warning: Option<filter::CrlfWarning>,
pub(super) content: Option<Vec<u8>>,
pub(super) executable: bool,
}
impl WorktreeFile {
pub fn executable(&self) -> bool {
self.executable
}
pub fn content(&self) -> Option<&[u8]> {
self.content.as_deref()
}
}
#[derive(Debug)]
pub struct PreparedWorktree {
pub(super) files: Vec<WorktreeFile>,
#[cfg(any(target_os = "linux", target_os = "macos"))]
snapshot: unix::Snapshot,
}
impl PreparedWorktree {
pub fn verify(&mut self) -> Result<(), WorktreeError> {
self.verify_with(&AtomicBool::new(false))
}
pub fn verify_with(&mut self, cancel: &AtomicBool) -> Result<(), WorktreeError> {
#[cfg(any(target_os = "linux", target_os = "macos"))]
{
self.snapshot.verify(cancel)
}
#[cfg(not(any(target_os = "linux", target_os = "macos")))]
{
let _ = cancel;
Err(WorktreeError::UnsupportedPlatform)
}
}
pub fn files(&self) -> &[WorktreeFile] {
&self.files
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum WorktreeAction {
Removed,
Created,
DirectoryCreated,
}
#[derive(Clone, Debug)]
#[non_exhaustive]
pub struct WorktreeWrite {
pub path: Vec<u8>,
pub action: WorktreeAction,
pub complete: bool,
}
#[derive(Debug, Default)]
#[non_exhaustive]
pub struct WorktreeReport {
pub writes: Vec<WorktreeWrite>,
}
#[derive(Debug, thiserror::Error)]
#[error("working-file patch publication failed")]
#[non_exhaustive]
pub struct WorktreeFailure {
#[source]
pub cause: Box<WorktreeError>,
pub report: WorktreeReport,
}
impl PreparedWorktree {
pub fn publish(self) -> Result<WorktreeReport, WorktreeFailure> {
self.publish_with(&AtomicBool::new(false))
}
pub fn publish_with(self, cancel: &AtomicBool) -> Result<WorktreeReport, WorktreeFailure> {
#[cfg(any(target_os = "linux", target_os = "macos"))]
{
unix::publish(self, cancel, &mut |_, _| Ok(()))
}
#[cfg(not(any(target_os = "linux", target_os = "macos")))]
{
let _ = cancel;
Err(WorktreeFailure {
cause: Box::new(WorktreeError::UnsupportedPlatform),
report: WorktreeReport::default(),
})
}
}
}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum WorktreeError {
#[error("preparing working-file patch")]
Patch(#[from] super::TreeError),
#[error("checking working-file snapshot")]
Status(#[from] crate::status::Error),
#[error("reading working-file preimage")]
Checkout(#[from] crate::checkout::Error),
#[error("converting patch content")]
Filter(#[from] filter::Error),
#[error("reading patch filter context")]
Index(#[from] crate::index::StorageError),
#[error("reading staged patch filter context")]
ReadBlob(#[from] crate::ReadBlobError),
#[error("opening patch filter objects")]
Objects(#[from] crate::ObjectReadError),
#[error("working-file patch I/O failed")]
Io {
path: Vec<u8>,
#[source]
source: std::io::Error,
},
#[error("working-file patch refused: {reason}")]
Refused {
path: Vec<u8>,
reason: &'static str,
},
#[error("working-file patch limit exceeded: {0}")]
Limit(&'static str),
#[error("working-file patch cancelled")]
Cancelled,
#[error("guarded working-file patches require Linux or macOS")]
UnsupportedPlatform,
}
impl Patch {
pub fn prepare_worktree(
&self,
repo: &Repository,
filters: &mut filter::Pipeline,
) -> Result<PreparedWorktree, WorktreeError> {
self.prepare_worktree_with(
repo,
filters,
TreeOptions::default(),
WorktreeLimits::default(),
&AtomicBool::new(false),
)
}
pub fn prepare_worktree_with(
&self,
repo: &Repository,
filters: &mut filter::Pipeline,
options: TreeOptions,
limits: WorktreeLimits,
cancel: &AtomicBool,
) -> Result<PreparedWorktree, WorktreeError> {
#[cfg(any(target_os = "linux", target_os = "macos"))]
{
unix::prepare(self, repo, filters, options, limits, cancel)
}
#[cfg(not(any(target_os = "linux", target_os = "macos")))]
{
let _ = (repo, filters, options, limits, cancel);
Err(WorktreeError::UnsupportedPlatform)
}
}
}