use std::path::PathBuf;
use dialoguer::Error as DialoguerError;
use rayon::ThreadPoolBuildError;
use mago_analyzer::error::AnalysisError;
use mago_database::error::DatabaseError;
use mago_orchestrator::OrchestratorError;
use mago_php_version::PHPVersion;
use mago_php_version::error::ParsingError;
use mago_reporting::error::ReportingError;
use crate::version_check::VersionPinParseError;
#[derive(Debug)]
pub enum Error {
Database(DatabaseError),
Reporting(ReportingError),
BuildingRuntime(std::io::Error),
ReadConfigFile { path: PathBuf, source: std::io::Error },
ParseConfigFile { path: PathBuf, source: Box<dyn std::error::Error + Send + Sync> },
UnsupportedConfigExtension(PathBuf),
EnvVarParse { name: &'static str, source: Box<dyn std::error::Error + Send + Sync> },
CircularExtends(PathBuf),
InvalidExtendsEntry { path: PathBuf, reason: String },
ExtendsTargetNotFound { entry: String, resolved: PathBuf, source: std::io::Error },
DeserializingToml(toml::de::Error),
SerializingToml(toml::ser::Error),
CanonicalizingPath(PathBuf, std::io::Error),
Json(serde_json::Error),
SelfUpdate(crate::updater::error::UpdateError),
PHPVersionIsTooOld(PHPVersion, PHPVersion),
PHPVersionIsTooNew(PHPVersion, PHPVersion),
InvalidPHPVersion(String, ParsingError),
Dialoguer(DialoguerError),
WritingConfiguration(std::io::Error),
ReadingComposerJson(std::io::Error),
ReadingBaselineFile(std::io::Error),
CreatingBaselineFile(std::io::Error),
ParsingComposerJson(serde_json::Error),
ThreadPoolBuildError(ThreadPoolBuildError),
Analysis(AnalysisError),
Orchestrator(OrchestratorError),
NotAGitRepository,
StagedFileHasUnstagedChanges(String),
UnknownFormatterPreset(String),
InvalidProjectVersionPin(VersionPinParseError),
ProjectMajorVersionMismatch(String, String),
ProjectVersionMismatch(String, String),
NoPinnedProjectVersion,
LatestReleaseDoesNotSatisfyPin(String, String),
InvalidArgument(String),
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Database(error) => write!(f, "Failed to load database: {error}"),
Self::Reporting(error) => write!(f, "Failed to report results: {error}"),
Self::BuildingRuntime(error) => write!(f, "Failed to build the runtime: {error}"),
Self::ReadConfigFile { path, source } => {
write!(f, "Failed to read configuration file `{}`: {source}", path.display())
}
Self::ParseConfigFile { path, source } => {
write!(f, "Failed to parse configuration file `{}`: {source}", path.display())
}
Self::UnsupportedConfigExtension(path) => {
write!(
f,
"Unsupported configuration file extension for `{}` (expected `.toml`, `.yaml`, `.yml`, or `.json`)",
path.display()
)
}
Self::EnvVarParse { name, source } => {
write!(f, "Invalid value for environment variable `{name}`: {source}")
}
Self::CircularExtends(path) => {
write!(f, "Configuration `extends` chain cycles back on `{}`", path.display())
}
Self::InvalidExtendsEntry { path, reason } => {
write!(f, "Invalid `extends` declaration in `{}`: {reason}", path.display())
}
Self::ExtendsTargetNotFound { entry, resolved, source } => {
write!(f, "Cannot resolve `extends` entry `{entry}` (looked at `{}`): {source}", resolved.display())
}
Self::DeserializingToml(error) => write!(f, "Failed to deserialize TOML: {error}"),
Self::SerializingToml(error) => write!(f, "Failed to serialize TOML: {error}"),
Self::CanonicalizingPath(path, error) => write!(f, "Failed to canonicalize path `{path:?}`: {error}"),
Self::Json(error) => write!(f, "Failed to parse JSON: {error}"),
Self::SelfUpdate(error) => write!(f, "Failed to self update: {error}"),
Self::PHPVersionIsTooOld(minimum, actual) => {
write!(f, "PHP version {actual} is not supported, minimum supported version is {minimum}")
}
Self::PHPVersionIsTooNew(maximum, actual) => {
write!(f, "PHP version {actual} is not supported, maximum supported version is {maximum}")
}
Self::InvalidPHPVersion(version, error) => {
write!(f, "Invalid PHP version `{version}`: {error}")
}
Self::Dialoguer(error) => write!(f, "Failed to interact with the user: {error}"),
Self::WritingConfiguration(error) => write!(f, "Failed to write the configuration file: {error}"),
Self::ReadingComposerJson(error) => write!(f, "Failed to read the `composer.json` file: {error}"),
Self::ParsingComposerJson(error) => write!(f, "Failed to parse the `composer.json` file: {error}"),
Self::ReadingBaselineFile(error) => write!(f, "Failed to read the baseline file: {error}"),
Self::CreatingBaselineFile(error) => write!(f, "Failed to create the baseline file: {error}"),
Self::Analysis(error) => write!(f, "Failed to analyze the source code: {error}"),
Self::ThreadPoolBuildError(error) => {
write!(f, "Failed to build the thread pool: {error}")
}
Self::Orchestrator(error) => write!(f, "Orchestrator error: {error}"),
Self::NotAGitRepository => write!(f, "Not inside a git repository"),
Self::StagedFileHasUnstagedChanges(path) => {
write!(f, "Cannot format staged files: '{path}' has both staged and unstaged changes")
}
Self::UnknownFormatterPreset(preset) => {
write!(f, "Unknown formatter preset: `{preset}`. Available presets are: laravel, psr12, default")
}
Self::InvalidArgument(message) => write!(f, "{message}"),
Self::InvalidProjectVersionPin(error) => write!(f, "{error}"),
Self::ProjectMajorVersionMismatch(pinned, installed) => {
write!(
f,
"mago.toml is pinned to major version `{pinned}`, but the installed mago binary is `{installed}`"
)
}
Self::ProjectVersionMismatch(pinned, installed) => {
write!(f, "mago.toml is pinned to `{pinned}`, but the installed mago binary is `{installed}`")
}
Self::NoPinnedProjectVersion => {
write!(f, "`self-update --to-project-version` requires a `version` pin in mago.toml")
}
Self::LatestReleaseDoesNotSatisfyPin(pinned, latest) => {
write!(
f,
"No published mago release satisfies the `version` pin `{pinned}` in mago.toml (most recent: `{latest}`)"
)
}
}
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Database(error) => Some(error),
Self::Reporting(error) => Some(error),
Self::ReadConfigFile { source, .. } => Some(source),
Self::ParseConfigFile { source, .. } => Some(source.as_ref()),
Self::UnsupportedConfigExtension(_) => None,
Self::EnvVarParse { source, .. } => Some(source.as_ref()),
Self::CircularExtends(_) => None,
Self::InvalidExtendsEntry { .. } => None,
Self::ExtendsTargetNotFound { source, .. } => Some(source),
Self::BuildingRuntime(error) => Some(error),
Self::DeserializingToml(error) => Some(error),
Self::SerializingToml(error) => Some(error),
Self::CanonicalizingPath(_, error) => Some(error),
Self::Json(error) => Some(error),
Self::SelfUpdate(error) => Some(error),
Self::InvalidPHPVersion(_, error) => Some(error),
Self::Dialoguer(error) => Some(error),
Self::WritingConfiguration(error) => Some(error),
Self::ReadingComposerJson(error) => Some(error),
Self::ParsingComposerJson(error) => Some(error),
Self::ReadingBaselineFile(error) => Some(error),
Self::CreatingBaselineFile(error) => Some(error),
Self::Analysis(error) => Some(error),
Self::ThreadPoolBuildError(error) => Some(error),
Self::Orchestrator(error) => Some(error),
Self::InvalidProjectVersionPin(error) => Some(error),
_ => None,
}
}
}
impl From<DatabaseError> for Error {
fn from(error: DatabaseError) -> Self {
Self::Database(error)
}
}
impl From<ReportingError> for Error {
fn from(error: ReportingError) -> Self {
Self::Reporting(error)
}
}
impl From<toml::de::Error> for Error {
fn from(error: toml::de::Error) -> Self {
Self::DeserializingToml(error)
}
}
impl From<toml::ser::Error> for Error {
fn from(error: toml::ser::Error) -> Self {
Self::SerializingToml(error)
}
}
impl From<serde_json::Error> for Error {
fn from(error: serde_json::Error) -> Self {
Self::Json(error)
}
}
impl From<crate::updater::error::UpdateError> for Error {
fn from(error: crate::updater::error::UpdateError) -> Self {
Self::SelfUpdate(error)
}
}
impl From<DialoguerError> for Error {
fn from(error: DialoguerError) -> Self {
Self::Dialoguer(error)
}
}
impl From<AnalysisError> for Error {
fn from(error: AnalysisError) -> Self {
Self::Analysis(error)
}
}
impl From<ThreadPoolBuildError> for Error {
fn from(error: ThreadPoolBuildError) -> Self {
Self::ThreadPoolBuildError(error)
}
}
impl From<OrchestratorError> for Error {
fn from(error: OrchestratorError) -> Self {
Self::Orchestrator(error)
}
}
impl From<VersionPinParseError> for Error {
fn from(error: VersionPinParseError) -> Self {
Self::InvalidProjectVersionPin(error)
}
}