Skip to content
Merged
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ clap = { version = "4.6", features = ["derive"] }
comfy-table = "7.2"
directories = "6.0"
serde = { version = "1.0", features = ["derive"] }
thiserror = "2"
toml = "1.1"
59 changes: 11 additions & 48 deletions src/action.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
use std::env;
use std::error::Error;
use std::ffi::{OsStr, OsString};
use std::fmt;
use std::io;
use std::path::{Path, PathBuf};
use std::process::{Command, ExitStatus, Stdio};
Expand Down Expand Up @@ -178,27 +176,13 @@ impl PreparedCommand {
}
}

#[derive(Debug)]
#[derive(Debug, thiserror::Error)]
pub enum CommandPreparationError {
InvalidPathEnvironment { source: env::JoinPathsError },
}

impl fmt::Display for CommandPreparationError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::InvalidPathEnvironment { .. } => {
formatter.write_str("the effective PATH contains an invalid path entry")
}
}
}
}

impl Error for CommandPreparationError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::InvalidPathEnvironment { source } => Some(source),
}
}
#[error("the effective PATH contains an invalid path entry")]
InvalidPathEnvironment {
#[source]
source: env::JoinPathsError,
},
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
Expand Down Expand Up @@ -306,39 +290,18 @@ impl ProcessExecutor {
}
}

#[derive(Debug)]
#[derive(Debug, thiserror::Error)]
pub enum ExecutionError {
#[error("failed to start `{}`: {source}", .program.to_string_lossy())]
Spawn {
program: OsString,
#[source]
source: io::Error,
},
#[error("failed while waiting for `{}`: {source}", .program.to_string_lossy())]
Wait {
program: OsString,
#[source]
source: io::Error,
},
}

impl fmt::Display for ExecutionError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Spawn { program, source } => write!(
formatter,
"failed to start `{}`: {source}",
program.to_string_lossy()
),
Self::Wait { program, source } => write!(
formatter,
"failed while waiting for `{}`: {source}",
program.to_string_lossy()
),
}
}
}

impl Error for ExecutionError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::Spawn { source, .. } | Self::Wait { source, .. } => Some(source),
}
}
}
34 changes: 6 additions & 28 deletions src/action_runner.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use std::error::Error;
use std::fmt;

use crate::action::{
Expand Down Expand Up @@ -116,16 +115,21 @@ impl<'a> ActionRunner<'a> {
}
}

#[derive(Debug)]
#[derive(Debug, thiserror::Error)]
pub enum ActionRunError {
#[error("failed to prepare action {stage}: {source}")]
Preparation {
stage: ActionStage,
#[source]
source: CommandPreparationError,
},
#[error("failed to execute action {stage}: {source}")]
Execution {
stage: ActionStage,
#[source]
source: ExecutionError,
},
#[error("action {stage} returned {}", .result.status())]
UnsuccessfulExit {
stage: ActionStage,
result: ExecutionResult,
Expand All @@ -148,29 +152,3 @@ impl ActionRunError {
}
}
}

impl fmt::Display for ActionRunError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Preparation { stage, source } => {
write!(formatter, "failed to prepare action {stage}: {source}")
}
Self::Execution { stage, source } => {
write!(formatter, "failed to execute action {stage}: {source}")
}
Self::UnsuccessfulExit { stage, result } => {
write!(formatter, "action {stage} returned {}", result.status())
}
}
}
}

impl Error for ActionRunError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::Preparation { source, .. } => Some(source),
Self::Execution { source, .. } => Some(source),
Self::UnsuccessfulExit { .. } => None,
}
}
}
50 changes: 20 additions & 30 deletions src/app.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
#![expect(
clippy::result_large_err,
reason = "direct typed error sources preserve Error::source downcasts"
)]

mod apply;
mod check_providers;
mod command;
Expand All @@ -6,7 +11,6 @@ mod list_jobs;
mod list_profiles;
mod list_targets;

use std::error::Error;
use std::fmt;
use std::io::{self, IsTerminal};
use std::process::ExitCode;
Expand All @@ -16,6 +20,7 @@ pub use command::{Dispatch, ExecutionRequest, Operation, ProfileSelection, Scope
use crate::config::ConfigLoadError;
use crate::manifest::ManifestError;
use crate::output::{TableRenderer, TsvRecord, TsvRenderer};
use crate::plan::ExecutionPlanError;
use crate::platform::PlatformInfo;
use crate::report::{CommandReport, ReportStatus};

Expand Down Expand Up @@ -151,40 +156,25 @@ fn normalize_list_output(result: io::Result<()>) -> io::Result<()> {
}
}

#[derive(Debug)]
enum ListCommandError {
Config(ConfigLoadError),
Manifest(ManifestError),
}
#[derive(Debug, thiserror::Error)]
enum ManifestCommandError {
#[error("{0}")]
Config(#[from] ConfigLoadError),

impl fmt::Display for ListCommandError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Config(source) => source.fmt(formatter),
Self::Manifest(source) => source.fmt(formatter),
}
}
#[error("{0}")]
Manifest(#[from] ManifestError),
}

