use crate::VcsName;
use camino::Utf8PathBuf;
use git_stub::{GitStub, GitStubParseError};
use std::{ffi::OsString, io};
use thiserror::Error;
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum VcsEnvError {
#[error(
"${var} environment variable is not valid \
UTF-8: {value:?}"
)]
NonUtf8 {
var: &'static str,
value: OsString,
},
}
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum VcsDetectError {
#[error(
"{repo_root} does not exist \
(expected a repository root with .git or .jj)"
)]
PathNotFound {
repo_root: Utf8PathBuf,
},
#[error(
"{repo_root} is not a directory \
(expected a repository root with .git or .jj)"
)]
NotADirectory {
repo_root: Utf8PathBuf,
},
#[error("I/O error while checking for VCS at {path}")]
Io {
path: Utf8PathBuf,
#[source]
source: io::Error,
},
#[error("no VCS found at {repo_root} (expected .git or .jj)")]
NotFound {
repo_root: Utf8PathBuf,
},
#[error(transparent)]
Env(#[from] VcsEnvError),
}
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum ShallowCloneError {
#[error("failed to run {vcs_name} at {binary_path:?} in {repo_root}")]
SpawnFailed {
vcs_name: VcsName,
binary_path: String,
repo_root: Utf8PathBuf,
#[source]
source: io::Error,
},
#[error(
"{vcs_name} failed to check for shallow clone \
({exit_status}): {stderr}"
)]
VcsFailed {
vcs_name: VcsName,
exit_status: String,
stderr: String,
},
#[error("I/O error while checking for shallow clone at {path}")]
Io {
path: Utf8PathBuf,
#[source]
source: io::Error,
},
#[error(
"{vcs_name} returned unexpected output for shallow clone \
check: expected \"true\" or \"false\", got {stdout:?}"
)]
UnexpectedOutput {
vcs_name: VcsName,
stdout: String,
},
}
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum ReadContentsError {
#[error("failed to run {vcs_name} at {binary_path:?} in {repo_root}")]
SpawnFailed {
vcs_name: VcsName,
binary_path: String,
repo_root: Utf8PathBuf,
#[source]
source: io::Error,
},
#[error("{vcs_name} failed to read {stub} ({exit_status}): {stderr}")]
VcsFailed {
vcs_name: VcsName,
stub: GitStub,
exit_status: String,
stderr: String,
},
}
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum MaterializeError {
#[error(
"path {path:?} contains non-normal component {component:?} \
(only plain file/directory names are allowed)"
)]
InvalidPathComponent {
path: Utf8PathBuf,
component: String,
},
#[error("path does not end with .gitstub: {path}")]
NotGitStub {
path: Utf8PathBuf,
},
#[error("failed to read Git stub {path}")]
ReadGitStub {
path: Utf8PathBuf,
#[source]
error: io::Error,
},
#[error("invalid Git stub format in {path}")]
InvalidGitStub {
path: Utf8PathBuf,
#[source]
error: GitStubParseError,
},
#[error("VCS detection failed")]
VcsDetect(#[from] VcsDetectError),
#[error("failed to read git stub contents")]
ReadContents(#[from] ReadContentsError),
#[error("failed to check for shallow clone at {repo_root}")]
ShallowCloneCheck {
repo_root: Utf8PathBuf,
#[source]
error: ShallowCloneError,
},
#[error(
"shallow clone detected at {repo_root}: cannot dereference \
git stubs without full history{}", shallow_clone_msg(.vcs),
)]
ShallowClone {
vcs: VcsName,
repo_root: Utf8PathBuf,
},
#[error("failed to create output directory {path}")]
CreateDir {
path: Utf8PathBuf,
#[source]
error: io::Error,
},
#[error("failed to write materialized spec to {path}")]
WriteOutput {
path: Utf8PathBuf,
#[source]
error: AtomicWriteError,
},
}
fn shallow_clone_msg(vcs: &VcsName) -> &'static str {
match vcs {
VcsName::Git => "(run `git fetch --unshallow`)",
VcsName::Jj => {
"(if this is a colocated repository, \
run `git fetch --unshallow`)"
}
}
}
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum AtomicWriteError {
#[error("writing file contents failed")]
Write(#[source] io::Error),
#[error("atomic create or rename failed")]
Rename(#[source] io::Error),
}