diff --git a/Cargo.toml b/Cargo.toml index 61f05a3..8f33c0f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,14 +1,14 @@ [package] name = "rsvenv" -version = "0.4.0" +version = "0.4.1" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +anyhow = "1.0.82" clap = { version = "4.5.3", features = ["derive"] } config = { version = "0.14.0", features = ["yaml"] } -error-stack = "0.4.1" glob = "0.3.1" itertools = "0.12.1" lazy_static = "1.4.0" diff --git a/src/arguments.rs b/src/arguments.rs index 63a71c3..b47c9b7 100644 --- a/src/arguments.rs +++ b/src/arguments.rs @@ -1,9 +1,7 @@ +use anyhow::Result; use clap::{Parser, Subcommand}; -use error_stack::Result; use simplelog::debug; -use crate::errors::CommandExecutionError; - #[derive(Parser)] #[command(version, about, long_about = None, arg_required_else_help(true))] pub struct Cli { @@ -43,7 +41,7 @@ pub enum Commands { } impl Commands { - pub fn execute(&self) -> Result<(), CommandExecutionError> { + pub fn execute(&self) -> Result<()> { debug!("executing command"); match self { Commands::Init(command) => command.execute(), diff --git a/src/commands/activate.rs b/src/commands/activate.rs index 2d0d765..5f43167 100644 --- a/src/commands/activate.rs +++ b/src/commands/activate.rs @@ -1,11 +1,8 @@ +use anyhow::{bail, Result}; use clap::Parser; -use error_stack::{Report, Result, ResultExt}; -use simplelog::{debug, error}; +use simplelog::debug; -use crate::{ - errors::{CommandExecutionError, VirtualEnvError}, - virtualenv::{pyenv::Pyenv, rsenv::Rsenv, VirtualEnvironment}, -}; +use crate::virtualenv::{pyenv::Pyenv, rsenv::Rsenv, VirtualEnvironment}; #[derive(Debug, Parser)] pub struct Command { @@ -13,51 +10,29 @@ pub struct Command { virtualenv: String, } -fn try_activate(v: VirtualEnvironment, venv: &String) -> Result<(), VirtualEnvError> { +fn try_activate(v: VirtualEnvironment, venv: &String) -> Result<()> { if v.list().contains(venv) { if let Err(e) = v.activate(Some(venv)) { - error!("{e}"); - return Err(e); + debug!("Unable to activate venv: {e:?}"); + bail!(e); }; return Ok(()); } - Err( - Report::new(VirtualEnvError::NotVirtualEnv(venv.to_string())) - .attach_printable("{venv} is not a virtual environment"), - ) + bail!("{venv} is not a virtual environment") } impl Command { - pub fn execute(&self) -> Result<(), CommandExecutionError> { - let context = CommandExecutionError { - command: "activate".into(), - }; - if let Err(e) = VirtualEnvironment::deactivate(true) { - debug!("{e}"); - }; - if try_activate( - VirtualEnvironment::new(&Rsenv).change_context(context.clone())?, - &self.virtualenv, - ) - .is_ok() - { + pub fn execute(&self) -> Result<()> { + VirtualEnvironment::deactivate(true)?; + if try_activate(VirtualEnvironment::new(&Rsenv)?, &self.virtualenv).is_ok() { return Ok(()); } - if try_activate( - VirtualEnvironment::new(&Pyenv).change_context(context)?, - &self.virtualenv, - ) - .is_ok() - { + if try_activate(VirtualEnvironment::new(&Pyenv)?, &self.virtualenv).is_ok() { return Ok(()); } - error!( + bail!( "Failed to find and activate virtual environment {}", self.virtualenv ); - Err(Report::new(CommandExecutionError { - command: "activate".into(), - }) - .attach_printable(format!("Unable to activate virtualenv {}", self.virtualenv))) } } diff --git a/src/commands/chdir_hook.rs b/src/commands/chdir_hook.rs index 807dbaa..823fe04 100644 --- a/src/commands/chdir_hook.rs +++ b/src/commands/chdir_hook.rs @@ -1,6 +1,6 @@ -use crate::{errors::CommandExecutionError, virtualenv::VirtualEnvironment}; +use crate::virtualenv::VirtualEnvironment; +use anyhow::Result; use clap::Parser; -use error_stack::{Result, ResultExt}; use simplelog::{debug, error}; use std::path::Path; @@ -44,7 +44,7 @@ impl Command { } } - pub fn execute(&self) -> Result<(), CommandExecutionError> { + pub fn execute(&self) -> Result<()> { let deactivated = self.should_deactivate(); if deactivated { if let Err(e) = VirtualEnvironment::deactivate(false) { @@ -54,9 +54,7 @@ impl Command { } if self.should_activate(deactivated) { if let Some(venv) = VirtualEnvironment::detect() { - venv.activate(None).change_context(CommandExecutionError { - command: "hook".into(), - })?; + venv.activate(None)?; }; } Ok(()) diff --git a/src/commands/create.rs b/src/commands/create.rs index f22c1a0..7c476e9 100644 --- a/src/commands/create.rs +++ b/src/commands/create.rs @@ -1,7 +1,7 @@ +use anyhow::Result; use clap::Parser; -use error_stack::{Result, ResultExt}; -use crate::{errors::CommandExecutionError, virtualenv::rsenv::Rsenv}; +use crate::virtualenv::rsenv::Rsenv; #[derive(Debug, Parser)] pub struct CreateCommand { @@ -12,12 +12,7 @@ pub struct CreateCommand { } impl CreateCommand { - pub fn execute(&self) -> Result<(), CommandExecutionError> { - Rsenv - .create(&self.name, &self.python) - .change_context(CommandExecutionError { - command: "create".into(), - })?; - Ok(()) + pub fn execute(&self) -> Result<()> { + Rsenv.create(&self.name, &self.python) } } diff --git a/src/commands/deactivate.rs b/src/commands/deactivate.rs index 29c42f4..d56eaae 100644 --- a/src/commands/deactivate.rs +++ b/src/commands/deactivate.rs @@ -1,6 +1,6 @@ -use crate::{errors::CommandExecutionError, virtualenv::VirtualEnvironment}; +use crate::virtualenv::VirtualEnvironment; +use anyhow::Result; use clap::Parser; -use error_stack::{Result, ResultExt}; #[derive(Debug, Parser)] pub struct Command { @@ -9,9 +9,7 @@ pub struct Command { } impl Command { - pub fn execute(&self) -> Result<(), CommandExecutionError> { - VirtualEnvironment::deactivate(true).change_context(CommandExecutionError { - command: "deactivate".into(), - }) + pub fn execute(&self) -> Result<()> { + VirtualEnvironment::deactivate(true) } } diff --git a/src/commands/delete.rs b/src/commands/delete.rs index c5cdf88..7ae71a8 100644 --- a/src/commands/delete.rs +++ b/src/commands/delete.rs @@ -1,6 +1,6 @@ -use crate::{errors::CommandExecutionError, virtualenv::rsenv::Rsenv}; +use crate::virtualenv::rsenv::Rsenv; +use anyhow::Result; use clap::Parser; -use error_stack::{Result, ResultExt}; #[derive(Debug, Parser)] pub struct DeleteCommand { @@ -9,11 +9,7 @@ pub struct DeleteCommand { } impl DeleteCommand { - pub fn execute(&self) -> Result<(), CommandExecutionError> { - Rsenv - .delete(self.venv.clone()) - .change_context(CommandExecutionError { - command: "delete".into(), - }) + pub fn execute(&self) -> Result<()> { + Rsenv.delete(self.venv.clone()) } } diff --git a/src/commands/init.rs b/src/commands/init.rs index 38f12be..69ad58f 100644 --- a/src/commands/init.rs +++ b/src/commands/init.rs @@ -1,7 +1,6 @@ -use crate::errors::CommandExecutionError; use crate::shell::SupportedShell; +use anyhow::{Context, Result}; use clap::Parser; -use error_stack::{Result, ResultExt}; use std::io; use std::io::Write; @@ -9,20 +8,14 @@ use std::io::Write; pub struct Command {} impl Command { - pub fn execute(&self) -> Result<(), CommandExecutionError> { - let error_context = CommandExecutionError { - command: "init".into(), - }; - let shell = SupportedShell::new() - .change_context(error_context.clone()) - .attach("Unable to detect current shell")?; + pub fn execute(&self) -> Result<()> { + let shell = SupportedShell::new().context("Unable to detect current shell")?; let hook = shell.get_hook(); io::stdout() .write_all(hook.as_bytes()) - .change_context(error_context) - .attach("Unable to write hook to STDOUT!")?; + .context("Unable to write hook to STDOUT!")?; Ok(()) } } diff --git a/src/commands/install.rs b/src/commands/install.rs index 48660c3..cbea5a5 100644 --- a/src/commands/install.rs +++ b/src/commands/install.rs @@ -1,21 +1,17 @@ use std::fs::{read_to_string, OpenOptions}; use std::io::Write; -use crate::errors::CommandExecutionError; use crate::shell::SupportedShell; +use anyhow::{bail, Context, Result}; use clap::Parser; -use error_stack::{Report, Result, ResultExt}; -use simplelog::{debug, error, info}; +use simplelog::{debug, info}; #[derive(Debug, Parser)] pub struct Command {} -fn find_occurence(filename: &str, substr: &str) -> Result { +fn find_occurence(filename: &str, substr: &str) -> Result { for line in read_to_string(filename) - .change_context(CommandExecutionError { - command: "install".into(), - }) - .attach_printable("Unable to read config file")? + .context("Unable to read config file")? .lines() { if line.contains(substr) { @@ -26,37 +22,25 @@ fn find_occurence(filename: &str, substr: &str) -> Result Result<(), CommandExecutionError> { - let error_context = CommandExecutionError { - command: "install".into(), - }; - let shell = SupportedShell::new() - .change_context(error_context.clone()) - .attach("Unable to detect current shell")?; + pub fn execute(&self) -> Result<()> { + let shell = SupportedShell::new()?; - let config_path = shell - .get_config_path() - .change_context(error_context.clone())?; + let config_path = shell.get_config_path()?; debug!("Installing to {config_path}"); let init_line = shell.get_init_command(); if find_occurence(&config_path, init_line)? { - error!("RSVENV already installed to config"); - return Err(Report::new(CommandExecutionError { - command: "install".into(), - })); + bail!("RSVENV already installed to config"); }; let mut file = OpenOptions::new() .append(true) .open(config_path) - .change_context(error_context.clone()) - .attach("Unable to open config file for appending")?; + .context("Unable to open config file for appending")?; file.write_all(init_line.as_bytes()) - .change_context(error_context) - .attach_printable("Error when writing to the file")?; + .context("Error when writing to the file")?; info!("Successfully modified shell config. Please restart you session"); Ok(()) diff --git a/src/commands/list.rs b/src/commands/list.rs index b74d034..29dd1a5 100644 --- a/src/commands/list.rs +++ b/src/commands/list.rs @@ -1,7 +1,6 @@ -use crate::errors::CommandExecutionError; use crate::virtualenv::{pyenv::Pyenv, rsenv::Rsenv, traits::VirtualEnvCompatible}; +use anyhow::Result; use clap::Parser; -use error_stack::Result; use std::collections::HashSet; use std::io; use std::io::Write; @@ -18,7 +17,7 @@ fn print_venvs(rsenv_venvs: HashSet) { } impl ListCommand { - pub fn execute(&self) -> Result<(), CommandExecutionError> { + pub fn execute(&self) -> Result<()> { let rsenv_venvs = Rsenv.list(); if !rsenv_venvs.is_empty() { io::stdout() diff --git a/src/commands/use_command.rs b/src/commands/use_command.rs index dd094b8..73880b4 100644 --- a/src/commands/use_command.rs +++ b/src/commands/use_command.rs @@ -1,10 +1,7 @@ -use crate::{ - errors::{CommandExecutionError, VirtualEnvError}, - virtualenv::{pyenv::Pyenv, rsenv::Rsenv, traits::VirtualEnvCompatible}, -}; +use crate::virtualenv::{pyenv::Pyenv, rsenv::Rsenv, traits::VirtualEnvCompatible}; +use anyhow::{bail, Result}; use clap::Parser; -use error_stack::{Report, Result}; -use simplelog::error; +use simplelog::debug; #[derive(Debug, Parser)] pub struct UseCommand { @@ -12,31 +9,25 @@ pub struct UseCommand { venv: String, } -fn try_save(f: &dyn VirtualEnvCompatible, venv: String) -> Result<(), VirtualEnvError> { +fn try_save(f: &dyn VirtualEnvCompatible, venv: String) -> Result<()> { if f.list().contains(&venv) { f.save(&venv)?; return Ok(()); } - Err( - Report::new(VirtualEnvError::NotVirtualEnv(venv.clone())).attach_printable(format!( - "{} not in the list of existing virtual environments", - venv - )), - ) + bail!("{} not in the list of existing virtual environments", venv) } impl UseCommand { - pub fn execute(&self) -> Result<(), CommandExecutionError> { + pub fn execute(&self) -> Result<()> { match try_save(&Rsenv, self.venv.clone()) { Ok(()) => return Ok(()), - Err(e) => error!("{e}"), + Err(e) => debug!("Rsenv: {e}"), } match try_save(&Pyenv, self.venv.clone()) { Ok(()) => return Ok(()), - Err(e) => error!("{e}"), + Err(e) => debug!("Pyenv: {e}"), } - error!("Virtual environment {} doesn't exist", &self.venv); - Ok(()) + bail!("Virtual environment {} doesn't exist", &self.venv); } } diff --git a/src/errors.rs b/src/errors.rs deleted file mode 100644 index dc13434..0000000 --- a/src/errors.rs +++ /dev/null @@ -1,68 +0,0 @@ -use core::fmt; - -use error_stack::Context; - -#[derive(Debug, Clone)] -pub enum VirtualEnvError { - VenvBuildError, - NotVirtualEnv(String), - VenvIsNotActive, - AlreadyExists(String), - CreatingError, - IOError, - ConfigurationError, - IncorrectName, - TemplateRenderError, - ShellDetectionError, -} - -impl fmt::Display for VirtualEnvError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let data = match self { - VirtualEnvError::NotVirtualEnv(name) => { - format!("{name} is not a valid virtual environment") - } - VirtualEnvError::VenvBuildError => "Unable to detect virtual environment".to_owned(), - VirtualEnvError::VenvIsNotActive => "Virtual environment is not active".to_owned(), - VirtualEnvError::IOError => "Unknown I/O error.".to_owned(), - VirtualEnvError::AlreadyExists(name) => { - format!("Virtual environment {name} already exists.") - } - VirtualEnvError::ConfigurationError => "Configuration error".to_owned(), - VirtualEnvError::CreatingError => "Error while creating virtual environment".to_owned(), - VirtualEnvError::IncorrectName => "Incorrect virtual environment name".to_owned(), - VirtualEnvError::TemplateRenderError => "Unable to render template".to_owned(), - VirtualEnvError::ShellDetectionError => "Unable to detect current shell".to_owned(), - }; - f.write_str(&data) - } -} - -impl Context for VirtualEnvError {} - -#[derive(Debug, Clone)] -pub struct CommandExecutionError { - pub command: String, -} - -impl fmt::Display for CommandExecutionError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(&format!("Error running command {}", self.command)) - } -} - -impl Context for CommandExecutionError {} - -#[derive(Debug)] -pub enum PythonInterpreterError { - UnableToDetectVersion, - CreateVenvError, -} - -impl fmt::Display for PythonInterpreterError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("Error running python interpreter") - } -} - -impl Context for PythonInterpreterError {} diff --git a/src/main.rs b/src/main.rs index fca6994..822fd7c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,7 +5,6 @@ use simplelog::{ mod arguments; mod commands; mod configuration; -mod errors; mod shell; mod virtualenv; @@ -18,13 +17,15 @@ fn main() { 2 => LevelFilter::Debug, _ => LevelFilter::Trace, }; - CombinedLogger::init(vec![TermLogger::new( + if let Err(e) = CombinedLogger::init(vec![TermLogger::new( log_level, Config::default(), TerminalMode::Stderr, ColorChoice::Auto, - )]) - .unwrap(); + )]) { + error!("Unable to initialize logger: {e:?}"); + } + // execute sub-command if let Err(e) = cli.command.unwrap().execute() { error!("{e}"); diff --git a/src/shell/mod.rs b/src/shell/mod.rs index d67e8fa..56db6e2 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -1,18 +1,15 @@ mod bash; mod fish; mod zsh; - use serde::Serialize; -use simplelog::{debug, error}; +use simplelog::debug; use std::path::{Path, PathBuf}; use std::os::unix::process::parent_id; use sysinfo::{Pid, System}; use tinytemplate::TinyTemplate; -use error_stack::{Report, Result, ResultExt}; - -use crate::errors::VirtualEnvError; +use anyhow::{bail, Context, Result}; #[derive(Debug, PartialEq)] pub enum SupportedShell { @@ -33,26 +30,25 @@ struct DeactivateTemplateContext { } impl SupportedShell { - pub fn new() -> Result { + pub fn new() -> Result { let mut system = System::new(); system.refresh_processes(); let name = match system.process(Pid::from_u32(parent_id())) { Some(parent_process) => parent_process.name(), None => { - error!("Unable to detect parent process"); - return Err(Report::new(VirtualEnvError::ShellDetectionError)); + bail!("Unable to detect parent process"); } }; debug!("Parent process: {name:?}"); SupportedShell::from_name(name) } - fn from_name(input: &str) -> Result { + fn from_name(input: &str) -> Result { match input { "zsh" => Ok(SupportedShell::Zsh), "bash" => Ok(SupportedShell::Bash), "fish" => Ok(SupportedShell::Fish), - _ => Err(Report::new(VirtualEnvError::ShellDetectionError)), + _ => bail!("Unable to detect shell from {input}"), } } @@ -69,15 +65,14 @@ impl SupportedShell { _ => bash::ACTIVATE_TEMPLATE, } } - pub(crate) fn get_config_path(&self) -> Result { + pub(crate) fn get_config_path(&self) -> Result { let config = match self { SupportedShell::Fish => fish::CONFIG, SupportedShell::Zsh => zsh::CONFIG, SupportedShell::Bash => bash::CONFIG, }; let result = shellexpand::full(config) - .change_context(VirtualEnvError::ConfigurationError) - .attach_printable("Incorrect config file")? + .context("Unable to expand config file path")? .into_owned(); Ok(result) } @@ -103,28 +98,24 @@ impl SupportedShell { } } - pub fn render_activate( - &self, - venv_root: PathBuf, - current_path: PathBuf, - ) -> Result { + pub fn render_activate(&self, venv_root: PathBuf, current_path: PathBuf) -> Result { let context = ActivateTemplateContext { activate_path: format!("{}", &self.get_activate_path(&venv_root).display()), current_directory: format!("{}", ¤t_path.display()), }; let mut tt = TinyTemplate::new(); tt.add_template("activate", self.get_activate_template()) - .change_context(VirtualEnvError::TemplateRenderError)?; + .context("Unable to add activation template")?; tt.render("activate", &context) - .change_context(VirtualEnvError::TemplateRenderError) + .context("Unable to render activation template") } - pub fn render_deactivate(&self, forced: bool) -> Result { + pub fn render_deactivate(&self, forced: bool) -> Result { let context = DeactivateTemplateContext { forced }; let mut tt = TinyTemplate::new(); tt.add_template("deactivate", self.get_deactivate_template()) - .change_context(VirtualEnvError::TemplateRenderError)?; + .context("Unable to add deactivation template")?; tt.render("deactivate", &context) - .change_context(VirtualEnvError::TemplateRenderError) + .context("Unable to render deactivation template") } } diff --git a/src/virtualenv.rs b/src/virtualenv.rs index 057a1b2..38e52ed 100644 --- a/src/virtualenv.rs +++ b/src/virtualenv.rs @@ -5,7 +5,8 @@ pub mod rsenv; pub mod traits; mod utils; -use error_stack::{Result, ResultExt}; +use anyhow::Context; +use anyhow::Result; use simplelog::info; use std::collections::HashSet; @@ -16,7 +17,6 @@ use self::local::Local; use self::pyenv::Pyenv; use self::rsenv::Rsenv; use self::traits::VirtualEnvCompatible; -use crate::errors::VirtualEnvError; use crate::shell::SupportedShell; use crate::virtualenv::utils::{get_current_dir, is_virtualenv}; @@ -27,7 +27,7 @@ pub struct VirtualEnvironment<'a> { } impl<'a> VirtualEnvironment<'a> { - pub fn new(kind: &'a dyn VirtualEnvCompatible) -> Result { + pub fn new(kind: &'a dyn VirtualEnvCompatible) -> Result { Ok(VirtualEnvironment { kind, shell: SupportedShell::new()?, @@ -54,30 +54,27 @@ impl<'a> VirtualEnvironment<'a> { None } - pub fn activate(&self, venv_name: Option<&String>) -> Result<(), VirtualEnvError> { + pub fn activate(&self, venv_name: Option<&String>) -> Result<()> { let path = self.kind.path(venv_name)?; is_virtualenv(&path)?; info!("Activating {path:?}"); let command = self.shell.render_activate(path, get_current_dir()?)?; io::stdout() .write_all(command.as_bytes()) - .attach_printable("Unable to write to STDOUT") - .change_context(VirtualEnvError::IOError)?; + .context("Unable to write to STDOUT")?; Ok(()) } - pub fn deactivate(force: bool) -> Result<(), VirtualEnvError> { - let value = - std::env::var("VIRTUAL_ENV").change_context(VirtualEnvError::VenvIsNotActive)?; + pub fn deactivate(force: bool) -> Result<()> { + let value = std::env::var("VIRTUAL_ENV").context("Unable to read VIRTUAL_ENV")?; info!("Deactivating {value:?}"); let shell = SupportedShell::new()?; let command = shell.render_deactivate(force)?; io::stdout() .write_all(command.as_bytes()) - .attach_printable("Unable to write to STDOUT") - .change_context(VirtualEnvError::IOError)?; + .context("Unable to write to STDOUT")?; Ok(()) } diff --git a/src/virtualenv/local.rs b/src/virtualenv/local.rs index a2824f4..6e9af30 100644 --- a/src/virtualenv/local.rs +++ b/src/virtualenv/local.rs @@ -1,8 +1,6 @@ -use error_stack::{Report, Result}; +use anyhow::{bail, Result}; use std::path::PathBuf; -use crate::errors::VirtualEnvError; - use super::{ traits::VirtualEnvCompatible, utils::{get_current_dir, is_virtualenv}, @@ -12,7 +10,7 @@ use super::{ pub struct Local; impl VirtualEnvCompatible for Local { - fn root_dir(&self) -> Result { + fn root_dir(&self) -> Result { get_current_dir() } @@ -20,18 +18,13 @@ impl VirtualEnvCompatible for Local { self.venv_name().is_ok() } - fn venv_name(&self) -> Result { + fn venv_name(&self) -> Result { let current_path = self.root_dir()?; for local_venv_path in ["venv", ".venv", "virtualenv", ".virtualenv"] { if is_virtualenv(¤t_path.join(local_venv_path)).is_ok() { return Ok(local_venv_path.to_string()); } } - Err( - Report::new(VirtualEnvError::NotVirtualEnv(".".into())).attach_printable(format!( - "No local venv in {}", - current_path.as_path().display() - )), - ) + bail!("No local venv in {}", current_path.as_path().display()); } } diff --git a/src/virtualenv/pyenv.rs b/src/virtualenv/pyenv.rs index 7e5e0af..6743597 100644 --- a/src/virtualenv/pyenv.rs +++ b/src/virtualenv/pyenv.rs @@ -1,16 +1,13 @@ -use error_stack::{Result, ResultExt}; +use anyhow::{Context, Result}; use std::{ collections::HashSet, fs::{self, File}, + io::Write, path::{Path, PathBuf}, }; -use std::io::Write; - use simplelog::{debug, info}; -use crate::errors::VirtualEnvError; - use super::{ traits::VirtualEnvCompatible, utils::{get_current_dir, get_venvs_by_glob}, @@ -20,11 +17,10 @@ use super::{ pub struct Pyenv; impl VirtualEnvCompatible for Pyenv { - fn root_dir(&self) -> Result { + fn root_dir(&self) -> Result { let root = std::env::var("PYENV_ROOT").unwrap_or("~/.pyenv".to_string()); let expanded = shellexpand::full(&root) - .change_context(VirtualEnvError::ConfigurationError) - .attach_printable("unable to expand PYENV_ROOT to the actual path")? + .context("unable to expand PYENV_ROOT to the actual path")? .to_string(); Ok(Path::new(&expanded).to_path_buf().join("versions")) } @@ -51,23 +47,20 @@ impl VirtualEnvCompatible for Pyenv { false } - fn venv_name(&self) -> Result { + fn venv_name(&self) -> Result { Ok( fs::read_to_string(get_current_dir()?.join(".python-version")) - .change_context(VirtualEnvError::IOError) - .attach_printable("Unable to read .python-verion")? + .context("Unable to read .python-verion")? .trim() .to_string(), ) } - fn save(&self, name: &str) -> Result<(), VirtualEnvError> { + fn save(&self, name: &str) -> Result<()> { File::create(".python-version") - .change_context(VirtualEnvError::IOError) - .attach_printable("Unable to create .python-version")? + .context("Unable to create .python-version")? .write_all(name.as_bytes()) - .change_context(VirtualEnvError::IOError) - .attach_printable("Unable to save data .python-version")?; + .context("Unable to save data .python-version")?; info!("Saved changes to .python-version"); fs::remove_file(".python-virtualenv").unwrap_or_default(); Ok(()) diff --git a/src/virtualenv/python.rs b/src/virtualenv/python.rs index 2e9cc8d..e0827c5 100644 --- a/src/virtualenv/python.rs +++ b/src/virtualenv/python.rs @@ -1,18 +1,16 @@ use std::{path::PathBuf, process, str::FromStr}; -use error_stack::{Report, Result, ResultExt}; +use anyhow::{bail, Context, Result}; use glob::Pattern; use simplelog::info; -use crate::errors::PythonInterpreterError; - pub struct PythonInterpreter<'a> { pub version: String, pub interpreter: &'a String, } impl<'a> PythonInterpreter<'a> { - pub fn new(interpreter: &'a String) -> Result { + pub fn new(interpreter: &'a String) -> Result { let version = PythonInterpreter::detect_version(interpreter)?; Ok(PythonInterpreter { version, @@ -20,7 +18,7 @@ impl<'a> PythonInterpreter<'a> { }) } - pub fn create_venv(&self, path: &PathBuf) -> Result<(), PythonInterpreterError> { + pub fn create_venv(&self, path: &PathBuf) -> Result<()> { info!( "Executing {} -m venv {}", self.interpreter, @@ -31,40 +29,31 @@ impl<'a> PythonInterpreter<'a> { .arg("venv") .arg(path) .status() - .change_context(PythonInterpreterError::CreateVenvError)?; + .context("Error spawning interpreter")?; if status.code().unwrap_or_default() > 0 { - return Err( - Report::new(PythonInterpreterError::CreateVenvError).attach_printable(format!( - "Error creating venv {}: ", - path.as_path().display() - )), - ); + bail!("Error creating venv {}: ", path.as_path().display()); } Ok(()) } - fn detect_version(interpreter: &String) -> Result { + fn detect_version(interpreter: &String) -> Result { info!("Detecting python version"); let output = process::Command::new(interpreter) .arg("-c") .arg(r#"import platform; print(platform.python_version())"#) .output() - .change_context(PythonInterpreterError::UnableToDetectVersion) - .attach_printable_lazy(|| "{:?}")?; + .context("Unable to spawn interpreter")?; if output.status.code().unwrap_or(1) != 0 { - return Err(Report::new(PythonInterpreterError::UnableToDetectVersion) - .attach_printable(format!("Python executable returned {}", output.status,))); + bail!("Python executable returned {}", output.status); } - let mut version = String::from_utf8(output.stdout) - .change_context(PythonInterpreterError::UnableToDetectVersion) - .attach_printable_lazy(|| "unable to read version from stdout: {:?}")?; + let mut version = + String::from_utf8(output.stdout).context("unable to read version from stdout")?; version = version.trim().into(); if !Pattern::from_str("*.*.*").unwrap().matches(&version) { - return Err(Report::new(PythonInterpreterError::UnableToDetectVersion) - .attach_printable(format!("Unexpected python version {}", &version))); + bail!("Unexpected python version {}", &version); } Ok(version) diff --git a/src/virtualenv/rsenv.rs b/src/virtualenv/rsenv.rs index 5588bdd..89c75a7 100644 --- a/src/virtualenv/rsenv.rs +++ b/src/virtualenv/rsenv.rs @@ -1,13 +1,13 @@ use std::{ collections::HashSet, - fs::{self, File}, + fs::{self, create_dir_all, File}, path::{Path, PathBuf}, }; -use crate::{configuration::SETTINGS, errors::VirtualEnvError}; -use error_stack::{Report, Result, ResultExt}; +use crate::configuration::SETTINGS; +use anyhow::{anyhow, bail, Context, Result}; use regex::Regex; -use simplelog::{error, info}; +use simplelog::info; use std::io::Write; use super::{ @@ -20,77 +20,64 @@ use super::{ pub struct Rsenv; impl Rsenv { - pub fn validate_name(name: &str) -> Result<(), VirtualEnvError> { + pub fn validate_name(name: &str) -> Result<()> { if Regex::new(r"^[\w._]*$").unwrap().is_match(name) || Regex::new(r"^[\w._]*\/[\w._]*$").unwrap().is_match(name) { return Ok(()); } - Err(Report::new(VirtualEnvError::IncorrectName).attach("name is invalid")) + bail!("name {name} is invalid"); } - pub fn create(&self, name: &String, python: &String) -> Result<(), VirtualEnvError> { - let interpreter = - PythonInterpreter::new(python).change_context(VirtualEnvError::CreatingError)?; + pub fn create(&self, name: &String, python: &String) -> Result<()> { + let interpreter = PythonInterpreter::new(python)?; let name_with_version = format!("{}/{}", &interpreter.version, name); let existing = self.list(); Rsenv::validate_name(name)?; if existing.contains(name) || existing.contains(&name_with_version) { - error!("Virtual environment {name} exists"); - return Err(Report::new(VirtualEnvError::AlreadyExists( - name.to_string(), - ))); + bail!("Virtual environment {name} exists"); } let root_dir = self.root_dir()?; let path = root_dir.as_path(); if !path.exists() { - let _ = fs::create_dir_all(path) - .change_context(VirtualEnvError::CreatingError) - .attach_printable("Unable to create root directory for virtual env"); + create_dir_all(path).context("Unable to create root directory for virtual env")?; info!("Created root dir"); } let venv_path = path.join(&interpreter.version).join(name); - interpreter - .create_venv(&venv_path) - .change_context(VirtualEnvError::CreatingError)?; + interpreter.create_venv(&venv_path)?; info!("Created venv {name_with_version}"); Ok(()) } - pub fn delete(&self, name: String) -> Result<(), VirtualEnvError> { + pub fn delete(&self, name: String) -> Result<()> { Rsenv::validate_name(&name)?; if !self.list().contains(&name) { - error!("Virtual environment `{name}` is not found"); - return Err(Report::new(VirtualEnvError::NotVirtualEnv(name.clone())) - .attach_printable(format!( - "Cannot delete virtual environment: {} is not managed by rsenv", - name - ))); + bail!( + "Cannot delete virtual environment: {} is not managed by rsenv", + name + ); } fs::remove_dir_all(self.root_dir()?.join(name.clone()).as_path()) - .change_context(VirtualEnvError::IOError) - .attach_printable("Unable to delete virtual env")?; + .context("Unable to delete virtual env")?; info!("Deleted venv {}", name); Ok(()) } } impl VirtualEnvCompatible for Rsenv { - fn root_dir(&self) -> Result { + fn root_dir(&self) -> Result { let expanded = shellexpand::full( &SETTINGS .read() - .map_err(|_| Report::new(VirtualEnvError::ConfigurationError)) - .attach_printable("unable to read path from SETTINGS")? + .map_err(|e| anyhow!("Unable to read path from SETTINGS: {e:?}"))? .path, ) - .change_context(VirtualEnvError::ConfigurationError) - .attach_printable("unable to expand SETTINGS.path to the actual path")? + .context("unable to expand SETTINGS.path to the actual path")? .to_string(); Ok(Path::new(&expanded).to_path_buf().join("venvs")) } @@ -116,20 +103,18 @@ impl VirtualEnvCompatible for Rsenv { false } - fn venv_name(&self) -> Result { + fn venv_name(&self) -> Result { Ok( fs::read_to_string(get_current_dir()?.join(".python-virtualenv")) - .change_context(VirtualEnvError::IOError) - .attach_printable("Unable to read .python-virtualenv")? + .context("Unable to read .python-virtualenv")? .trim() .to_string(), ) } - fn save(&self, name: &str) -> Result<(), VirtualEnvError> { + fn save(&self, name: &str) -> Result<()> { let _ = File::create(".python-virtualenv") - .change_context(VirtualEnvError::IOError) - .attach_printable("Unable to create .python-virtualenv")? + .context("Unable to create .python-virtualenv")? .write_all(name.as_bytes()); info!("Saved changes to .python-virtualenv"); fs::remove_file(".python-version").unwrap_or_default(); diff --git a/src/virtualenv/traits.rs b/src/virtualenv/traits.rs index f1258fa..2067e85 100644 --- a/src/virtualenv/traits.rs +++ b/src/virtualenv/traits.rs @@ -1,11 +1,9 @@ -use crate::errors::VirtualEnvError; - use super::utils::is_virtualenv; -use error_stack::Result; +use anyhow::Result; use std::{collections::HashSet, path::PathBuf}; pub trait VirtualEnvCompatible { - fn root_dir(&self) -> Result; + fn root_dir(&self) -> Result; fn list(&self) -> HashSet { HashSet::new() @@ -13,16 +11,16 @@ pub trait VirtualEnvCompatible { fn relevant(&self) -> bool; - fn venv_name(&self) -> Result; + fn venv_name(&self) -> Result; - fn path(&self, name: Option<&String>) -> Result { + fn path(&self, name: Option<&String>) -> Result { let venv_name = self.venv_name()?; let b = self.root_dir()?.join(name.unwrap_or(&venv_name)); is_virtualenv(&b)?; Ok(b) } - fn save(&self, _name: &str) -> Result<(), VirtualEnvError> { + fn save(&self, _name: &str) -> Result<()> { Ok(()) } } diff --git a/src/virtualenv/utils.rs b/src/virtualenv/utils.rs index 268180e..18aec6f 100644 --- a/src/virtualenv/utils.rs +++ b/src/virtualenv/utils.rs @@ -4,38 +4,29 @@ use std::{ path::{Path, PathBuf}, }; -use error_stack::{Report, Result, ResultExt}; +use anyhow::{bail, Context, Result}; -use crate::errors::VirtualEnvError; - -pub fn is_virtualenv(path: &Path) -> Result<(), VirtualEnvError> { +pub fn is_virtualenv(path: &Path) -> Result<()> { if fs::metadata(path.join("bin").join("activate")).is_ok_and(|x| x.is_file()) { Ok(()) } else { - Err(Report::new(VirtualEnvError::NotVirtualEnv( - path.to_string_lossy().to_string(), - )) - .attach_printable("Is not a virtual environment")) + bail!("{} is not a virtual environment", path.display()); } } -pub fn get_current_dir() -> Result { - std::env::current_dir() - .attach_printable("Unable to get current dir") - .change_context(VirtualEnvError::VenvBuildError) +pub fn get_current_dir() -> Result { + std::env::current_dir().context("Unable to get current dir") } -pub fn get_venvs_by_glob(glob: String, dir: &PathBuf) -> Result, VirtualEnvError> { +pub fn get_venvs_by_glob(glob: String, dir: &PathBuf) -> Result> { let mut result = HashSet::new(); for path in glob::glob(format!("{}/{glob}", &dir.as_path().display()).as_str()) - .attach_printable(format!("Unable to parse glob {glob}")) - .change_context(VirtualEnvError::VenvBuildError)? + .with_context(|| format!("Unable to parse glob {glob}"))? .flatten() { let value = path .strip_prefix(dir) - .attach_printable("Unable to strip prefix") - .change_context(VirtualEnvError::VenvBuildError)? + .context("Unable to strip prefix")? .to_str(); if let Some(unwrapped) = value { if is_virtualenv(&path).is_ok() {