impl Error for ListCommandError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::Config(source) => Some(source),
Self::Manifest(source) => Some(source),
}
}
}
#[derive(Debug, thiserror::Error)]
enum ExecutionCommandError {
#[error("{0}")]
Config(#[from] ConfigLoadError),

impl From<ConfigLoadError> for ListCommandError {
fn from(source: ConfigLoadError) -> Self {
Self::Config(source)
}
}
#[error("{0}")]
Manifest(#[from] ManifestError),

impl From<ManifestError> for ListCommandError {
fn from(source: ManifestError) -> Self {
Self::Manifest(source)
}
#[error("{0}")]
Plan(#[from] ExecutionPlanError),
}

#[cfg(test)]
Expand Down
58 changes: 5 additions & 53 deletions src/app/apply.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
use std::error::Error;
use std::fmt;
use std::path::Path;

use super::ExecutionRequest;
use super::{ExecutionCommandError, ExecutionRequest};
use crate::action::ExecutionResult;
use crate::action_runner::{ActionOutcome, ActionRunError, ActionStage};
use crate::config::{ConfigLoadError, LoadedConfig};
use crate::config::LoadedConfig;
use crate::diagnostic::lookup;
use crate::interpolation::{DotPaths, XdgPaths};
use crate::job_runner::{BlockReason, JobExecutionReport, JobOutcome, JobRunner, JobState};
use crate::link::LinkOutcome;
use crate::manifest::{EffectiveManifest, ManifestError};
use crate::manifest::EffectiveManifest;
use crate::plan::{
ExecutionPlan, ExecutionPlanError, ExecutionPlanner, PlannedJob, PlannedPackage,
PlannedProviderInstall,
ExecutionPlan, ExecutionPlanner, PlannedJob, PlannedPackage, PlannedProviderInstall,
};
use crate::platform::PlatformInfo;
use crate::provider::{
Expand All @@ -28,7 +25,7 @@ use crate::report::{
pub(super) fn run(
config: &Path,
request: &ExecutionRequest,
) -> Result<CommandReport, CommandError> {
) -> Result<CommandReport, ExecutionCommandError> {
let loaded = LoadedConfig::load(config)?;
let platform = PlatformInfo::detect();
let manifest = EffectiveManifest::select_for_execution(
Expand Down Expand Up @@ -425,51 +422,6 @@ fn captured_text(output: Option<&[u8]>) -> Option<String> {
.map(|output| String::from_utf8_lossy(output).into_owned())
}

#[derive(Debug)]
pub(super) enum CommandError {
Config(ConfigLoadError),
Manifest(ManifestError),
Plan(ExecutionPlanError),
}

impl fmt::Display for CommandError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Config(source) => source.fmt(formatter),
Self::Manifest(source) => source.fmt(formatter),
Self::Plan(source) => source.fmt(formatter),
}
}
}

impl Error for CommandError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::Config(source) => Some(source),
Self::Manifest(source) => Some(source),
Self::Plan(source) => Some(source),
}
}
}

impl From<ConfigLoadError> for CommandError {
fn from(source: ConfigLoadError) -> Self {
Self::Config(source)
}
}

impl From<ManifestError> for CommandError {
fn from(source: ManifestError) -> Self {
Self::Manifest(source)
}
}

impl From<ExecutionPlanError> for CommandError {
fn from(source: ExecutionPlanError) -> Self {
Self::Plan(source)
}
}

#[cfg(test)]
mod tests {
use std::env;
Expand Down
47 changes: 4 additions & 43 deletions src/app/check_providers.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
use std::error::Error;
use std::fmt;

use super::ScopeSelection;
use super::{ManifestCommandError, ScopeSelection};
use crate::check::{ProviderChecker, build_report};
use crate::config::{ConfigLoadError, LoadedConfig};
use crate::config::LoadedConfig;
use crate::interpolation::{DotPaths, XdgPaths};
use crate::manifest::{EffectiveManifest, ManifestError};
use crate::manifest::EffectiveManifest;
use crate::platform::PlatformInfo;
use crate::report::CommandReport;

pub(super) fn run(
config: &std::path::Path,
scope: &ScopeSelection,
platform_override: Option<&PlatformInfo>,
) -> Result<CommandReport, CommandError> {
) -> Result<CommandReport, ManifestCommandError> {
let loaded = LoadedConfig::load(config)?;
let platform = platform_override
.cloned()
Expand All @@ -38,39 +35,3 @@ pub(super) fn run(
&checks,
))
}

#[derive(Debug)]
pub(super) enum CommandError {
Config(ConfigLoadError),
Manifest(ManifestError),
}

impl fmt::Display for CommandError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Config(source) => source.fmt(formatter),
Self::Manifest(source) => source.fmt(formatter),
}
}
}

impl Error for CommandError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::Config(source) => Some(source),
Self::Manifest(source) => Some(source),
}
}
}

impl From<ConfigLoadError> for CommandError {
fn from(source: ConfigLoadError) -> Self {
Self::Config(source)
}
}

impl From<ManifestError> for CommandError {
fn from(source: ManifestError) -> Self {
Self::Manifest(source)
}
}
Loading