From 7c776e07fdb3dfb34ba0108e491c3392b9b80b3e Mon Sep 17 00:00:00 2001 From: Bas Zalmstra Date: Thu, 13 Jul 2023 14:45:33 +0200 Subject: [PATCH 1/3] refactor: use miette instead of anyhow and ariadne --- Cargo.lock | 153 +++++++++++++++++++++++++++---- Cargo.toml | 3 +- src/cli/add.rs | 19 ++-- src/cli/auth.rs | 27 +++--- src/cli/global/install.rs | 75 +++++++++------ src/cli/global/mod.rs | 2 +- src/cli/info.rs | 8 +- src/cli/init.rs | 18 ++-- src/cli/install.rs | 2 +- src/cli/mod.rs | 16 ++-- src/cli/run.rs | 26 +++--- src/cli/shell.rs | 35 ++++--- src/cli/task.rs | 2 +- src/environment.rs | 100 ++++++++++---------- src/lib.rs | 1 - src/main.rs | 9 +- src/prefix.rs | 11 ++- src/project/environment.rs | 5 +- src/project/manifest.rs | 51 ++++------- src/project/mod.rs | 151 ++++++++++++++++-------------- src/repodata.rs | 22 +++-- src/report_error.rs | 27 ------ src/virtual_packages.rs | 15 +-- tests/common/builders.rs | 10 +- tests/common/mod.rs | 15 +-- tests/common/package_database.rs | 15 ++- 26 files changed, 472 insertions(+), 346 deletions(-) delete mode 100644 src/report_error.rs diff --git a/Cargo.lock b/Cargo.lock index 4213afea6..e7c2d2dd0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -46,6 +46,17 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom 0.2.10", + "once_cell", + "version_check", +] + [[package]] name = "aho-corasick" version = "1.0.2" @@ -287,16 +298,6 @@ dependencies = [ "xz2", ] -[[package]] -name = "ariadne" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72fe02fc62033df9ba41cba57ee19acf5e742511a140c7dbc3a873e19a19a1bd" -dependencies = [ - "unicode-width", - "yansi", -] - [[package]] name = "asn1-rs" version = "0.5.2" @@ -814,6 +815,15 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "backtrace-ext" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "537beee3be4a18fb023b570f80e3ae28003db9167a751266b259926e25539d50" +dependencies = [ + "backtrace", +] + [[package]] name = "base16ct" version = "0.1.1" @@ -1103,7 +1113,7 @@ dependencies = [ "bitflags", "clap_lex", "strsim", - "terminal_size", + "terminal_size 0.2.6", ] [[package]] @@ -1939,6 +1949,9 @@ name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash", +] [[package]] name = "hashbrown" @@ -2253,6 +2266,12 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "is_ci" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "616cde7c720bb2bb5824a224687d8f77bfd38922027f01d825cd7453be5099fb" + [[package]] name = "is_executable" version = "1.0.1" @@ -2514,6 +2533,38 @@ dependencies = [ "autocfg", ] +[[package]] +name = "miette" +version = "5.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a236ff270093b0b67451bc50a509bd1bad302cb1d3c7d37d5efe931238581fa9" +dependencies = [ + "backtrace", + "backtrace-ext", + "is-terminal", + "miette-derive", + "once_cell", + "owo-colors", + "supports-color", + "supports-hyperlinks", + "supports-unicode", + "terminal_size 0.1.17", + "textwrap", + "thiserror", + "unicode-width", +] + +[[package]] +name = "miette-derive" +version = "5.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4901771e1d44ddb37964565c654a3223ba41a594d02b8da471cc4464912b5cfa" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.22", +] + [[package]] name = "mime" version = "0.3.17" @@ -2895,6 +2946,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +[[package]] +name = "owo-colors" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" + [[package]] name = "p12" version = "0.6.3" @@ -3020,8 +3077,6 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" name = "pixi" version = "0.0.7" dependencies = [ - "anyhow", - "ariadne", "clap", "clap-verbosity-flag", "clap_complete", @@ -3035,6 +3090,7 @@ dependencies = [ "insta", "is_executable", "itertools", + "miette", "minijinja", "once_cell", "rattler", @@ -4145,6 +4201,12 @@ dependencies = [ "serde", ] +[[package]] +name = "smawk" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f67ad224767faa3c7d8b6d91985b78e70a1324408abcb1cfcc2be4c06bc06043" + [[package]] name = "snafu" version = "0.7.4" @@ -4251,6 +4313,34 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab16ced94dbd8a46c82fd81e3ed9a8727dac2977ea869d217bcc4ea1f122e81f" +[[package]] +name = "supports-color" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4950e7174bffabe99455511c39707310e7e9b440364a2fcb1cc21521be57b354" +dependencies = [ + "is-terminal", + "is_ci", +] + +[[package]] +name = "supports-hyperlinks" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84231692eb0d4d41e4cdd0cabfdd2e6cd9e255e65f80c9aa7c98dd502b4233d" +dependencies = [ + "is-terminal", +] + +[[package]] +name = "supports-unicode" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b6c2cb240ab5dd21ed4906895ee23fe5a48acdbd15a3ce388e7b62a9b66baf7" +dependencies = [ + "is-terminal", +] + [[package]] name = "syn" version = "1.0.109" @@ -4340,6 +4430,16 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "terminal_size" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "terminal_size" version = "0.2.6" @@ -4350,6 +4450,17 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "textwrap" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7b3e525a49ec206798b40326a44121291b530c963cfb01018f63e135bac543d" +dependencies = [ + "smawk", + "unicode-linebreak", + "unicode-width", +] + [[package]] name = "thiserror" version = "1.0.40" @@ -4708,6 +4819,16 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" +[[package]] +name = "unicode-linebreak" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5faade31a542b8b35855fff6e8def199853b2da8da256da52f52f1316ee3137" +dependencies = [ + "hashbrown 0.12.3", + "regex", +] + [[package]] name = "unicode-normalization" version = "0.1.22" @@ -5241,12 +5362,6 @@ dependencies = [ "linked-hash-map", ] -[[package]] -name = "yansi" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" - [[package]] name = "yasna" version = "0.5.2" diff --git a/Cargo.toml b/Cargo.toml index baa3e3736..8139eb23b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,8 +16,6 @@ rustls-tls = ["reqwest/rustls-tls", "rattler_repodata_gateway/rustls-tls", "ratt slow_integration_tests = [] [dependencies] -anyhow = "1.0.70" -ariadne = "0.3.0" clap = { version = "4.2.4", default-features = false, features = ["derive", "usage", "wrap_help", "std", "color", "error-context"] } clap-verbosity-flag = "2.0.1" clap_complete = "4.2.1" @@ -32,6 +30,7 @@ indicatif = "0.17.3" insta = { version = "1.29.0", features = ["yaml"] } is_executable = "1.0.1" itertools = "0.10.5" +miette = { version = "5.9.0", features = ["fancy", "supports-color", "supports-hyperlinks", "supports-unicode", "terminal_size", "textwrap"] } minijinja = { version = "0.34.0", features = ["builtins"] } once_cell = "1.17.1" rattler = { default-features = false, git = "https://github.com/mamba-org/rattler", branch = "main" } diff --git a/src/cli/add.rs b/src/cli/add.rs index ca67292dc..04b0bb396 100644 --- a/src/cli/add.rs +++ b/src/cli/add.rs @@ -3,10 +3,10 @@ use crate::{ project::Project, virtual_packages::get_minimal_virtual_packages, }; -use anyhow::Context; use clap::Parser; use indexmap::IndexMap; use itertools::Itertools; +use miette::{IntoDiagnostic, WrapErr}; use rattler_conda_types::{ version_spec::VersionOperator, MatchSpec, NamelessMatchSpec, Platform, Version, VersionSpec, }; @@ -69,7 +69,7 @@ impl SpecType { } } -pub async fn execute(args: Args) -> anyhow::Result<()> { +pub async fn execute(args: Args) -> miette::Result<()> { let mut project = Project::load_or_else_discover(args.manifest_path.as_deref())?; let spec_type = SpecType::from_args(&args); add_specs_to_project(&mut project, args.specs, spec_type).await @@ -79,15 +79,15 @@ pub async fn add_specs_to_project( project: &mut Project, specs: Vec, spec_type: SpecType, -) -> anyhow::Result<()> { +) -> miette::Result<()> { // Split the specs into package name and version specifier let new_specs = specs .into_iter() .map(|spec| match &spec.name { Some(name) => Ok((name.clone(), spec.into())), - None => Err(anyhow::anyhow!("missing package name for spec '{spec}'")), + None => Err(miette::miette!("missing package name for spec '{spec}'")), }) - .collect::>>()?; + .collect::>>()?; // Get the current specs @@ -111,7 +111,7 @@ pub async fn add_specs_to_project( ) { Ok(versions) => versions, Err(err) => { - return Err(err).context(anyhow::anyhow!( + return Err(err).wrap_err_with(||miette::miette!( "could not determine any available versions for {} on {platform}. Either the package could not be found or version constraints on other dependencies result in a conflict.", new_specs.keys().join(", ") )); @@ -184,7 +184,7 @@ pub fn determine_best_version( current_specs: &IndexMap, sparse_repo_data: &[SparseRepoData], platform: Platform, -) -> anyhow::Result> { +) -> miette::Result> { let combined_specs = current_specs .iter() .chain(new_specs.iter()) @@ -204,7 +204,8 @@ pub fn determine_best_version( platform_sparse_repo_data, package_names.iter().cloned(), None, - )?; + ) + .into_diagnostic()?; // Construct a solver task to start solving. let task = rattler_solve::SolverTask { @@ -226,7 +227,7 @@ pub fn determine_best_version( pinned_packages: vec![], }; - let records = libsolv_c::Solver.solve(task)?; + let records = libsolv_c::Solver.solve(task).into_diagnostic()?; // Determine the versions of the new packages Ok(records diff --git a/src/cli/auth.rs b/src/cli/auth.rs index ead396684..0522f2c5d 100644 --- a/src/cli/auth.rs +++ b/src/cli/auth.rs @@ -1,4 +1,5 @@ use clap::Parser; +use miette::IntoDiagnostic; use rattler_networking::{Authentication, AuthenticationStorage}; fn default_authentication_storage() -> AuthenticationStorage { @@ -48,10 +49,14 @@ pub struct Args { subcommand: Subcommand, } -fn get_url(url: &str) -> anyhow::Result { +fn get_url(url: &str) -> miette::Result { // parse as url and extract host without scheme or port let host = if url.contains("://") { - url::Url::parse(url)?.host_str().unwrap().to_string() + url::Url::parse(url) + .into_diagnostic()? + .host_str() + .unwrap() + .to_string() } else { url.to_string() }; @@ -66,7 +71,7 @@ fn get_url(url: &str) -> anyhow::Result { Ok(host) } -fn login(args: LoginArgs, storage: AuthenticationStorage) -> anyhow::Result<()> { +fn login(args: LoginArgs, storage: AuthenticationStorage) -> miette::Result<()> { let host = get_url(&args.host)?; println!("Authenticating with {}", host); @@ -74,40 +79,40 @@ fn login(args: LoginArgs, storage: AuthenticationStorage) -> anyhow::Result<()> Authentication::CondaToken(conda_token) } else if let Some(username) = args.username { if args.password.is_none() { - anyhow::bail!("Password must be provided when using basic authentication"); + miette::bail!("Password must be provided when using basic authentication"); } let password = args.password.unwrap(); Authentication::BasicHTTP { username, password } } else if let Some(token) = args.token { Authentication::BearerToken(token) } else { - anyhow::bail!("No authentication method provided"); + miette::bail!("No authentication method provided"); }; if host.contains("prefix.dev") && !matches!(auth, Authentication::BearerToken(_)) { - anyhow::bail!( + miette::bail!( "Authentication with prefix.dev requires a token. Use `--token` to provide one." ); } if host.contains("anaconda.org") && !matches!(auth, Authentication::CondaToken(_)) { - anyhow::bail!("Authentication with anaconda.org requires a conda token. Use `--conda-token` to provide one."); + miette::bail!("Authentication with anaconda.org requires a conda token. Use `--conda-token` to provide one."); } - storage.store(&host, &auth)?; + storage.store(&host, &auth).into_diagnostic()?; Ok(()) } -fn logout(args: LogoutArgs, storage: AuthenticationStorage) -> anyhow::Result<()> { +fn logout(args: LogoutArgs, storage: AuthenticationStorage) -> miette::Result<()> { let host = get_url(&args.host)?; println!("Removing authentication for {}", host); - storage.delete(&host)?; + storage.delete(&host).into_diagnostic()?; Ok(()) } -pub async fn execute(args: Args) -> anyhow::Result<()> { +pub async fn execute(args: Args) -> miette::Result<()> { let storage = default_authentication_storage(); match args.subcommand { diff --git a/src/cli/global/install.rs b/src/cli/global/install.rs index 31364a314..6ee9c30da 100644 --- a/src/cli/global/install.rs +++ b/src/cli/global/install.rs @@ -6,6 +6,7 @@ use crate::{ use clap::Parser; use dirs::home_dir; use itertools::Itertools; +use miette::IntoDiagnostic; use rattler::install::Transaction; use rattler_conda_types::{Channel, ChannelConfig, MatchSpec, Platform, PrefixRecord}; use rattler_networking::AuthenticatedClient; @@ -47,17 +48,19 @@ struct BinDir(pub PathBuf); impl BinDir { /// Create the Binary Executable directory - pub async fn create() -> anyhow::Result { + pub async fn create() -> miette::Result { let bin_dir = bin_dir()?; - tokio::fs::create_dir_all(&bin_dir).await?; + tokio::fs::create_dir_all(&bin_dir) + .await + .into_diagnostic()?; Ok(Self(bin_dir)) } } /// Binaries are installed in ~/.pixi/bin -fn bin_dir() -> anyhow::Result { +fn bin_dir() -> miette::Result { Ok(home_dir() - .ok_or_else(|| anyhow::anyhow!("could not find home directory"))? + .ok_or_else(|| miette::miette!("could not find home directory"))? .join(BIN_DIR)) } @@ -65,17 +68,19 @@ struct BinEnvDir(pub PathBuf); impl BinEnvDir { /// Create the Binary Environment directory - pub async fn create(package_name: &str) -> anyhow::Result { + pub async fn create(package_name: &str) -> miette::Result { let bin_env_dir = bin_env_dir()?.join(package_name); - tokio::fs::create_dir_all(&bin_env_dir).await?; + tokio::fs::create_dir_all(&bin_env_dir) + .await + .into_diagnostic()?; Ok(Self(bin_env_dir)) } } /// Binary environments are installed in ~/.pixi/envs -fn bin_env_dir() -> anyhow::Result { +fn bin_env_dir() -> miette::Result { Ok(home_dir() - .ok_or_else(|| anyhow::anyhow!("could not find home directory"))? + .ok_or_else(|| miette::miette!("could not find home directory"))? .join(BIN_ENVS_DIR)) } @@ -83,22 +88,25 @@ fn bin_env_dir() -> anyhow::Result { async fn find_designated_package( prefix: &Prefix, package_name: &str, -) -> anyhow::Result { +) -> miette::Result { let prefix_records = prefix.find_installed_packages(None).await?; prefix_records .into_iter() .find(|r| r.repodata_record.package_record.name == package_name) - .ok_or_else(|| anyhow::anyhow!("could not find {} in prefix", package_name)) + .ok_or_else(|| miette::miette!("could not find {} in prefix", package_name)) } /// Create the environment activation script -fn create_activation_script(prefix: &Prefix, shell: ShellEnum) -> anyhow::Result { - let activator = Activator::from_path(prefix.root(), shell, Platform::Osx64)?; - let result = activator.activation(ActivationVariables { - conda_prefix: None, - path: None, - path_modification_behaviour: PathModificationBehaviour::Prepend, - })?; +fn create_activation_script(prefix: &Prefix, shell: ShellEnum) -> miette::Result { + let activator = + Activator::from_path(prefix.root(), shell, Platform::Osx64).into_diagnostic()?; + let result = activator + .activation(ActivationVariables { + conda_prefix: None, + path: None, + path_modification_behaviour: PathModificationBehaviour::Prepend, + }) + .into_diagnostic()?; let script = format!("#!/bin/sh\n{}", result.script); Ok(script) } @@ -142,7 +150,7 @@ async fn create_executable_scripts( prefix_package: &PrefixRecord, shell: &ShellEnum, activation_script: String, -) -> anyhow::Result> { +) -> miette::Result> { let executables = prefix_package .files .iter() @@ -164,14 +172,16 @@ async fn create_executable_scripts( let file_name = exec .file_stem() - .ok_or_else(|| anyhow::anyhow!("could not get filename from {}", exec.display()))?; + .ok_or_else(|| miette::miette!("could not get filename from {}", exec.display()))?; let mut executable_script_path = bin_dir.0.join(file_name); if cfg!(windows) { executable_script_path.set_extension("bat"); }; - tokio::fs::write(&executable_script_path, script).await?; + tokio::fs::write(&executable_script_path, script) + .await + .into_diagnostic()?; #[cfg(unix)] { @@ -188,19 +198,20 @@ async fn create_executable_scripts( } /// Install a global command -pub async fn execute(args: Args) -> anyhow::Result<()> { +pub async fn execute(args: Args) -> miette::Result<()> { // Figure out what channels we are using let channel_config = ChannelConfig::default(); let channels = args .channel .iter() .map(|c| Channel::from_str(c, &channel_config)) - .collect::, _>>()?; + .collect::, _>>() + .into_diagnostic()?; // Find the MatchSpec we want to install - let package_matchspec = MatchSpec::from_str(&args.package)?; + let package_matchspec = MatchSpec::from_str(&args.package).into_diagnostic()?; let package_name = package_matchspec.name.clone().ok_or_else(|| { - anyhow::anyhow!( + miette::miette!( "could not find package name in MatchSpec {}", package_matchspec ) @@ -214,7 +225,8 @@ pub async fn execute(args: Args) -> anyhow::Result<()> { platform_sparse_repodata.iter(), vec![package_name.clone()], None, - )?; + ) + .into_diagnostic()?; // Solve for environment // Construct a solver task that we can start solving. @@ -222,7 +234,8 @@ pub async fn execute(args: Args) -> anyhow::Result<()> { specs: vec![package_matchspec], available_packages: &available_packages, - virtual_packages: rattler_virtual_packages::VirtualPackage::current()? + virtual_packages: rattler_virtual_packages::VirtualPackage::current() + .into_diagnostic()? .iter() .cloned() .map(Into::into) @@ -233,7 +246,7 @@ pub async fn execute(args: Args) -> anyhow::Result<()> { }; // Solve it - let records = libsolv_c::Solver.solve(task)?; + let records = libsolv_c::Solver.solve(task).into_diagnostic()?; // Create the binary environment prefix where we install or update the package let bin_prefix = BinEnvDir::create(&package_name).await?; @@ -242,7 +255,8 @@ pub async fn execute(args: Args) -> anyhow::Result<()> { // Create the transaction that we need let transaction = - Transaction::from_current_and_desired(prefix_records, records.iter().cloned(), platform)?; + Transaction::from_current_and_desired(prefix_records, records.iter().cloned(), platform) + .into_diagnostic()?; // Execute the transaction if there is work to do if !transaction.operations.is_empty() { @@ -252,7 +266,8 @@ pub async fn execute(args: Args) -> anyhow::Result<()> { execute_transaction( transaction, prefix.root().to_path_buf(), - rattler::default_cache_dir()?, + rattler::default_cache_dir() + .map_err(|_| miette::miette!("could not determine default cache directory"))?, AuthenticatedClient::default(), ), ) @@ -280,7 +295,7 @@ pub async fn execute(args: Args) -> anyhow::Result<()> { // Check if the bin path is on the path if script_names.is_empty() { - anyhow::bail!( + miette::bail!( "could not find an executable entrypoint in package {} {} {} from {}, are you sure it exists?", console::style(prefix_package.repodata_record.package_record.name).bold(), console::style(prefix_package.repodata_record.package_record.version).bold(), diff --git a/src/cli/global/mod.rs b/src/cli/global/mod.rs index 19da38a6d..a7e8b736d 100644 --- a/src/cli/global/mod.rs +++ b/src/cli/global/mod.rs @@ -16,7 +16,7 @@ pub struct Args { command: Command, } -pub async fn execute(cmd: Args) -> anyhow::Result<()> { +pub async fn execute(cmd: Args) -> miette::Result<()> { match cmd.command { Command::Install(args) => install::execute(args).await?, }; diff --git a/src/cli/info.rs b/src/cli/info.rs index 0adec5d4e..91917c738 100644 --- a/src/cli/info.rs +++ b/src/cli/info.rs @@ -1,6 +1,7 @@ use std::{fmt::Display, path::PathBuf}; use clap::Parser; +use miette::IntoDiagnostic; use rattler_conda_types::{GenericVirtualPackage, Platform}; use rattler_virtual_packages::VirtualPackage; use serde::Serialize; @@ -77,7 +78,7 @@ impl Display for Info { } } -pub async fn execute(args: Args) -> anyhow::Result<()> { +pub async fn execute(args: Args) -> miette::Result<()> { let project = Project::load_or_else_discover(args.manifest_path.as_deref()).ok(); let project_info = project.map(|p| ProjectInfo { @@ -85,7 +86,8 @@ pub async fn execute(args: Args) -> anyhow::Result<()> { tasks: p.manifest.tasks.keys().cloned().collect(), }); - let virtual_packages = VirtualPackage::current()? + let virtual_packages = VirtualPackage::current() + .into_diagnostic()? .iter() .cloned() .map(GenericVirtualPackage::from) @@ -100,7 +102,7 @@ pub async fn execute(args: Args) -> anyhow::Result<()> { }; if args.json { - println!("{}", serde_json::to_string_pretty(&info)?); + println!("{}", serde_json::to_string_pretty(&info).into_diagnostic()?); Ok(()) } else { println!("{}", info); diff --git a/src/cli/init.rs b/src/cli/init.rs index f8460fad2..62be98161 100644 --- a/src/cli/init.rs +++ b/src/cli/init.rs @@ -1,6 +1,6 @@ use crate::{config::get_default_author, consts}; -use anyhow::anyhow; use clap::Parser; +use miette::IntoDiagnostic; use minijinja::{context, Environment}; use rattler_conda_types::Platform; use std::io::{Error, ErrorKind}; @@ -40,15 +40,15 @@ const GITIGNORE_TEMPLATE: &str = r#"# pixi environments .pixi "#; -pub async fn execute(args: Args) -> anyhow::Result<()> { +pub async fn execute(args: Args) -> miette::Result<()> { let env = Environment::new(); - let dir = get_dir(args.path)?; + let dir = get_dir(args.path).into_diagnostic()?; let manifest_path = dir.join(consts::PROJECT_MANIFEST); let gitignore_path = dir.join(".gitignore"); // Check if the project file doesnt already exist. We don't want to overwrite it. if fs::metadata(&manifest_path).map_or(false, |x| x.is_file()) { - anyhow::bail!("{} already exists", consts::PROJECT_MANIFEST); + miette::bail!("{} already exists", consts::PROJECT_MANIFEST); } // Fail silently if it already exists or cannot be created. @@ -58,7 +58,7 @@ pub async fn execute(args: Args) -> anyhow::Result<()> { let name = dir .file_name() .ok_or_else(|| { - anyhow!( + miette::miette!( "Cannot get file or directory name from the path: {}", dir.to_string_lossy() ) @@ -86,12 +86,14 @@ pub async fn execute(args: Args) -> anyhow::Result<()> { }, ) .unwrap(); - fs::write(&manifest_path, rv)?; + fs::write(&manifest_path, rv).into_diagnostic()?; // create a .gitignore if one is missing if !gitignore_path.is_file() { - let rv = env.render_named_str("gitignore.txt", GITIGNORE_TEMPLATE, ())?; - fs::write(&gitignore_path, rv)?; + let rv = env + .render_named_str("gitignore.txt", GITIGNORE_TEMPLATE, ()) + .into_diagnostic()?; + fs::write(&gitignore_path, rv).into_diagnostic()?; } // Emit success diff --git a/src/cli/install.rs b/src/cli/install.rs index 565af036a..8f9d4bfca 100644 --- a/src/cli/install.rs +++ b/src/cli/install.rs @@ -11,7 +11,7 @@ pub struct Args { pub manifest_path: Option, } -pub async fn execute(args: Args) -> anyhow::Result<()> { +pub async fn execute(args: Args) -> miette::Result<()> { let project = Project::load_or_else_discover(args.manifest_path.as_deref())?; get_up_to_date_prefix(&project).await?; diff --git a/src/cli/mod.rs b/src/cli/mod.rs index b12f48404..a3a4d1115 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -2,9 +2,9 @@ use super::util::IndicatifWriter; use clap::{CommandFactory, Parser}; use clap_complete::Shell; use clap_verbosity_flag::Verbosity; +use miette::IntoDiagnostic; use crate::progress; -use anyhow::Error; use tracing_subscriber::{filter::LevelFilter, util::SubscriberInitExt, EnvFilter}; pub mod add; @@ -57,7 +57,7 @@ pub enum Command { Info(info::Args), } -fn completion(args: CompletionCommand) -> Result<(), Error> { +fn completion(args: CompletionCommand) -> miette::Result<()> { clap_complete::generate( args.shell.or(Shell::from_env()).unwrap_or(Shell::Bash), &mut Args::command(), @@ -68,7 +68,7 @@ fn completion(args: CompletionCommand) -> Result<(), Error> { Ok(()) } -pub async fn execute() -> anyhow::Result<()> { +pub async fn execute() -> miette::Result<()> { let args = Args::parse(); let level_filter = match args.verbose.log_level_filter() { @@ -82,9 +82,10 @@ pub async fn execute() -> anyhow::Result<()> { let env_filter = EnvFilter::builder() .with_default_directive(level_filter.into()) - .from_env()? + .from_env() + .into_diagnostic()? // filter logs from apple codesign because they are very noisy - .add_directive("apple_codesign=off".parse()?); + .add_directive("apple_codesign=off".parse().into_diagnostic()?); // Setup the tracing subscriber tracing_subscriber::fmt() @@ -92,14 +93,15 @@ pub async fn execute() -> anyhow::Result<()> { .with_writer(IndicatifWriter::new(progress::global_multi_progress())) .without_time() .finish() - .try_init()?; + .try_init() + .into_diagnostic()?; // Execute the command execute_command(args.command).await } /// Execute the actual command -pub async fn execute_command(command: Command) -> Result<(), Error> { +pub async fn execute_command(command: Command) -> miette::Result<()> { match command { Command::Completion(cmd) => completion(cmd), Command::Init(cmd) => init::execute(cmd).await, diff --git a/src/cli/run.rs b/src/cli/run.rs index d495d3e22..ea127520c 100644 --- a/src/cli/run.rs +++ b/src/cli/run.rs @@ -1,4 +1,3 @@ -use anyhow::Context; use std::collections::{HashMap, HashSet, VecDeque}; use std::path::PathBuf; use std::string::String; @@ -7,6 +6,7 @@ use clap::Parser; use deno_task_shell::parser::SequentialList; use deno_task_shell::{execute_with_pipes, get_output_writer_and_handle, pipe, ShellState}; use itertools::Itertools; +use miette::{miette, Context, IntoDiagnostic}; use rattler_conda_types::Platform; use crate::prefix::Prefix; @@ -42,7 +42,7 @@ pub struct Args { pub fn order_tasks( tasks: Vec, project: &Project, -) -> anyhow::Result)>> { +) -> miette::Result)>> { let tasks: Vec<_> = tasks.iter().map(|c| c.to_string()).collect(); // Find the command in the project. @@ -93,7 +93,7 @@ pub fn order_tasks( if !added.contains(dependency) { let cmd = project .task_opt(dependency) - .ok_or_else(|| anyhow::anyhow!("failed to find dependency {}", dependency))? + .ok_or_else(|| miette::miette!("failed to find dependency {}", dependency))? .clone(); s1.push_back((cmd, Vec::new())); @@ -107,7 +107,7 @@ pub fn order_tasks( Ok(s2) } -pub async fn create_script(task: Task, args: Vec) -> anyhow::Result { +pub async fn create_script(task: Task, args: Vec) -> miette::Result { // Construct the script from the task let task = match task { Task::Execute(Execute { @@ -120,7 +120,7 @@ pub async fn create_script(task: Task, args: Vec) -> anyhow::Result quote_arguments(args), _ => { - return Err(anyhow::anyhow!("No command given")); + return Err(miette::miette!("No command given")); } }; @@ -129,7 +129,7 @@ pub async fn create_script(task: Task, args: Vec) -> anyhow::Result, -) -> anyhow::Result { +) -> miette::Result { // Execute the shell command Ok(deno_task_shell::execute( script, @@ -180,7 +180,7 @@ fn quote_arguments(args: impl IntoIterator>) -> String { /// CLI entry point for `pixi run` /// When running the sigints are ignored and child can react to them. As it pleases. -pub async fn execute(args: Args) -> anyhow::Result<()> { +pub async fn execute(args: Args) -> miette::Result<()> { let project = Project::load_or_else_discover(args.manifest_path.as_deref())?; // Get the correctly ordered commands @@ -216,14 +216,14 @@ pub async fn execute(args: Args) -> anyhow::Result<()> { /// activation scripts from the environment and stores the environment variables it added, it adds /// environment variables set by the project and merges all of that with the system environment /// variables. -pub async fn get_task_env(project: &Project) -> anyhow::Result> { +pub async fn get_task_env(project: &Project) -> miette::Result> { // Get the prefix which we can then activate. let prefix = get_up_to_date_prefix(project).await?; // Get environment variables from the activation let activation_env = await_in_progress("activating environment", run_activation(prefix)) .await - .context("failed to activate environment")?; + .wrap_err("failed to activate environment")?; // Get environment variables from the manifest let manifest_env = get_metadata_env(project); @@ -236,7 +236,7 @@ pub async fn get_task_env(project: &Project) -> anyhow::Result anyhow::Result> { +async fn run_activation(prefix: Prefix) -> miette::Result> { let activator_result = tokio::task::spawn_blocking(move || { // Run and cache the activation script let shell: ShellEnum = ShellEnum::default(); @@ -256,7 +256,9 @@ async fn run_activation(prefix: Prefix) -> anyhow::Result, } -pub async fn execute(args: Args) -> anyhow::Result<()> { +pub async fn execute(args: Args) -> miette::Result<()> { let project = Project::load_or_else_discover(args.manifest_path.as_deref())?; // Determine the current shell @@ -23,18 +24,21 @@ pub async fn execute(args: Args) -> anyhow::Result<()> { // Construct an activator so we can run commands from the environment let prefix = get_up_to_date_prefix(&project).await?; - let activator = Activator::from_path(prefix.root(), shell.clone(), Platform::current())?; + let activator = Activator::from_path(prefix.root(), shell.clone(), Platform::current()) + .into_diagnostic()?; - let activator_result = activator.activation(ActivationVariables { - // Get the current PATH variable - path: Default::default(), + let activator_result = activator + .activation(ActivationVariables { + // Get the current PATH variable + path: Default::default(), - // Start from an empty prefix - conda_prefix: None, + // Start from an empty prefix + conda_prefix: None, - // Prepending environment paths so they get found first. - path_modification_behaviour: PathModificationBehaviour::Prepend, - })?; + // Prepending environment paths so they get found first. + path_modification_behaviour: PathModificationBehaviour::Prepend, + }) + .into_diagnostic()?; // Generate a temporary file with the script to execute. This includes the activation of the // environment. @@ -44,7 +48,9 @@ pub async fn execute(args: Args) -> anyhow::Result<()> { add_metadata_as_env_vars(&mut script, &shell, &project)?; // Add the conda default env variable so that the tools that use this know it exists. - shell.set_env_var(&mut script, "CONDA_DEFAULT_ENV", project.name())?; + shell + .set_env_var(&mut script, "CONDA_DEFAULT_ENV", project.name()) + .into_diagnostic()?; // Start the shell as the last part of the activation script based on the default shell. let interactive_shell: ShellEnum = ShellEnum::from_parent_process() @@ -55,8 +61,9 @@ pub async fn execute(args: Args) -> anyhow::Result<()> { // Write the contents of the script to a temporary file that we can execute with the shell. let mut temp_file = tempfile::Builder::new() .suffix(&format!(".{}", shell.extension())) - .tempfile()?; - std::io::Write::write_all(&mut temp_file, script.as_bytes())?; + .tempfile() + .into_diagnostic()?; + std::io::Write::write_all(&mut temp_file, script.as_bytes()).into_diagnostic()?; // Execute the script with the shell let mut command = shell @@ -64,5 +71,5 @@ pub async fn execute(args: Args) -> anyhow::Result<()> { .spawn() .expect("failed to execute process"); - std::process::exit(command.wait()?.code().unwrap_or(1)); + std::process::exit(command.wait().into_diagnostic()?.code().unwrap_or(1)); } diff --git a/src/cli/task.rs b/src/cli/task.rs index 352c7b583..1e5c71da5 100644 --- a/src/cli/task.rs +++ b/src/cli/task.rs @@ -97,7 +97,7 @@ pub struct Args { pub manifest_path: Option, } -pub fn execute(args: Args) -> anyhow::Result<()> { +pub fn execute(args: Args) -> miette::Result<()> { let mut project = Project::load_or_else_discover(args.manifest_path.as_deref())?; match args.operation { Operation::Add(args) => { diff --git a/src/environment.rs b/src/environment.rs index 3c6d1ae45..f1d482ae8 100644 --- a/src/environment.rs +++ b/src/environment.rs @@ -1,6 +1,4 @@ -use crate::report_error::ReportError; use crate::{ - consts, prefix::Prefix, progress::{ await_in_progress, default_progress_style, finished_progress_style, global_multi_progress, @@ -8,12 +6,11 @@ use crate::{ virtual_packages::verify_current_platform_has_required_virtual_packages, Project, }; -use anyhow::Context; -use ariadne::{Label, Report, ReportKind, Source}; use futures::future::ready; use futures::{stream, FutureExt, StreamExt, TryFutureExt, TryStreamExt}; use indicatif::ProgressBar; use itertools::Itertools; +use miette::{Context, IntoDiagnostic, LabeledSpan}; use rattler::{ install::{link_package, InstallDriver, InstallOptions, Transaction, TransactionOperation}, package_cache::PackageCache, @@ -38,27 +35,19 @@ use std::{ /// Returns the prefix associated with the given environment. If the prefix doesnt exist or is not /// up to date it is updated. -pub async fn get_up_to_date_prefix(project: &Project) -> anyhow::Result { +pub async fn get_up_to_date_prefix(project: &Project) -> miette::Result { // Make sure the project supports the current platform let platform = Platform::current(); if !project.platforms().contains(&platform) { let span = project.manifest.project.platforms.span(); - let report = Report::build(ReportKind::Error, consts::PROJECT_MANIFEST, span.start) - .with_message("the project is not configured for your current platform") - .with_label( - Label::new((consts::PROJECT_MANIFEST, span)) - .with_message(format!("add '{platform}' here")), - ) - .with_help(format!( + return Err(miette::miette!( + help = format!( "The project needs to be configured to support your platform ({platform})." - )) - .finish(); - - return Err(ReportError { - source: (consts::PROJECT_MANIFEST, Source::from(&project.source)), - report, - } - .into()); + ), + labels = vec![LabeledSpan::at(span, format!("add '{platform}' here"),)], + "the project is not configured for your current platform" + ) + .with_source_code(project.source())); } // Make sure the system requirements are met @@ -81,10 +70,11 @@ pub async fn get_up_to_date_prefix(project: &Project) -> anyhow::Result // Construct a transaction to bring the environment up to date with the lock-file content let transaction = Transaction::from_current_and_desired( - installed_packages_future.await??, + installed_packages_future.await.into_diagnostic()??, get_required_packages(&lock_file, platform)?, platform, - )?; + ) + .into_diagnostic()?; // Execute the transaction if there is work to do if !transaction.operations.is_empty() { @@ -94,7 +84,8 @@ pub async fn get_up_to_date_prefix(project: &Project) -> anyhow::Result execute_transaction( transaction, prefix.root().to_path_buf(), - rattler::default_cache_dir()?, + rattler::default_cache_dir() + .map_err(|_| miette::miette!("could not determine default cache directory"))?, AuthenticatedClient::default(), ), ) @@ -105,28 +96,26 @@ pub async fn get_up_to_date_prefix(project: &Project) -> anyhow::Result } /// Loads the lockfile for the specified project or returns a dummy one if none could be found. -pub async fn load_lock_for_manifest_path(path: &Path) -> anyhow::Result { +pub async fn load_lock_for_manifest_path(path: &Path) -> miette::Result { load_lock_file(&Project::load(path)?).await } /// Loads the lockfile for the specified project or returns a dummy one if none could be found. -pub async fn load_lock_file(project: &Project) -> anyhow::Result { +pub async fn load_lock_file(project: &Project) -> miette::Result { let lock_file_path = project.lock_file_path(); tokio::task::spawn_blocking(move || { if lock_file_path.is_file() { - CondaLock::from_path(&lock_file_path).map_err(anyhow::Error::from) + CondaLock::from_path(&lock_file_path).into_diagnostic() } else { - LockFileBuilder::default() - .build() - .map_err(anyhow::Error::from) + LockFileBuilder::default().build().into_diagnostic() } }) .await - .unwrap_or_else(|e| Err(e.into())) + .unwrap_or_else(|e| Err(e).into_diagnostic()) } /// Returns true if the locked packages match the dependencies in the project. -pub fn lock_file_up_to_date(project: &Project, lock_file: &CondaLock) -> anyhow::Result { +pub fn lock_file_up_to_date(project: &Project, lock_file: &CondaLock) -> miette::Result { let platforms = project.platforms(); // If a platform is missing from the lock file the lock file is completely out-of-date. @@ -281,7 +270,7 @@ pub async fn update_lock_file( project: &Project, existing_lock_file: CondaLock, repodata: Option>, -) -> anyhow::Result { +) -> miette::Result { let platforms = project.platforms(); // Get the repodata for the project @@ -321,7 +310,8 @@ pub async fn update_lock_file( platform_sparse_repo_data, package_names.iter().copied(), None, - )?; + ) + .into_diagnostic()?; // Get the virtual packages for this platform let virtual_packages = project.virtual_packages(platform)?; @@ -333,28 +323,31 @@ pub async fn update_lock_file( locked_packages: existing_lock_file .packages_for_platform(platform) .map(RepoDataRecord::try_from) - .collect::, _>>()?, + .collect::, _>>() + .into_diagnostic()?, pinned_packages: vec![], virtual_packages, }; // Solve the task - let records = libsolv_c::Solver.solve(task)?; + let records = libsolv_c::Solver.solve(task).into_diagnostic()?; // Update lock file let mut locked_packages = LockedPackages::new(platform); for record in records { - let locked_package = LockedPackage::try_from(record)?; + let locked_package = LockedPackage::try_from(record).into_diagnostic()?; locked_packages = locked_packages.add_locked_package(locked_package); } builder = builder.add_locked_packages(locked_packages); } - let conda_lock = builder.build()?; + let conda_lock = builder.build().into_diagnostic()?; // Write the conda lock to disk - conda_lock.to_path(&project.lock_file_path())?; + conda_lock + .to_path(&project.lock_file_path()) + .into_diagnostic()?; Ok(conda_lock) } @@ -363,12 +356,12 @@ pub async fn update_lock_file( pub fn get_required_packages( lock_file: &CondaLock, platform: Platform, -) -> anyhow::Result> { +) -> miette::Result> { lock_file .package .iter() .filter(|pkg| pkg.platform == platform) - .map(|pkg| pkg.clone().try_into().map_err(anyhow::Error::from)) + .map(|pkg| pkg.clone().try_into().into_diagnostic()) .collect() } @@ -378,7 +371,7 @@ pub async fn execute_transaction( target_prefix: PathBuf, cache_dir: PathBuf, download_client: AuthenticatedClient, -) -> anyhow::Result<()> { +) -> miette::Result<()> { // Open the package cache let package_cache = PackageCache::new(cache_dir.join("pkgs")); @@ -470,7 +463,7 @@ async fn execute_operation( link_pb: &ProgressBar, op: TransactionOperation, install_options: &InstallOptions, -) -> anyhow::Result<()> { +) -> miette::Result<()> { // Determine the package to install let install_record = op.record_to_install(); let remove_record = op.record_to_remove(); @@ -493,8 +486,8 @@ async fn execute_operation( download_client.clone(), ) .map_ok(|cache_dir| Some((install_record.clone(), cache_dir))) - .map_err(anyhow::Error::from) - .await; + .await + .into_diagnostic(); // Increment the download progress bar. if let Some(pb) = download_pb { @@ -543,7 +536,7 @@ async fn install_package_to_environment( repodata_record: RepoDataRecord, install_driver: &InstallDriver, install_options: &InstallOptions, -) -> anyhow::Result<()> { +) -> miette::Result<()> { // Link the contents of the package into our environment. This returns all the paths that were // linked. let paths = link_package( @@ -552,7 +545,8 @@ async fn install_package_to_environment( install_driver, install_options.clone(), ) - .await?; + .await + .into_diagnostic()?; // Construct a PrefixRecord for the package let prefix_record = PrefixRecord { @@ -587,7 +581,7 @@ async fn install_package_to_environment( }) .await { - Ok(result) => Ok(result?), + Ok(result) => Ok(result.into_diagnostic()?), Err(err) => { if let Ok(panic) = err.try_into_panic() { std::panic::resume_unwind(panic); @@ -602,7 +596,7 @@ async fn install_package_to_environment( async fn remove_package_from_environment( target_prefix: &Path, package: &PrefixRecord, -) -> anyhow::Result<()> { +) -> miette::Result<()> { // TODO: Take into account any clobbered files, they need to be restored. // TODO: Can we also delete empty directories? @@ -614,8 +608,10 @@ async fn remove_package_from_environment( // Simply ignore if the file is already gone. } Err(e) => { - return Err(e) - .with_context(|| format!("failed to delete {}", paths.relative_path.display())) + return Err(e).into_diagnostic().wrap_err(format!( + "failed to delete {}", + paths.relative_path.display() + )) } } } @@ -627,7 +623,9 @@ async fn remove_package_from_environment( package.repodata_record.package_record.version, package.repodata_record.package_record.build )); - tokio::fs::remove_file(conda_meta_path).await?; + tokio::fs::remove_file(conda_meta_path) + .await + .into_diagnostic()?; Ok(()) } diff --git a/src/lib.rs b/src/lib.rs index acceaf6da..bd5d35e7b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,7 +6,6 @@ pub mod prefix; pub mod progress; pub mod project; pub mod repodata; -pub mod report_error; pub mod task; pub mod util; pub mod virtual_packages; diff --git a/src/main.rs b/src/main.rs index 7dda09902..3cbfb84ff 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,16 +1,9 @@ -use console::style; use pixi::cli; -use pixi::report_error; #[tokio::main] pub async fn main() { if let Err(err) = cli::execute().await { - match err.downcast::() { - Ok(report) => report.eprint(), - Err(err) => { - eprintln!("{}: {:?}", style("error").bold().red(), err); - } - } + eprintln!("{err:?}"); std::process::exit(1); } } diff --git a/src/prefix.rs b/src/prefix.rs index 530d83047..4a7e82683 100644 --- a/src/prefix.rs +++ b/src/prefix.rs @@ -1,5 +1,6 @@ use futures::stream::FuturesUnordered; use futures::StreamExt; +use miette::IntoDiagnostic; use rattler_conda_types::PrefixRecord; use std::path::{Path, PathBuf}; use tokio::task::JoinHandle; @@ -12,7 +13,7 @@ pub struct Prefix { impl Prefix { /// Constructs a new instance. Returns an error if the directory doesnt exist. - pub fn new(path: impl Into) -> anyhow::Result { + pub fn new(path: impl Into) -> miette::Result { let root = path.into(); Ok(Self { root }) } @@ -27,7 +28,7 @@ impl Prefix { pub async fn find_installed_packages( &self, concurrency_limit: Option, - ) -> anyhow::Result> { + ) -> miette::Result> { let concurrency_limit = concurrency_limit.unwrap_or(100); let mut meta_futures = FuturesUnordered::>>::new(); @@ -36,7 +37,7 @@ impl Prefix { .into_iter() .flatten() { - let entry = entry?; + let entry = entry.into_diagnostic()?; let path = entry.path(); if path.ends_with(".json") { continue; @@ -49,7 +50,7 @@ impl Prefix { .await .expect("we know there are pending futures") { - Ok(record) => result.push(record?), + Ok(record) => result.push(record.into_diagnostic()?), Err(e) => { if let Ok(panic) = e.try_into_panic() { std::panic::resume_unwind(panic); @@ -67,7 +68,7 @@ impl Prefix { while let Some(record) = meta_futures.next().await { match record { - Ok(record) => result.push(record?), + Ok(record) => result.push(record.into_diagnostic()?), Err(e) => { if let Ok(panic) = e.try_into_panic() { std::panic::resume_unwind(panic); diff --git a/src/project/environment.rs b/src/project/environment.rs index c96488a7f..37a2262fd 100644 --- a/src/project/environment.rs +++ b/src/project/environment.rs @@ -1,5 +1,6 @@ use crate::Project; use itertools::Itertools; +use miette::IntoDiagnostic; use rattler_shell::shell::{Shell, ShellEnum}; use std::collections::HashMap; use std::fmt::Write; @@ -12,9 +13,9 @@ pub fn add_metadata_as_env_vars( script: &mut impl Write, shell: &ShellEnum, project: &Project, -) -> anyhow::Result<()> { +) -> miette::Result<()> { for (key, value) in get_metadata_env(project) { - shell.set_env_var(script, &key, &value)?; + shell.set_env_var(script, &key, &value).into_diagnostic()?; } Ok(()) diff --git a/src/project/manifest.rs b/src/project/manifest.rs index ddd723cb9..06f28f354 100644 --- a/src/project/manifest.rs +++ b/src/project/manifest.rs @@ -1,9 +1,7 @@ -use crate::consts::PROJECT_MANIFEST; -use crate::report_error::ReportError; -use crate::task::Task; +use crate::{consts, task::Task}; use ::serde::Deserialize; -use ariadne::{ColorGenerator, Fmt, Label, Report, ReportKind, Source}; use indexmap::IndexMap; +use miette::{LabeledSpan, NamedSource, Report}; use rattler_conda_types::{Channel, NamelessMatchSpec, Platform, Version}; use rattler_virtual_packages::{Archspec, Cuda, LibC, Linux, Osx, VirtualPackage}; use serde::Deserializer; @@ -62,18 +60,17 @@ pub struct ProjectManifest { impl ProjectManifest { /// Validate the - pub fn validate(&self, contents: &str) -> anyhow::Result<()> { + pub fn validate(&self, source: NamedSource) -> miette::Result<()> { // Check if the targets are defined for existing platforms for target_sel in self.target.keys() { match target_sel.as_ref() { TargetSelector::Platform(p) => { if !self.project.platforms.as_ref().contains(p) { return Err(create_unsupported_platform_report( - contents, + source, target_sel.span(), p, - ) - .into()); + )); } } } @@ -261,30 +258,22 @@ impl From for LibC { // Create an error report for usign a platform that is not supported by the project. fn create_unsupported_platform_report( - source: &str, + source: NamedSource, span: Range, - p: &Platform, -) -> ReportError { - let mut color_generator = ColorGenerator::new(); - let platform = color_generator.next(); - - let report = Report::build(ReportKind::Error, PROJECT_MANIFEST, span.start) - .with_message("Targeting a platform that this project does not support") - .with_label( - Label::new((PROJECT_MANIFEST, span)) - .with_message(format!("'{}' is not a supported platform", p.fg(platform))) - .with_color(platform), - ) - .with_help(format!( - "Add '{}' to the `project.platforms` array of the {PROJECT_MANIFEST} manifest.", - p.fg(platform) - )) - .finish(); - - ReportError { - report, - source: (PROJECT_MANIFEST, Source::from(source)), - } + platform: &Platform, +) -> Report { + miette::miette!( + labels = vec![LabeledSpan::at( + span, + format!("'{}' is not a supported platform", platform) + )], + help = format!( + "Add '{platform}' to the `project.platforms` array of the {} manifest.", + consts::PROJECT_MANIFEST + ), + "targeting a platform that this project does not support" + ) + .with_source_code(source) } #[cfg(test)] diff --git a/src/project/mod.rs b/src/project/mod.rs index f0b965942..ff04daef3 100644 --- a/src/project/mod.rs +++ b/src/project/mod.rs @@ -3,13 +3,10 @@ pub mod manifest; mod serde; use crate::consts; -use crate::consts::PROJECT_MANIFEST; use crate::project::manifest::{ProjectManifest, TargetMetadata, TargetSelector}; -use crate::report_error::ReportError; use crate::task::{CmdArgs, Task}; -use anyhow::Context; -use ariadne::{Label, Report, ReportKind, Source}; use indexmap::IndexMap; +use miette::{IntoDiagnostic, LabeledSpan, NamedSource, WrapErr}; use rattler_conda_types::{ Channel, ChannelConfig, MatchSpec, NamelessMatchSpec, Platform, Version, }; @@ -25,7 +22,7 @@ use toml_edit::{Array, Document, Item, Table, TomlError, Value}; #[derive(Debug)] pub struct Project { root: PathBuf, - pub source: String, + source: String, doc: Document, pub manifest: ProjectManifest, } @@ -66,30 +63,38 @@ fn task_as_toml(task: Task) -> Item { impl Project { /// Discovers the project manifest file in the current directory or any of the parent /// directories. - pub fn discover() -> anyhow::Result { + pub fn discover() -> miette::Result { let project_toml = match find_project_root() { Some(root) => root.join(consts::PROJECT_MANIFEST), - None => anyhow::bail!("could not find {}", consts::PROJECT_MANIFEST), + None => miette::bail!("could not find {}", consts::PROJECT_MANIFEST), }; Self::load(&project_toml) } + /// Returns the source code of the project as [`NamedSource`]. + pub fn source(&self) -> NamedSource { + NamedSource::new(consts::PROJECT_MANIFEST, self.source.clone()) + } + /// Loads a project manifest file. - pub fn load(filename: &Path) -> anyhow::Result { + pub fn load(filename: &Path) -> miette::Result { // Determine the parent directory of the manifest file - let full_path = dunce::canonicalize(filename)?; + let full_path = dunce::canonicalize(filename).into_diagnostic()?; let root = full_path .parent() - .ok_or_else(|| anyhow::anyhow!("can not find parent of {}", filename.display()))?; + .ok_or_else(|| miette::miette!("can not find parent of {}", filename.display()))?; // Load the TOML document - Self::from_manifest_str(root, fs::read_to_string(filename)?).with_context(|| { - format!( - "failed to parse {} from {}", - consts::PROJECT_MANIFEST, - root.display() - ) - }) + fs::read_to_string(filename) + .into_diagnostic() + .and_then(|content| Self::from_manifest_str(root, content)) + .wrap_err_with(|| { + format!( + "failed to parse {} from {}", + consts::PROJECT_MANIFEST, + root.display() + ) + }) } /// Find task dependencies @@ -108,9 +113,9 @@ impl Project { } /// Add a task to the project - pub fn add_task(&mut self, name: impl AsRef, task: Task) -> anyhow::Result<()> { + pub fn add_task(&mut self, name: impl AsRef, task: Task) -> miette::Result<()> { if self.manifest.tasks.contains_key(name.as_ref()) { - anyhow::bail!("task {} already exists", name.as_ref()); + miette::bail!("task {} already exists", name.as_ref()); }; let tasks_table = &mut self.doc["tasks"]; @@ -121,14 +126,14 @@ impl Project { // Cast the item into a table let tasks_table = tasks_table.as_table_like_mut().ok_or_else(|| { - anyhow::anyhow!("tasks in {} are malformed", consts::PROJECT_MANIFEST) + miette::miette!("tasks in {} are malformed", consts::PROJECT_MANIFEST) })?; let depends_on = task.depends_on(); for depends in depends_on { if !self.manifest.tasks.contains_key(depends) { - anyhow::bail!( + miette::bail!( "task '{}' for the depends on for '{}' does not exist", depends, name.as_ref(), @@ -147,18 +152,18 @@ impl Project { } /// Remove a task from the project, and the tasks that depend on it - pub fn remove_task(&mut self, name: impl AsRef) -> anyhow::Result<()> { + pub fn remove_task(&mut self, name: impl AsRef) -> miette::Result<()> { self.manifest .tasks .get(name.as_ref()) - .ok_or_else(|| anyhow::anyhow!("task {} does not exist", name.as_ref()))?; + .ok_or_else(|| miette::miette!("task {} does not exist", name.as_ref()))?; let tasks_table = &mut self.doc["tasks"]; if tasks_table.is_none() { - anyhow::bail!("internal data-structure inconsistent with toml"); + miette::bail!("internal data-structure inconsistent with toml"); } let tasks_table = tasks_table.as_table_like_mut().ok_or_else(|| { - anyhow::anyhow!("tasks in {} are malformed", consts::PROJECT_MANIFEST) + miette::miette!("tasks in {} are malformed", consts::PROJECT_MANIFEST) })?; // If it does not exist in toml, consider this ok as we want to remove it anyways @@ -169,7 +174,7 @@ impl Project { Ok(()) } - pub fn load_or_else_discover(manifest_path: Option<&Path>) -> anyhow::Result { + pub fn load_or_else_discover(manifest_path: Option<&Path>) -> miette::Result { let project = match manifest_path { Some(path) => Project::load(path)?, None => Project::discover()?, @@ -177,7 +182,7 @@ impl Project { Ok(project) } - pub fn reload(&mut self) -> anyhow::Result<()> { + pub fn reload(&mut self) -> miette::Result<()> { let project = Self::load(self.root().join(consts::PROJECT_MANIFEST).as_path())?; self.root = project.root; self.doc = project.doc; @@ -186,7 +191,7 @@ impl Project { } /// Loads a project manifest. - pub fn from_manifest_str(root: &Path, contents: impl Into) -> anyhow::Result { + pub fn from_manifest_str(root: &Path, contents: impl Into) -> miette::Result { let contents = contents.into(); let (manifest, doc) = match toml_edit::de::from_str::(&contents) .map_err(TomlError::from) @@ -195,24 +200,32 @@ impl Project { Ok(result) => result, Err(e) => { if let Some(span) = e.span() { - return Err(ReportError { - source: (PROJECT_MANIFEST, Source::from(&contents)), - report: Report::build(ReportKind::Error, PROJECT_MANIFEST, span.start) - .with_message("failed to parse project manifest") - .with_label( - Label::new((PROJECT_MANIFEST, span)).with_message(e.message()), - ) - .finish(), - } - .into()); + return Err(miette::miette!( + labels = vec![LabeledSpan::at(span, e.message())], + "failed to parse project manifest" + ) + .with_source_code(contents)); + // ReportError { + // source: (PROJECT_MANIFEST, Source::from(&contents)), + // report: Report::build(ReportKind::Error, PROJECT_MANIFEST, span.start) + // .with_message("failed to parse project manifest") + // .with_label( + // Label::new((PROJECT_MANIFEST, span)).with_message(e.message()), + // ) + // .finish(), + // }) + // .into_diagnostic(); } else { - return Err(e.into()); + return Err(e).into_diagnostic(); } } }; // Validate the contents of the manifest - manifest.validate(&contents)?; + manifest.validate(NamedSource::new( + consts::PROJECT_MANIFEST, + contents.to_owned(), + ))?; Ok(Self { root: root.to_path_buf(), @@ -226,7 +239,7 @@ impl Project { pub fn dependencies( &self, platform: Platform, - ) -> anyhow::Result> { + ) -> miette::Result> { // Get the base dependencies (defined in the `[dependencies]` section) let base_dependencies = self.manifest.dependencies.iter(); @@ -248,7 +261,7 @@ impl Project { pub fn build_dependencies( &self, platform: Platform, - ) -> anyhow::Result> { + ) -> miette::Result> { // Get the base dependencies (defined in the `[build-dependencies]` section) let base_dependencies = self.manifest.build_dependencies.iter(); @@ -271,7 +284,7 @@ impl Project { pub fn host_dependencies( &self, platform: Platform, - ) -> anyhow::Result> { + ) -> miette::Result> { // Get the base dependencies (defined in the `[host-dependencies]` section) let base_dependencies = self.manifest.host_dependencies.iter(); @@ -294,7 +307,7 @@ impl Project { pub fn all_dependencies( &self, platform: Platform, - ) -> anyhow::Result> { + ) -> miette::Result> { let mut dependencies = self.dependencies(platform)?; dependencies.extend(self.host_dependencies(platform)?); dependencies.extend(self.build_dependencies(platform)?); @@ -330,7 +343,7 @@ impl Project { fn add_to_deps_table( deps_table: &mut Item, spec: &MatchSpec, - ) -> anyhow::Result<(String, NamelessMatchSpec)> { + ) -> miette::Result<(String, NamelessMatchSpec)> { // If it doesnt exist create a proper table if deps_table.is_none() { *deps_table = Item::Table(Table::new()); @@ -338,14 +351,14 @@ impl Project { // Cast the item into a table let deps_table = deps_table.as_table_like_mut().ok_or_else(|| { - anyhow::anyhow!("dependencies in {} are malformed", consts::PROJECT_MANIFEST) + miette::miette!("dependencies in {} are malformed", consts::PROJECT_MANIFEST) })?; // Determine the name of the package to add let name = spec .name .as_deref() - .ok_or_else(|| anyhow::anyhow!("* package specifier is not supported"))?; + .ok_or_else(|| miette::miette!("* package specifier is not supported"))?; // Format the requirement // TODO: Do this smarter. E.g.: @@ -359,7 +372,7 @@ impl Project { Ok((name.to_string(), nameless)) } - pub fn add_dependency(&mut self, spec: &MatchSpec) -> anyhow::Result<()> { + pub fn add_dependency(&mut self, spec: &MatchSpec) -> miette::Result<()> { // Find the dependencies table let deps = &mut self.doc["dependencies"]; let (name, nameless) = Project::add_to_deps_table(deps, spec)?; @@ -369,7 +382,7 @@ impl Project { Ok(()) } - pub fn add_host_dependency(&mut self, spec: &MatchSpec) -> anyhow::Result<()> { + pub fn add_host_dependency(&mut self, spec: &MatchSpec) -> miette::Result<()> { // Find the dependencies table let deps = &mut self.doc["host-dependencies"]; let (name, nameless) = Project::add_to_deps_table(deps, spec)?; @@ -385,7 +398,7 @@ impl Project { Ok(()) } - pub fn add_build_dependency(&mut self, spec: &MatchSpec) -> anyhow::Result<()> { + pub fn add_build_dependency(&mut self, spec: &MatchSpec) -> miette::Result<()> { // Find the dependencies table let deps = &mut self.doc["build-dependencies"]; let (name, nameless) = Project::add_to_deps_table(deps, spec)?; @@ -418,13 +431,15 @@ impl Project { } /// Save back changes - pub fn save(&self) -> anyhow::Result<()> { - fs::write(self.manifest_path(), self.doc.to_string()).with_context(|| { - format!( - "unable to write changes to {}", - self.manifest_path().display() - ) - })?; + pub fn save(&self) -> miette::Result<()> { + fs::write(self.manifest_path(), self.doc.to_string()) + .into_diagnostic() + .wrap_err_with(|| { + format!( + "unable to write changes to {}", + self.manifest_path().display() + ) + })?; Ok(()) } @@ -437,13 +452,12 @@ impl Project { pub fn add_channels( &mut self, channels: impl IntoIterator>, - ) -> anyhow::Result<()> { + ) -> miette::Result<()> { let mut stored_channels = Vec::new(); for channel in channels { - self.manifest.project.channels.push(Channel::from_str( - channel.as_ref(), - &ChannelConfig::default(), - )?); + self.manifest.project.channels.push( + Channel::from_str(channel.as_ref(), &ChannelConfig::default()).into_diagnostic()?, + ); stored_channels.push(channel.as_ref().to_owned()); } @@ -459,14 +473,13 @@ impl Project { pub fn set_channels( &mut self, channels: impl IntoIterator>, - ) -> anyhow::Result<()> { + ) -> miette::Result<()> { self.manifest.project.channels.clear(); let mut stored_channels = Vec::new(); for channel in channels { - self.manifest.project.channels.push(Channel::from_str( - channel.as_ref(), - &ChannelConfig::default(), - )?); + self.manifest.project.channels.push( + Channel::from_str(channel.as_ref(), &ChannelConfig::default()).into_diagnostic()?, + ); stored_channels.push(channel.as_ref().to_owned()); } @@ -479,7 +492,7 @@ impl Project { } /// Returns a mutable reference to the channels array. - fn channels_array_mut(&mut self) -> anyhow::Result<&mut Array> { + fn channels_array_mut(&mut self) -> miette::Result<&mut Array> { let project = &mut self.doc["project"]; if project.is_none() { *project = Item::Table(Table::new()); @@ -492,7 +505,7 @@ impl Project { channels .as_array_mut() - .ok_or_else(|| anyhow::anyhow!("malformed channels array")) + .ok_or_else(|| miette::miette!("malformed channels array")) } /// Returns the platforms this project targets diff --git a/src/repodata.rs b/src/repodata.rs index 09711d31c..82659c7fc 100644 --- a/src/repodata.rs +++ b/src/repodata.rs @@ -1,13 +1,13 @@ use crate::{progress, project::Project}; -use anyhow::Context; use indicatif::ProgressBar; +use miette::{Context, IntoDiagnostic}; use rattler_conda_types::{Channel, Platform}; use rattler_networking::AuthenticatedClient; use rattler_repodata_gateway::{fetch, sparse::SparseRepoData}; use std::{path::Path, time::Duration}; impl Project { - pub async fn fetch_sparse_repodata(&self) -> anyhow::Result> { + pub async fn fetch_sparse_repodata(&self) -> miette::Result> { let channels = self.channels(); let platforms = self.platforms(); fetch_sparse_repodata(channels, platforms).await @@ -17,7 +17,7 @@ impl Project { pub async fn fetch_sparse_repodata( channels: &[Channel], target_platforms: &[Platform], -) -> anyhow::Result> { +) -> miette::Result> { // Determine all the repodata that requires fetching. let mut fetch_targets = Vec::with_capacity(channels.len() * target_platforms.len()); for channel in channels { @@ -41,11 +41,13 @@ pub async fn fetch_sparse_repodata( top_level_progress.set_message("fetching latest repodata"); top_level_progress.enable_steady_tick(Duration::from_millis(50)); - let repodata_cache_path = rattler::default_cache_dir()?.join("repodata"); + let repodata_cache_path = rattler::default_cache_dir() + .map_err(|_| miette::miette!("could not determine default cache directory"))? + .join("repodata"); let repodata_download_client = AuthenticatedClient::default(); let multi_progress = progress::global_multi_progress(); let (tx, mut rx) = - tokio::sync::mpsc::channel::>>(fetch_targets.len()); + tokio::sync::mpsc::channel::>>(fetch_targets.len()); let mut progress_bars = Vec::new(); for (channel, platform) in fetch_targets { // Construct a progress bar for the fetch @@ -102,7 +104,7 @@ pub async fn fetch_sparse_repodata( // If there was an error, report it. if let Some(error) = error { - return Err(error).context("failed to fetch repodata from channels"); + return Err(error).wrap_err("failed to fetch repodata from channels"); } Ok(result) @@ -117,7 +119,7 @@ async fn fetch_repo_data_records_with_progress( client: AuthenticatedClient, progress_bar: indicatif::ProgressBar, allow_not_found: bool, -) -> anyhow::Result> { +) -> miette::Result> { // Download the repodata.json let download_progress_progress_bar = progress_bar.clone(); let result = fetch::fetch_repo_data( @@ -145,7 +147,7 @@ async fn fetch_repo_data_records_with_progress( progress_bar.set_style(progress::errored_progress_style()); progress_bar.finish_with_message("404 not found"); - return Err(e.into()); + return Err(e).into_diagnostic(); } Ok(result) => result, }; @@ -174,7 +176,7 @@ async fn fetch_repo_data_records_with_progress( Ok(Err(err)) => { progress_bar.set_style(progress::errored_progress_style()); progress_bar.finish_with_message("Error"); - Err(err.into()) + Err(err).into_diagnostic() } Err(err) => match err.try_into_panic() { Ok(panic) => { @@ -184,7 +186,7 @@ async fn fetch_repo_data_records_with_progress( progress_bar.set_style(progress::errored_progress_style()); progress_bar.finish_with_message("Cancelled.."); // Since the task was cancelled most likely the whole async stack is being cancelled. - Err(anyhow::anyhow!("cancelled")) + Err(miette::miette!("cancelled")) } }, } diff --git a/src/report_error.rs b/src/report_error.rs deleted file mode 100644 index 5d2aace2e..000000000 --- a/src/report_error.rs +++ /dev/null @@ -1,27 +0,0 @@ -use ariadne::{Report, Source}; -use std::{ - fmt::{Debug, Display, Formatter}, - ops::Range, -}; - -/// An error that contains a [`ariadne::Report`]. This allows the application to display a very -/// nicely formatted diagnostic. -#[derive(Debug)] -pub struct ReportError { - pub report: Report<'static, (&'static str, Range)>, - pub source: (&'static str, Source), -} - -impl std::error::Error for ReportError {} - -impl Display for ReportError { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - self.report.fmt(f) - } -} - -impl ReportError { - pub fn eprint(self) { - self.report.eprint(self.source).unwrap() - } -} diff --git a/src/virtual_packages.rs b/src/virtual_packages.rs index fd7714c79..cef7dc52c 100644 --- a/src/virtual_packages.rs +++ b/src/virtual_packages.rs @@ -1,5 +1,5 @@ use crate::Project; -use anyhow::bail; +use miette::IntoDiagnostic; use rattler_conda_types::{GenericVirtualPackage, Platform, Version}; use rattler_virtual_packages::{Archspec, Cuda, LibC, Linux, Osx, VirtualPackage}; use serde::Deserialize; @@ -109,7 +109,7 @@ impl Project { pub fn virtual_packages( &self, platform: Platform, - ) -> anyhow::Result> { + ) -> miette::Result> { // Start with the minimal virtual packages let reference_packages = get_minimal_virtual_packages(platform); @@ -132,10 +132,11 @@ impl Project { /// Verifies if the current platform satisfies the minimal virtual package requirements. pub fn verify_current_platform_has_required_virtual_packages( project: &Project, -) -> Result<(), anyhow::Error> { +) -> miette::Result<()> { let current_platform = Platform::current(); - let system_virtual_packages = VirtualPackage::current()? + let system_virtual_packages = VirtualPackage::current() + .into_diagnostic()? .iter() .cloned() .map(GenericVirtualPackage::from) @@ -147,14 +148,14 @@ pub fn verify_current_platform_has_required_virtual_packages( for req_pkg in required_pkgs { if let Some(local_vpkg) = system_virtual_packages.get(&req_pkg.name) { if req_pkg.build_string != local_vpkg.build_string { - bail!("The current system has a mismatching virtual package. The project requires '{}' to be on build '{}' but the system has build '{}'", req_pkg.name, req_pkg.build_string, local_vpkg.build_string); + miette::bail!("The current system has a mismatching virtual package. The project requires '{}' to be on build '{}' but the system has build '{}'", req_pkg.name, req_pkg.build_string, local_vpkg.build_string); } if req_pkg.version > local_vpkg.version { - bail!("The current system has a mismatching virtual package. The project requires '{}' to be at least version '{}' but the system has version '{}'", req_pkg.name, req_pkg.version, local_vpkg.version); + miette::bail!("The current system has a mismatching virtual package. The project requires '{}' to be at least version '{}' but the system has version '{}'", req_pkg.name, req_pkg.version, local_vpkg.version); } } else { - bail!("The platform you are running on should at least have the virtual package {} on version {}, build_string: {}", req_pkg.name, req_pkg.version, req_pkg.build_string) + miette::bail!("The platform you are running on should at least have the virtual package {} on version {}, build_string: {}", req_pkg.name, req_pkg.version, req_pkg.build_string) } } diff --git a/tests/common/builders.rs b/tests/common/builders.rs index ac03ad642..8ee47f003 100644 --- a/tests/common/builders.rs +++ b/tests/common/builders.rs @@ -13,7 +13,7 @@ //! //! ```rust //! impl IntoFuture for InitBuilder { -//! type Output = anyhow::Result<()>; +//! type Output = miette::Result<()>; //! type IntoFuture = Pin + Send + 'static>>; //! //! fn into_future(self) -> Self::IntoFuture { @@ -54,7 +54,7 @@ impl InitBuilder { } impl IntoFuture for InitBuilder { - type Output = anyhow::Result<()>; + type Output = miette::Result<()>; type IntoFuture = Pin + Send + 'static>>; fn into_future(self) -> Self::IntoFuture { @@ -95,7 +95,7 @@ impl AddBuilder { } impl IntoFuture for AddBuilder { - type Output = anyhow::Result<()>; + type Output = miette::Result<()>; type IntoFuture = Pin + Send + 'static>>; fn into_future(self) -> Self::IntoFuture { @@ -122,7 +122,7 @@ impl TaskAddBuilder { } /// Execute the CLI command - pub fn execute(self) -> anyhow::Result<()> { + pub fn execute(self) -> miette::Result<()> { task::execute(task::Args { operation: task::Operation::Add(self.args), manifest_path: self.manifest_path, @@ -143,7 +143,7 @@ impl TaskAliasBuilder { } /// Execute the CLI command - pub fn execute(self) -> anyhow::Result<()> { + pub fn execute(self) -> miette::Result<()> { task::execute(task::Args { operation: task::Operation::Alias(self.args), manifest_path: self.manifest_path, diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 197421143..597181ca4 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -14,6 +14,7 @@ use pixi::{consts, Project}; use rattler_conda_types::conda_lock::CondaLock; use rattler_conda_types::{MatchSpec, Version}; +use miette::IntoDiagnostic; use std::path::{Path, PathBuf}; use std::process::Output; use std::str::FromStr; @@ -79,13 +80,13 @@ impl LockFileExt for CondaLock { impl PixiControl { /// Create a new PixiControl instance - pub fn new() -> anyhow::Result { - let tempdir = tempfile::tempdir()?; + pub fn new() -> miette::Result { + let tempdir = tempfile::tempdir().into_diagnostic()?; Ok(PixiControl { tmpdir: tempdir }) } /// Loads the project manifest and returns it. - pub fn project(&self) -> anyhow::Result { + pub fn project(&self) -> miette::Result { Project::load(&self.manifest_path()) } @@ -123,7 +124,7 @@ impl PixiControl { } /// Run a command - pub async fn run(&self, mut args: run::Args) -> anyhow::Result { + pub async fn run(&self, mut args: run::Args) -> miette::Result { args.manifest_path = args.manifest_path.or_else(|| Some(self.manifest_path())); let mut tasks = order_tasks(args.task, &self.project().unwrap())?; @@ -143,7 +144,7 @@ impl PixiControl { } /// Create an installed environment. I.e a resolved and installed prefix - pub async fn install(&self) -> anyhow::Result<()> { + pub async fn install(&self) -> miette::Result<()> { pixi::cli::install::execute(Args { manifest_path: Some(self.manifest_path()), }) @@ -151,7 +152,7 @@ impl PixiControl { } /// Get the associated lock file - pub async fn lock_file(&self) -> anyhow::Result { + pub async fn lock_file(&self) -> miette::Result { pixi::environment::load_lock_for_manifest_path(&self.manifest_path()).await } @@ -179,7 +180,7 @@ impl TasksControl<'_> { } /// Remove a task - pub async fn remove(&self, name: impl ToString) -> anyhow::Result<()> { + pub async fn remove(&self, name: impl ToString) -> miette::Result<()> { task::execute(task::Args { manifest_path: Some(self.pixi.manifest_path()), operation: task::Operation::Remove(task::RemoveArgs { diff --git a/tests/common/package_database.rs b/tests/common/package_database.rs index 541f2d7b1..7ff1760a1 100644 --- a/tests/common/package_database.rs +++ b/tests/common/package_database.rs @@ -5,6 +5,7 @@ #![allow(dead_code)] use itertools::Itertools; +use miette::IntoDiagnostic; use rattler_conda_types::{ package::ArchiveType, ChannelInfo, PackageRecord, Platform, RepoData, VersionWithSource, }; @@ -29,7 +30,7 @@ impl PackageDatabase { } /// Writes the repodata of this instance to the specified channel directory - pub async fn write_repodata(&self, channel_path: &Path) -> anyhow::Result<()> { + pub async fn write_repodata(&self, channel_path: &Path) -> miette::Result<()> { let mut platforms = self.platforms(); // Make sure NoArch is always included @@ -65,10 +66,14 @@ impl PackageDatabase { removed: Default::default(), version: Some(1), }; - let repodata_str = serde_json::to_string_pretty(&repodata)?; - - tokio::fs::create_dir_all(&subdir_path).await?; - tokio::fs::write(subdir_path.join("repodata.json"), repodata_str).await?; + let repodata_str = serde_json::to_string_pretty(&repodata).into_diagnostic()?; + + tokio::fs::create_dir_all(&subdir_path) + .await + .into_diagnostic()?; + tokio::fs::write(subdir_path.join("repodata.json"), repodata_str) + .await + .into_diagnostic()?; } Ok(()) From 8867fa1339ad398bbbd13461d92f3b0fb7a02e79 Mon Sep 17 00:00:00 2001 From: Ruben Arts Date: Thu, 13 Jul 2023 15:09:22 +0200 Subject: [PATCH 2/3] fix: into_diagnostic on unix specific code --- src/cli/global/install.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cli/global/install.rs b/src/cli/global/install.rs index 6ee9c30da..9954cde51 100644 --- a/src/cli/global/install.rs +++ b/src/cli/global/install.rs @@ -189,7 +189,8 @@ async fn create_executable_scripts( std::fs::set_permissions( executable_script_path, std::fs::Permissions::from_mode(0o744), - )?; + ) + .into_diagnostic()?; } scripts.push(file_name.to_string_lossy().into_owned()); From 99544637df874ad7d610c9842ab3f8f0d41236c2 Mon Sep 17 00:00:00 2001 From: Bas Zalmstra Date: Thu, 13 Jul 2023 15:21:20 +0200 Subject: [PATCH 3/3] fix: name of the file with toml errors --- src/project/mod.rs | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/src/project/mod.rs b/src/project/mod.rs index ff04daef3..5f4afd937 100644 --- a/src/project/mod.rs +++ b/src/project/mod.rs @@ -204,17 +204,7 @@ impl Project { labels = vec![LabeledSpan::at(span, e.message())], "failed to parse project manifest" ) - .with_source_code(contents)); - // ReportError { - // source: (PROJECT_MANIFEST, Source::from(&contents)), - // report: Report::build(ReportKind::Error, PROJECT_MANIFEST, span.start) - // .with_message("failed to parse project manifest") - // .with_label( - // Label::new((PROJECT_MANIFEST, span)).with_message(e.message()), - // ) - // .finish(), - // }) - // .into_diagnostic(); + .with_source_code(NamedSource::new(consts::PROJECT_MANIFEST, contents))); } else { return Err(e).into_diagnostic(); }