use std::path::PathBuf;
use thiserror::Error;
pub type ChangesResult<T> = Result<T, ChangesError>;
#[derive(Debug, Error, Clone)]
pub enum ChangesError {
#[error("Git operation failed: {operation} - {reason}")]
GitError {
operation: String,
reason: String,
},
#[error("Invalid commit reference '{reference}': {reason}")]
InvalidCommitRef {
reference: String,
reason: String,
},
#[error("Invalid commit range from '{from}' to '{to}': {reason}")]
InvalidCommitRange {
from: String,
to: String,
reason: String,
},
#[error("Package not found for file '{file}' in workspace '{workspace_root}'")]
PackageNotFound {
file: PathBuf,
workspace_root: PathBuf,
},
#[error("No packages found in workspace at '{workspace_root}'")]
NoPackagesFound {
workspace_root: PathBuf,
},
#[error("Invalid file path '{path}': {reason}")]
InvalidPath {
path: PathBuf,
reason: String,
},
#[error("Filesystem error at '{path}': {reason}")]
FileSystemError {
path: PathBuf,
reason: String,
},
#[error("Failed to parse package.json at '{path}': {reason}")]
PackageJsonParseError {
path: PathBuf,
reason: String,
},
#[error("Working directory has uncommitted changes: {reason}")]
UncommittedChanges {
reason: String,
},
#[error("Failed to detect monorepo structure: {reason}")]
MonorepoDetectionFailed {
reason: String,
},
#[error("No changes detected in {scope}")]
NoChangesDetected {
scope: String,
},
#[error("File '{path}' is outside workspace root '{workspace_root}'")]
FileOutsideWorkspace {
path: PathBuf,
workspace_root: PathBuf,
},
#[error("Invalid workspace root '{path}': {reason}")]
InvalidWorkspaceRoot {
path: PathBuf,
reason: String,
},
#[error("Failed to compute statistics for '{file}': {reason}")]
StatisticsError {
file: PathBuf,
reason: String,
},
#[error("Pattern matching failed for pattern '{pattern}': {reason}")]
PatternError {
pattern: String,
reason: String,
},
#[error("Git repository not found at '{path}'")]
RepositoryNotFound {
path: PathBuf,
},
#[error("Merge conflict detected in file '{file}'")]
MergeConflict {
file: PathBuf,
},
#[error("Invalid changes analysis configuration: {reason}")]
InvalidConfig {
reason: String,
},
#[error("Changes analysis timed out after {duration_secs} seconds")]
Timeout {
duration_secs: u64,
},
#[error(
"Failed to calculate next version for package '{package}': cannot bump {current_version} with {bump_type} - {reason}"
)]
VersionCalculationFailed {
package: String,
current_version: String,
bump_type: String,
reason: String,
},
}
impl AsRef<str> for ChangesError {
fn as_ref(&self) -> &str {
match self {
Self::GitError { .. } => "git error",
Self::InvalidCommitRef { .. } => "invalid commit reference",
Self::InvalidCommitRange { .. } => "invalid commit range",
Self::PackageNotFound { .. } => "package not found",
Self::NoPackagesFound { .. } => "no packages found",
Self::InvalidPath { .. } => "invalid path",
Self::FileSystemError { .. } => "filesystem error",
Self::PackageJsonParseError { .. } => "package.json parse error",
Self::UncommittedChanges { .. } => "uncommitted changes",
Self::MonorepoDetectionFailed { .. } => "monorepo detection failed",
Self::NoChangesDetected { .. } => "no changes detected",
Self::FileOutsideWorkspace { .. } => "file outside workspace",
Self::InvalidWorkspaceRoot { .. } => "invalid workspace root",
Self::StatisticsError { .. } => "statistics error",
Self::PatternError { .. } => "pattern error",
Self::RepositoryNotFound { .. } => "repository not found",
Self::MergeConflict { .. } => "merge conflict",
Self::InvalidConfig { .. } => "invalid configuration",
Self::Timeout { .. } => "analysis timeout",
Self::VersionCalculationFailed { .. } => "version calculation failed",
}
}
}
impl ChangesError {
#[must_use]
pub fn is_transient(&self) -> bool {
matches!(
self,
Self::FileSystemError { .. }
| Self::GitError { .. }
| Self::Timeout { .. }
| Self::StatisticsError { .. }
)
}
#[must_use]
pub fn is_git_related(&self) -> bool {
matches!(
self,
Self::GitError { .. }
| Self::InvalidCommitRef { .. }
| Self::InvalidCommitRange { .. }
| Self::RepositoryNotFound { .. }
| Self::MergeConflict { .. }
| Self::UncommittedChanges { .. }
)
}
}