diff --git a/Cargo.lock b/Cargo.lock index 435c6b6..8aa6af0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -453,6 +453,7 @@ dependencies = [ "strum_macros", "tar", "tempfile", + "thiserror", "toml", "update-informer", "xz2", diff --git a/Cargo.toml b/Cargo.toml index 1ae5405..8b9384f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,6 +36,7 @@ serde = { version = "1.0.146", features = ["derive"] } miette = "5.4.1" regex = "1.7.0" serde_json = "1.0.87" +thiserror = "1.0.37" update-informer = "0.5.0" [target.aarch64-unknown-linux-gnu.dependencies] diff --git a/src/config.rs b/src/config.rs index 2306e60..08bb98e 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,6 +1,6 @@ -use crate::{host_triple::HostTriple, targets::Target, toolchain::rust::XtensaRust}; +use crate::{error::Error, host_triple::HostTriple, targets::Target, toolchain::rust::XtensaRust}; use directories_next::ProjectDirs; -use miette::{ErrReport, IntoDiagnostic, Result, WrapErr}; +use miette::{IntoDiagnostic, Result}; use serde::{Deserialize, Serialize}; use std::{ collections::HashSet, @@ -31,31 +31,34 @@ pub struct Config { impl Config { /// Load the config from config file - pub fn load() -> Result { + pub fn load() -> Result { let dirs = ProjectDirs::from("rs", "esp", "espup").unwrap(); let file = dirs.config_dir().join("espup.toml"); - let config = if let Ok(data) = read(file) { - toml::from_slice(&data).into_diagnostic()? + let config = if let Ok(data) = read(&file) { + toml::from_slice(&data) + .into_diagnostic() + .map_err(|_| Error::FailedToDeserialize)? } else { - return Err(ErrReport::msg("No config file found")); + return Err(Error::FileNotFound(file.to_string_lossy().into_owned())); }; Ok(config) } /// Save the config to file - pub fn save(&self) -> Result<()> { + pub fn save(&self) -> Result<(), Error> { let dirs = ProjectDirs::from("rs", "esp", "espup").unwrap(); let file = dirs.config_dir().join("espup.toml"); let serialized = toml::to_string(&self.clone()) .into_diagnostic() - .wrap_err("Failed to serialize config")?; + .map_err(|_| Error::FailedToSerialize)?; create_dir_all(file.parent().unwrap()) .into_diagnostic() - .wrap_err("Failed to create config directory")?; + .map_err(|e| Error::FailedToCreateConfigFile(e.to_string()))?; write(&file, serialized) .into_diagnostic() - .wrap_err_with(|| format!("Failed to write config to {}", file.display())) + .map_err(|_| Error::FailedToWrite(file.display().to_string()))?; + Ok(()) } } diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..fe0bdf3 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,81 @@ +use crate::emoji; + +#[derive(Debug, miette::Diagnostic, thiserror::Error)] +pub enum Error { + // Host Triple + #[diagnostic(code(espup::host_triple::unsupported_host_triple))] + #[error("{} Host triple '{0}' is not supported", emoji::ERROR)] + UnsupportedHostTriple(String), + // Target + #[diagnostic(code(espup::targets::unsupported_target))] + #[error("{} Target '{0}' is not supported", emoji::ERROR)] + UnsupportedTarget(String), + // Config + #[diagnostic(code(espup::config::file_not_found))] + #[error("{} No config file found in '{0}'", emoji::ERROR)] + FileNotFound(String), + #[diagnostic(code(espup::config::failed_to_deserialize))] + #[error("{} Failed to deserialize config", emoji::ERROR)] + FailedToDeserialize, + #[diagnostic(code(espup::config::failed_to_serialize))] + #[error("{} Failed to serialize config", emoji::ERROR)] + FailedToSerialize, + #[diagnostic(code(espup::config::failed_to_create_config_file))] + #[error("{} Failed to create config directory", emoji::ERROR)] + FailedToCreateConfigFile(String), + #[diagnostic(code(espup::config::failed_to_write))] + #[error("{} Failed to write config to '{0}'", emoji::ERROR)] + FailedToWrite(String), + // Toolchain + #[error(transparent)] + IoError(#[from] std::io::Error), + #[error(transparent)] + RewquestError(#[from] reqwest::Error), + #[diagnostic(code(espup::toolchain::failed_to_create_directory))] + #[error("{} Creating directory '{0}' failed", emoji::ERROR)] + FailedToCreateDirectory(String), + #[diagnostic(code(espup::toolchain::unsupported_file_extension))] + #[error("{} Unsuported file extension: '{0}'", emoji::ERROR)] + UnsuportedFileExtension(String), + // Toolchain - Rust + #[diagnostic(code(espup::toolchain::rust::failed_to_get_latest_version))] + #[error("{} Failed To serialize Json from string.", emoji::ERROR)] + FailedToSerializeJson, + #[diagnostic(code(espup::toolchain::rust::xtensa_rust_already_installed))] + #[error("{} Previous installation of Rust Toolchain exists in: '{0}'. Please, remove the directory before new installation.", emoji::ERROR)] + XtensaToolchainAlreadyInstalled(String), + #[diagnostic(code(espup::toolchain::rust::invalid_version))] + #[error( + "{} Invalid toolchain version '{0}', must be in the form of '...'", + emoji::ERROR + )] + InvalidXtensaToolchanVersion(String), + #[diagnostic(code(espup::toolchain::rust::detection_error))] + #[error("{} Error detecting rustup: {0}", emoji::ERROR)] + RustupDetectionError(String), + #[error(transparent)] + CmdError(#[from] embuild::cmd::CmdError), + // Toolchain - ESP-IDF + #[diagnostic(code(espup::toolchain::espidf::failed_to_instatiate_cmake))] + #[error("{} Failed to add CMake to ESP-IDF tools", emoji::ERROR)] + FailedToInstantiateCmake, + #[diagnostic(code(espup::toolchain::espidf::failed_to_create_esp_idf_install_closure))] + #[error("{} Failed to create ESP-IDF install closure", emoji::ERROR)] + FailedToCreateEspIdfInstallClosure, + #[diagnostic(code(espup::toolchain::espidf::failed_to_install_esp_idf))] + #[error("{} Failed to install ESP-IDF", emoji::ERROR)] + FailedToInstallEspIdf, + // Main + #[diagnostic(code(espup::wrong_windows_arguments))] + #[error( + "{} When installing esp-idf in Windows, only --targets \"all\" is supported.", + emoji::ERROR + )] + WrongWindowsArguments, + #[diagnostic(code(espup::failed_to_remove_directory))] + #[error( + "{} Failed to remove '{0}' direcretory. Please, manually verify that the directory is properly removed.", + emoji::ERROR + )] + FailedToRemoveDirectory(String), +} diff --git a/src/host_triple.rs b/src/host_triple.rs index d5e3cdd..8f83084 100644 --- a/src/host_triple.rs +++ b/src/host_triple.rs @@ -1,5 +1,4 @@ -use crate::emoji; -use anyhow::{Context, Result}; +use crate::error::Error; use guess_host_triple::guess_host_triple; use serde::{Deserialize, Serialize}; use std::str::FromStr; @@ -30,17 +29,14 @@ pub enum HostTriple { } /// Parse the host triple if specified, otherwise guess it. -pub fn get_host_triple(host_triple_arg: Option) -> Result { - if let Some(host_triple_arg) = host_triple_arg { - HostTriple::from_str(&host_triple_arg).context(format!( - "{} Host triple '{}' is not supported.", - emoji::ERROR, - host_triple_arg, - )) +pub fn get_host_triple(host_triple_arg: Option) -> Result { + let host_triple = if let Some(host_triple) = &host_triple_arg { + host_triple } else { - HostTriple::from_str(guess_host_triple().unwrap()) - .context(format!("{} Unable to guess host triple.", emoji::ERROR,)) - } + guess_host_triple().unwrap() + }; + + HostTriple::from_str(host_triple).map_err(|_| Error::UnsupportedHostTriple(host_triple.into())) } #[cfg(test)] diff --git a/src/lib.rs b/src/lib.rs index 4cd83ce..9cc5672 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,6 @@ pub mod config; pub mod emoji; +pub mod error; pub mod host_triple; pub mod targets; pub mod toolchain; diff --git a/src/main.rs b/src/main.rs index 36dc284..f467c7d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,3 @@ -use anyhow::{bail, Result}; use clap::Parser; use directories_next::ProjectDirs; use dirs::home_dir; @@ -9,6 +8,7 @@ use embuild::{ use espup::{ config::Config, emoji, + error::Error, host_triple::get_host_triple, logging::initialize_logger, targets::{parse_targets, Target}, @@ -25,6 +25,7 @@ use espup::{ update::check_for_update, }; use log::{debug, info, warn}; +use miette::Result; use std::{ collections::HashSet, fs::{remove_dir_all, remove_file, File}, @@ -128,7 +129,7 @@ pub struct UninstallOpts { } /// Installs the Rust for ESP chips environment -fn install(args: InstallOpts) -> Result<()> { +fn install(args: InstallOpts) -> Result<(), Error> { initialize_logger(&args.log_level); check_for_update(env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION")); info!("{} Installing esp-rs", emoji::DISC); @@ -233,9 +234,7 @@ fn install(args: InstallOpts) -> Result<()> { targets, xtensa_rust, }; - if let Err(e) = config.save() { - bail!("{} Failed to save config {:#}", emoji::ERROR, e); - } + config.save()?; info!("{} Installation successfully completed!", emoji::CHECK); warn!( @@ -246,7 +245,7 @@ fn install(args: InstallOpts) -> Result<()> { } /// Uninstalls the Rust for ESP chips environment -fn uninstall(args: UninstallOpts) -> Result<()> { +fn uninstall(args: UninstallOpts) -> Result<(), Error> { initialize_logger(&args.log_level); check_for_update(env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION")); @@ -304,7 +303,7 @@ fn uninstall(args: UninstallOpts) -> Result<()> { } /// Updates Xtensa Rust toolchain. -fn update(args: UpdateOpts) -> Result<()> { +fn update(args: UpdateOpts) -> Result<(), Error> { initialize_logger(&args.log_level); check_for_update(env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION")); @@ -343,15 +342,13 @@ fn update(args: UpdateOpts) -> Result<()> { config.xtensa_rust = Some(xtensa_rust); } - if let Err(e) = config.save() { - bail!("{} Failed to save config {:#}", emoji::ERROR, e); - } + config.save()?; info!("{} Update successfully completed!", emoji::CHECK); Ok(()) } -fn main() -> Result<()> { +fn main() -> Result<(), Error> { match Cli::parse().subcommand { SubCommand::Install(args) => install(*args), SubCommand::Update(args) => update(args), @@ -360,7 +357,7 @@ fn main() -> Result<()> { } /// Deletes dist folder. -fn clear_dist_folder() -> Result<()> { +fn clear_dist_folder() -> Result<(), Error> { let dist_path = PathBuf::from(get_dist_path("")); if dist_path.exists() { info!("{} Clearing dist folder", emoji::WRENCH); @@ -370,7 +367,7 @@ fn clear_dist_folder() -> Result<()> { } /// Returns the absolute path to the export file, uses the DEFAULT_EXPORT_FILE if no arg is provided. -fn get_export_file(export_file: Option) -> Result { +fn get_export_file(export_file: Option) -> Result { if let Some(export_file) = export_file { if export_file.is_absolute() { Ok(export_file) @@ -385,7 +382,7 @@ fn get_export_file(export_file: Option) -> Result { } /// Creates the export file with the necessary environment variables. -fn export_environment(export_file: &PathBuf, exports: &[String]) -> Result<()> { +fn export_environment(export_file: &PathBuf, exports: &[String]) -> Result<(), Error> { info!("{} Creating export file", emoji::WRENCH); let mut file = File::create(export_file)?; for e in exports.iter() { @@ -413,17 +410,18 @@ fn export_environment(export_file: &PathBuf, exports: &[String]) -> Result<()> { #[cfg(windows)] /// For Windows, we need to check that we are installing all the targets if we are installing esp-idf. -pub fn check_arguments(targets: &HashSet, esp_idf_version: &Option) -> Result<()> { - if esp_idf_version.is_some() + +pub fn check_arguments( + targets: &HashSet, + espidf_version: &Option, +) -> Result<(), Error> { + if espidf_version.is_some() && (!targets.contains(&Target::ESP32) || !targets.contains(&Target::ESP32C3) || !targets.contains(&Target::ESP32S2) || !targets.contains(&Target::ESP32S3)) { - bail!( - "{} When installing esp-idf in Windows, only --targets \"all\" is supported.", - emoji::ERROR - ); + return Err(Error::WrongWindowsArguments); } Ok(()) diff --git a/src/targets.rs b/src/targets.rs index a2ac389..220b9de 100644 --- a/src/targets.rs +++ b/src/targets.rs @@ -1,7 +1,6 @@ //! ESP32 chip variants support. -use crate::emoji; -use anyhow::Context; +use crate::{emoji, error::Error}; use log::debug; use serde::{Deserialize, Serialize}; use std::{collections::HashSet, str::FromStr}; @@ -26,7 +25,7 @@ pub enum Target { } /// Returns a vector of Chips from a comma or space separated string. -pub fn parse_targets(targets_str: &str) -> Result, String> { +pub fn parse_targets(targets_str: &str) -> Result, Error> { debug!("{} Parsing targets: {}", emoji::DEBUG, targets_str); let targets_str = targets_str.to_lowercase(); @@ -35,18 +34,14 @@ pub fn parse_targets(targets_str: &str) -> Result, String> { let targets: HashSet = if targets_str.contains("all") { Target::iter().collect() } else { - targets_str - .split([',', ' ']) - .map(|target| { - Target::from_str(target) - .context(format!( - "{} Target '{}' is not supported", - emoji::ERROR, - target - )) - .unwrap() - }) - .collect() + let mut targets = HashSet::new(); + for target in targets_str.split([',', ' ']) { + targets.insert( + Target::from_str(target).map_err(|_| Error::UnsupportedTarget(target.into()))?, + ); + } + + targets }; debug!("{} Parsed targets: {:?}", emoji::DEBUG, targets); @@ -56,38 +51,33 @@ pub fn parse_targets(targets_str: &str) -> Result, String> { #[cfg(test)] mod tests { use crate::targets::{parse_targets, Target}; + use std::collections::HashSet; #[test] + #[allow(unused_variables)] fn test_parse_targets() { - assert_eq!( - parse_targets("esp32"), - Ok([Target::ESP32].into_iter().collect()) - ); - assert_eq!( - parse_targets("esp32,esp32s2"), - Ok([Target::ESP32, Target::ESP32S2].into_iter().collect()) - ); - assert_eq!( - parse_targets("esp32s3 esp32"), - Ok([Target::ESP32S3, Target::ESP32].into_iter().collect()) - ); - assert_eq!( - parse_targets("esp32s3,esp32,esp32c3"), - Ok([Target::ESP32S3, Target::ESP32, Target::ESP32C3] - .into_iter() - .collect()) - ); - assert_eq!( - parse_targets("all"), - Ok([ - Target::ESP32, - Target::ESP32S2, - Target::ESP32S3, - Target::ESP32C2, - Target::ESP32C3, - ] + let targets: HashSet = [Target::ESP32].into_iter().collect(); + assert!(matches!(parse_targets("esp32"), Ok(targets))); + let targets: HashSet = [Target::ESP32, Target::ESP32S2].into_iter().collect(); + assert!(matches!(parse_targets("esp32,esp32s2"), Ok(targets))); + let targets: HashSet = [Target::ESP32S3, Target::ESP32].into_iter().collect(); + assert!(matches!(parse_targets("esp32s3 esp32"), Ok(targets))); + let targets: HashSet = [Target::ESP32S3, Target::ESP32, Target::ESP32C3] .into_iter() - .collect()) - ); + .collect(); + assert!(matches!( + parse_targets("esp32s3,esp32,esp32c3"), + Ok(targets) + )); + let targets: HashSet = [ + Target::ESP32, + Target::ESP32S2, + Target::ESP32S3, + Target::ESP32C2, + Target::ESP32C3, + ] + .into_iter() + .collect(); + assert!(matches!(parse_targets("all"), Ok(targets))); } } diff --git a/src/toolchain/espidf.rs b/src/toolchain/espidf.rs index 1d7b754..5095ced 100644 --- a/src/toolchain/espidf.rs +++ b/src/toolchain/espidf.rs @@ -2,18 +2,19 @@ use crate::{ emoji, + error::Error, targets::Target, toolchain::{ gcc::{get_toolchain_name, get_ulp_toolchain_name}, get_home_dir, }, }; -use anyhow::{Context, Result}; use embuild::{espidf, espidf::EspIdfRemote, git}; use log::{debug, info}; -use std::collections::HashSet; +use miette::Result; use std::{ collections::hash_map::DefaultHasher, + collections::HashSet, env, fs::remove_dir_all, hash::{Hash, Hasher}, @@ -64,18 +65,22 @@ pub struct EspIdfRepo { impl EspIdfRepo { /// Installs esp-idf. - pub fn install(self) -> Result> { + pub fn install(self) -> Result, Error> { let cmake_generator = DEFAULT_CMAKE_GENERATOR; let mut exports: Vec = Vec::new(); // A closure to specify which tools `idf-tools.py` should install. let make_tools = move |repo: &git::Repository, - version: &Result| - -> Result> { + version: &anyhow::Result| + -> anyhow::Result> { + let version_str = match version { + Ok(v) => format!("v{v}"), + Err(_) => "(unknown version)".to_string(), + }; info!( "{} Using esp-idf {} at '{}'", emoji::INFO, - espidf::EspIdfVersion::format(version), + version_str, repo.worktree().display() ); @@ -99,7 +104,8 @@ impl EspIdfRepo { subtools.push("cmake".to_string()) } _ => { - tools.push(espidf::Tools::cmake()?); + tools + .push(espidf::Tools::cmake().map_err(|_| Error::FailedToInstantiateCmake)?); } } #[cfg(windows)] @@ -119,13 +125,15 @@ impl EspIdfRepo { Ok(tools) }; - let install = |esp_idf_origin: espidf::EspIdfOrigin| -> Result { - espidf::Installer::new(esp_idf_origin) - .install_dir(Some(self.install_path.clone())) - .with_tools(make_tools) - .install() - .context("Could not install esp-idf") - }; + + let install = + |esp_idf_origin: espidf::EspIdfOrigin| -> anyhow::Result { + espidf::Installer::new(esp_idf_origin) + .install_dir(Some(self.install_path.clone())) + .with_tools(make_tools) + .install() + .map_err(|_| Error::FailedToCreateEspIdfInstallClosure) + }; let repo = espidf::EspIdfRemote { git_ref: espidf::parse_esp_idf_git_ref(&self.version), @@ -134,7 +142,7 @@ impl EspIdfRepo { let espidf_origin = espidf::EspIdfOrigin::Managed(repo.clone()); #[cfg(unix)] - let espidf = install(espidf_origin)?; + let espidf = install(espidf_origin).map_err(|_| Error::FailedToInstallEspIdf)?; #[cfg(windows)] install(espidf_origin)?; let espidf_dir = get_install_path(repo); diff --git a/src/toolchain/gcc.rs b/src/toolchain/gcc.rs index 10abcfc..3b61543 100644 --- a/src/toolchain/gcc.rs +++ b/src/toolchain/gcc.rs @@ -2,13 +2,14 @@ use crate::{ emoji, + error::Error, host_triple::HostTriple, targets::Target, toolchain::{download_file, espidf::get_tool_path}, }; -use anyhow::Result; use embuild::espidf::EspIdfVersion; use log::{debug, info, warn}; +use miette::Result; use std::{ collections::HashSet, path::{Path, PathBuf}, @@ -43,7 +44,7 @@ impl Gcc { } /// Installs the gcc toolchain. - pub fn install(&self) -> Result<()> { + pub fn install(&self) -> Result<(), Error> { let target_dir = format!("{}/{}-{}", self.toolchain_name, self.release, self.version); let gcc_path = get_tool_path(&target_dir); let extension = get_artifact_extension(&self.host_triple); @@ -140,7 +141,7 @@ pub fn get_ulp_toolchain_name(target: Target, version: Option<&EspIdfVersion>) - pub fn install_gcc_targets( targets: &HashSet, host_triple: &HostTriple, -) -> Result> { +) -> Result, Error> { info!("{} Installing gcc for build targets", emoji::WRENCH); let mut exports: Vec = Vec::new(); for target in targets { diff --git a/src/toolchain/llvm.rs b/src/toolchain/llvm.rs index ff93385..e0e886a 100644 --- a/src/toolchain/llvm.rs +++ b/src/toolchain/llvm.rs @@ -2,11 +2,12 @@ use crate::{ emoji, + error::Error, host_triple::HostTriple, toolchain::{download_file, espidf::get_tool_path}, }; -use anyhow::{Ok, Result}; use log::{info, warn}; +use miette::Result; use std::path::{Path, PathBuf}; const DEFAULT_LLVM_REPOSITORY: &str = "https://github.com/espressif/llvm-project/releases/download"; @@ -48,7 +49,7 @@ impl Llvm { } /// Installs the LLVM toolchain. - pub fn install(&self) -> Result> { + pub fn install(&self) -> Result, Error> { let mut exports: Vec = Vec::new(); if Path::new(&self.path).exists() { diff --git a/src/toolchain/mod.rs b/src/toolchain/mod.rs index fc1e004..fd25b64 100644 --- a/src/toolchain/mod.rs +++ b/src/toolchain/mod.rs @@ -1,8 +1,8 @@ -use crate::emoji; -use anyhow::{bail, Result}; +use crate::{emoji, error::Error}; use dirs::home_dir; use flate2::bufread::GzDecoder; use log::info; +use miette::Result; use std::{ fs::{create_dir_all, File}, io::{copy, BufReader}, @@ -27,7 +27,7 @@ pub fn download_file( file_name: &str, output_directory: &str, uncompress: bool, -) -> Result { +) -> Result { let file_path = format!("{}/{}", output_directory, file_name); if Path::new(&file_path).exists() { info!("{} Using cached file: '{}'", emoji::INFO, file_path); @@ -39,11 +39,7 @@ pub fn download_file( output_directory ); if let Err(_e) = create_dir_all(output_directory) { - bail!( - "{} Creating directory '{}' failed", - emoji::ERROR, - output_directory - ); + return Err(Error::FailedToCreateDirectory(output_directory.to_string())); } } info!( @@ -86,11 +82,7 @@ pub fn download_file( archive.unpack(output_directory).unwrap(); } _ => { - bail!( - "{} Unsuported file extension: '{}'", - emoji::ERROR, - extension - ); + return Err(Error::UnsuportedFileExtension(extension.to_string())); } } } else { diff --git a/src/toolchain/rust.rs b/src/toolchain/rust.rs index cb6a3ff..dbe9884 100644 --- a/src/toolchain/rust.rs +++ b/src/toolchain/rust.rs @@ -2,12 +2,13 @@ use crate::{ emoji, + error::Error, host_triple::HostTriple, toolchain::{download_file, espidf::get_dist_path, get_home_dir}, }; -use anyhow::{bail, Result}; use embuild::cmd; use log::{debug, info, warn}; +use miette::Result; use regex::Regex; use reqwest::header; use serde::{Deserialize, Serialize}; @@ -48,7 +49,7 @@ pub struct XtensaRust { impl XtensaRust { /// Get the latest version of Xtensa Rust toolchain. - pub fn get_latest_version() -> Result { + pub fn get_latest_version() -> Result { let mut headers = header::HeaderMap::new(); headers.insert("Accept", "application/vnd.github.v3+json".parse().unwrap()); @@ -62,7 +63,8 @@ impl XtensaRust { .headers(headers) .send()? .text()?; - let json: serde_json::Value = serde_json::from_str(&res)?; + let json: serde_json::Value = + serde_json::from_str(&res).map_err(|_| Error::FailedToSerializeJson)?; let mut version = json["tag_name"].to_string(); version.retain(|c| c != 'v' && c != '"'); @@ -72,17 +74,15 @@ impl XtensaRust { } /// Installs the Xtensa Rust toolchain. - pub fn install(&self) -> Result<()> { + pub fn install(&self) -> Result<(), Error> { #[cfg(unix)] let toolchain_path = self.toolchain_destination.clone(); #[cfg(windows)] let toolchain_path = self.toolchain_destination.clone().join("esp"); if toolchain_path.exists() { - bail!( - "{} The previous installation of Rust Toolchain exists in: '{}'. Please, remove the directory before the new installation.", - emoji::WARN, - toolchain_path.display() - ); + return Err(Error::XtensaToolchainAlreadyInstalled( + toolchain_path.display().to_string(), + )); } info!( "{} Installing Xtensa Rust {} toolchain", @@ -178,20 +178,17 @@ impl XtensaRust { } /// Parses the version of the Xtensa toolchain. - pub fn parse_version(arg: &str) -> Result { + pub fn parse_version(arg: &str) -> Result { debug!("{} Parsing Xtensa Rust version: {}", emoji::DEBUG, arg); let re = Regex::new(RE_TOOLCHAIN_VERSION).unwrap(); if !re.is_match(arg) { - bail!( - "{} Invalid toolchain version, must be in the form of '...'", - emoji::ERROR - ); + return Err(Error::InvalidXtensaToolchanVersion(arg.to_string())); } Ok(arg.to_string()) } /// Removes the Xtensa Rust toolchain. - pub fn uninstall(&self) -> Result<()> { + pub fn uninstall(&self) -> Result<(), Error> { info!("{} Uninstalling Xtensa Rust toolchain", emoji::WRENCH); remove_dir_all(&self.toolchain_destination)?; Ok(()) @@ -206,7 +203,7 @@ pub struct Crate { impl Crate { /// Installs a crate. - pub fn install(&self) -> Result<()> { + pub fn install(&self) -> Result<(), Error> { #[cfg(unix)] let crate_path = format!("{}/bin/{}", get_cargo_home().display(), self.name); #[cfg(windows)] @@ -233,7 +230,7 @@ impl Crate { } } -pub fn install_extra_crates(crates: &HashSet) -> Result<()> { +pub fn install_extra_crates(crates: &HashSet) -> Result<(), Error> { debug!( "{} Installing the following crates: {:#?}", emoji::DEBUG, @@ -263,8 +260,8 @@ pub fn get_rustup_home() -> PathBuf { } /// Checks if rustup and the proper nightly version are installed. If rustup is not installed, -/// it bails. If nigthly version is not installed, proceed to install it. -pub fn check_rust_installation(nightly_version: &str) -> Result<()> { +/// it returns an error. If nigthly version is not installed, proceed to install it. +pub fn check_rust_installation(nightly_version: &str) -> Result<(), Error> { info!("{} Checking existing Rust installation", emoji::WRENCH); match cmd!("rustup", "toolchain", "list") @@ -284,7 +281,7 @@ pub fn check_rust_installation(nightly_version: &str) -> Result<()> { warn!("{} rustup was not found.", emoji::WARN); install_rustup(nightly_version)?; } else { - bail!("{} Error detecting rustup: {}", emoji::ERROR, e); + return Err(Error::RustupDetectionError(e.to_string())); } } } @@ -293,7 +290,7 @@ pub fn check_rust_installation(nightly_version: &str) -> Result<()> { } /// Installs rustup -fn install_rustup(nightly_version: &str) -> Result<()> { +fn install_rustup(nightly_version: &str) -> Result<(), Error> { #[cfg(windows)] let rustup_init_path = download_file( "https://win.rustup.rs/x86_64".to_string(), @@ -359,7 +356,7 @@ fn install_rustup(nightly_version: &str) -> Result<()> { } /// Installs the RiscV target. -pub fn install_riscv_target(nightly_version: &str) -> Result<()> { +pub fn install_riscv_target(nightly_version: &str) -> Result<(), Error> { info!("{} Installing Riscv target", emoji::WRENCH); cmd!( "rustup", @@ -383,7 +380,7 @@ pub fn install_riscv_target(nightly_version: &str) -> Result<()> { } /// Installs the desired version of the nightly toolchain. -fn install_rust_nightly(version: &str) -> Result<()> { +fn install_rust_nightly(version: &str) -> Result<(), Error> { info!("{} Installing {} toolchain", emoji::WRENCH, version); cmd!( "rustup",