Merge pull request #60 from SergioGasquez/fixes/errors

Improve errors
This commit is contained in:
Sergio Gasquez Arcos 2022-11-15 16:52:35 +01:00 committed by GitHub
commit 1b605ca442
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 212 additions and 142 deletions

1
Cargo.lock generated
View File

@ -453,6 +453,7 @@ dependencies = [
"strum_macros", "strum_macros",
"tar", "tar",
"tempfile", "tempfile",
"thiserror",
"toml", "toml",
"update-informer", "update-informer",
"xz2", "xz2",

View File

@ -36,6 +36,7 @@ serde = { version = "1.0.146", features = ["derive"] }
miette = "5.4.1" miette = "5.4.1"
regex = "1.7.0" regex = "1.7.0"
serde_json = "1.0.87" serde_json = "1.0.87"
thiserror = "1.0.37"
update-informer = "0.5.0" update-informer = "0.5.0"
[target.aarch64-unknown-linux-gnu.dependencies] [target.aarch64-unknown-linux-gnu.dependencies]

View File

@ -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 directories_next::ProjectDirs;
use miette::{ErrReport, IntoDiagnostic, Result, WrapErr}; use miette::{IntoDiagnostic, Result};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{ use std::{
collections::HashSet, collections::HashSet,
@ -31,31 +31,34 @@ pub struct Config {
impl Config { impl Config {
/// Load the config from config file /// Load the config from config file
pub fn load() -> Result<Self> { pub fn load() -> Result<Self, Error> {
let dirs = ProjectDirs::from("rs", "esp", "espup").unwrap(); let dirs = ProjectDirs::from("rs", "esp", "espup").unwrap();
let file = dirs.config_dir().join("espup.toml"); let file = dirs.config_dir().join("espup.toml");
let config = if let Ok(data) = read(file) { let config = if let Ok(data) = read(&file) {
toml::from_slice(&data).into_diagnostic()? toml::from_slice(&data)
.into_diagnostic()
.map_err(|_| Error::FailedToDeserialize)?
} else { } else {
return Err(ErrReport::msg("No config file found")); return Err(Error::FileNotFound(file.to_string_lossy().into_owned()));
}; };
Ok(config) Ok(config)
} }
/// Save the config to file /// 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 dirs = ProjectDirs::from("rs", "esp", "espup").unwrap();
let file = dirs.config_dir().join("espup.toml"); let file = dirs.config_dir().join("espup.toml");
let serialized = toml::to_string(&self.clone()) let serialized = toml::to_string(&self.clone())
.into_diagnostic() .into_diagnostic()
.wrap_err("Failed to serialize config")?; .map_err(|_| Error::FailedToSerialize)?;
create_dir_all(file.parent().unwrap()) create_dir_all(file.parent().unwrap())
.into_diagnostic() .into_diagnostic()
.wrap_err("Failed to create config directory")?; .map_err(|e| Error::FailedToCreateConfigFile(e.to_string()))?;
write(&file, serialized) write(&file, serialized)
.into_diagnostic() .into_diagnostic()
.wrap_err_with(|| format!("Failed to write config to {}", file.display())) .map_err(|_| Error::FailedToWrite(file.display().to_string()))?;
Ok(())
} }
} }

81
src/error.rs Normal file
View File

@ -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 '<major>.<minor>.<patch>.<subpatch>'",
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),
}

View File

@ -1,5 +1,4 @@
use crate::emoji; use crate::error::Error;
use anyhow::{Context, Result};
use guess_host_triple::guess_host_triple; use guess_host_triple::guess_host_triple;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::str::FromStr; use std::str::FromStr;
@ -30,17 +29,14 @@ pub enum HostTriple {
} }
/// Parse the host triple if specified, otherwise guess it. /// Parse the host triple if specified, otherwise guess it.
pub fn get_host_triple(host_triple_arg: Option<String>) -> Result<HostTriple> { pub fn get_host_triple(host_triple_arg: Option<String>) -> Result<HostTriple, Error> {
if let Some(host_triple_arg) = host_triple_arg { let host_triple = if let Some(host_triple) = &host_triple_arg {
HostTriple::from_str(&host_triple_arg).context(format!( host_triple
"{} Host triple '{}' is not supported.",
emoji::ERROR,
host_triple_arg,
))
} else { } else {
HostTriple::from_str(guess_host_triple().unwrap()) guess_host_triple().unwrap()
.context(format!("{} Unable to guess host triple.", emoji::ERROR,)) };
}
HostTriple::from_str(host_triple).map_err(|_| Error::UnsupportedHostTriple(host_triple.into()))
} }
#[cfg(test)] #[cfg(test)]

View File

@ -1,5 +1,6 @@
pub mod config; pub mod config;
pub mod emoji; pub mod emoji;
pub mod error;
pub mod host_triple; pub mod host_triple;
pub mod targets; pub mod targets;
pub mod toolchain; pub mod toolchain;

View File

@ -1,4 +1,3 @@
use anyhow::{bail, Result};
use clap::Parser; use clap::Parser;
use directories_next::ProjectDirs; use directories_next::ProjectDirs;
use dirs::home_dir; use dirs::home_dir;
@ -9,6 +8,7 @@ use embuild::{
use espup::{ use espup::{
config::Config, config::Config,
emoji, emoji,
error::Error,
host_triple::get_host_triple, host_triple::get_host_triple,
logging::initialize_logger, logging::initialize_logger,
targets::{parse_targets, Target}, targets::{parse_targets, Target},
@ -25,6 +25,7 @@ use espup::{
update::check_for_update, update::check_for_update,
}; };
use log::{debug, info, warn}; use log::{debug, info, warn};
use miette::Result;
use std::{ use std::{
collections::HashSet, collections::HashSet,
fs::{remove_dir_all, remove_file, File}, fs::{remove_dir_all, remove_file, File},
@ -128,7 +129,7 @@ pub struct UninstallOpts {
} }
/// Installs the Rust for ESP chips environment /// Installs the Rust for ESP chips environment
fn install(args: InstallOpts) -> Result<()> { fn install(args: InstallOpts) -> Result<(), Error> {
initialize_logger(&args.log_level); initialize_logger(&args.log_level);
check_for_update(env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION")); check_for_update(env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"));
info!("{} Installing esp-rs", emoji::DISC); info!("{} Installing esp-rs", emoji::DISC);
@ -233,9 +234,7 @@ fn install(args: InstallOpts) -> Result<()> {
targets, targets,
xtensa_rust, xtensa_rust,
}; };
if let Err(e) = config.save() { config.save()?;
bail!("{} Failed to save config {:#}", emoji::ERROR, e);
}
info!("{} Installation successfully completed!", emoji::CHECK); info!("{} Installation successfully completed!", emoji::CHECK);
warn!( warn!(
@ -246,7 +245,7 @@ fn install(args: InstallOpts) -> Result<()> {
} }
/// Uninstalls the Rust for ESP chips environment /// Uninstalls the Rust for ESP chips environment
fn uninstall(args: UninstallOpts) -> Result<()> { fn uninstall(args: UninstallOpts) -> Result<(), Error> {
initialize_logger(&args.log_level); initialize_logger(&args.log_level);
check_for_update(env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION")); check_for_update(env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"));
@ -304,7 +303,7 @@ fn uninstall(args: UninstallOpts) -> Result<()> {
} }
/// Updates Xtensa Rust toolchain. /// Updates Xtensa Rust toolchain.
fn update(args: UpdateOpts) -> Result<()> { fn update(args: UpdateOpts) -> Result<(), Error> {
initialize_logger(&args.log_level); initialize_logger(&args.log_level);
check_for_update(env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION")); 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); config.xtensa_rust = Some(xtensa_rust);
} }
if let Err(e) = config.save() { config.save()?;
bail!("{} Failed to save config {:#}", emoji::ERROR, e);
}
info!("{} Update successfully completed!", emoji::CHECK); info!("{} Update successfully completed!", emoji::CHECK);
Ok(()) Ok(())
} }
fn main() -> Result<()> { fn main() -> Result<(), Error> {
match Cli::parse().subcommand { match Cli::parse().subcommand {
SubCommand::Install(args) => install(*args), SubCommand::Install(args) => install(*args),
SubCommand::Update(args) => update(args), SubCommand::Update(args) => update(args),
@ -360,7 +357,7 @@ fn main() -> Result<()> {
} }
/// Deletes dist folder. /// Deletes dist folder.
fn clear_dist_folder() -> Result<()> { fn clear_dist_folder() -> Result<(), Error> {
let dist_path = PathBuf::from(get_dist_path("")); let dist_path = PathBuf::from(get_dist_path(""));
if dist_path.exists() { if dist_path.exists() {
info!("{} Clearing dist folder", emoji::WRENCH); 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. /// 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<PathBuf>) -> Result<PathBuf> { fn get_export_file(export_file: Option<PathBuf>) -> Result<PathBuf, Error> {
if let Some(export_file) = export_file { if let Some(export_file) = export_file {
if export_file.is_absolute() { if export_file.is_absolute() {
Ok(export_file) Ok(export_file)
@ -385,7 +382,7 @@ fn get_export_file(export_file: Option<PathBuf>) -> Result<PathBuf> {
} }
/// Creates the export file with the necessary environment variables. /// 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); info!("{} Creating export file", emoji::WRENCH);
let mut file = File::create(export_file)?; let mut file = File::create(export_file)?;
for e in exports.iter() { for e in exports.iter() {
@ -413,17 +410,18 @@ fn export_environment(export_file: &PathBuf, exports: &[String]) -> Result<()> {
#[cfg(windows)] #[cfg(windows)]
/// For Windows, we need to check that we are installing all the targets if we are installing esp-idf. /// 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<Target>, esp_idf_version: &Option<String>) -> Result<()> {
if esp_idf_version.is_some() pub fn check_arguments(
targets: &HashSet<Target>,
espidf_version: &Option<String>,
) -> Result<(), Error> {
if espidf_version.is_some()
&& (!targets.contains(&Target::ESP32) && (!targets.contains(&Target::ESP32)
|| !targets.contains(&Target::ESP32C3) || !targets.contains(&Target::ESP32C3)
|| !targets.contains(&Target::ESP32S2) || !targets.contains(&Target::ESP32S2)
|| !targets.contains(&Target::ESP32S3)) || !targets.contains(&Target::ESP32S3))
{ {
bail!( return Err(Error::WrongWindowsArguments);
"{} When installing esp-idf in Windows, only --targets \"all\" is supported.",
emoji::ERROR
);
} }
Ok(()) Ok(())

View File

@ -1,7 +1,6 @@
//! ESP32 chip variants support. //! ESP32 chip variants support.
use crate::emoji; use crate::{emoji, error::Error};
use anyhow::Context;
use log::debug; use log::debug;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{collections::HashSet, str::FromStr}; 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. /// Returns a vector of Chips from a comma or space separated string.
pub fn parse_targets(targets_str: &str) -> Result<HashSet<Target>, String> { pub fn parse_targets(targets_str: &str) -> Result<HashSet<Target>, Error> {
debug!("{} Parsing targets: {}", emoji::DEBUG, targets_str); debug!("{} Parsing targets: {}", emoji::DEBUG, targets_str);
let targets_str = targets_str.to_lowercase(); let targets_str = targets_str.to_lowercase();
@ -35,18 +34,14 @@ pub fn parse_targets(targets_str: &str) -> Result<HashSet<Target>, String> {
let targets: HashSet<Target> = if targets_str.contains("all") { let targets: HashSet<Target> = if targets_str.contains("all") {
Target::iter().collect() Target::iter().collect()
} else { } else {
targets_str let mut targets = HashSet::new();
.split([',', ' ']) for target in targets_str.split([',', ' ']) {
.map(|target| { targets.insert(
Target::from_str(target) Target::from_str(target).map_err(|_| Error::UnsupportedTarget(target.into()))?,
.context(format!( );
"{} Target '{}' is not supported", }
emoji::ERROR,
target targets
))
.unwrap()
})
.collect()
}; };
debug!("{} Parsed targets: {:?}", emoji::DEBUG, targets); debug!("{} Parsed targets: {:?}", emoji::DEBUG, targets);
@ -56,38 +51,33 @@ pub fn parse_targets(targets_str: &str) -> Result<HashSet<Target>, String> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::targets::{parse_targets, Target}; use crate::targets::{parse_targets, Target};
use std::collections::HashSet;
#[test] #[test]
#[allow(unused_variables)]
fn test_parse_targets() { fn test_parse_targets() {
assert_eq!( let targets: HashSet<Target> = [Target::ESP32].into_iter().collect();
parse_targets("esp32"), assert!(matches!(parse_targets("esp32"), Ok(targets)));
Ok([Target::ESP32].into_iter().collect()) let targets: HashSet<Target> = [Target::ESP32, Target::ESP32S2].into_iter().collect();
); assert!(matches!(parse_targets("esp32,esp32s2"), Ok(targets)));
assert_eq!( let targets: HashSet<Target> = [Target::ESP32S3, Target::ESP32].into_iter().collect();
parse_targets("esp32,esp32s2"), assert!(matches!(parse_targets("esp32s3 esp32"), Ok(targets)));
Ok([Target::ESP32, Target::ESP32S2].into_iter().collect()) let targets: HashSet<Target> = [Target::ESP32S3, Target::ESP32, Target::ESP32C3]
);
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,
]
.into_iter() .into_iter()
.collect()) .collect();
); assert!(matches!(
parse_targets("esp32s3,esp32,esp32c3"),
Ok(targets)
));
let targets: HashSet<Target> = [
Target::ESP32,
Target::ESP32S2,
Target::ESP32S3,
Target::ESP32C2,
Target::ESP32C3,
]
.into_iter()
.collect();
assert!(matches!(parse_targets("all"), Ok(targets)));
} }
} }

View File

@ -2,18 +2,19 @@
use crate::{ use crate::{
emoji, emoji,
error::Error,
targets::Target, targets::Target,
toolchain::{ toolchain::{
gcc::{get_toolchain_name, get_ulp_toolchain_name}, gcc::{get_toolchain_name, get_ulp_toolchain_name},
get_home_dir, get_home_dir,
}, },
}; };
use anyhow::{Context, Result};
use embuild::{espidf, espidf::EspIdfRemote, git}; use embuild::{espidf, espidf::EspIdfRemote, git};
use log::{debug, info}; use log::{debug, info};
use std::collections::HashSet; use miette::Result;
use std::{ use std::{
collections::hash_map::DefaultHasher, collections::hash_map::DefaultHasher,
collections::HashSet,
env, env,
fs::remove_dir_all, fs::remove_dir_all,
hash::{Hash, Hasher}, hash::{Hash, Hasher},
@ -64,18 +65,22 @@ pub struct EspIdfRepo {
impl EspIdfRepo { impl EspIdfRepo {
/// Installs esp-idf. /// Installs esp-idf.
pub fn install(self) -> Result<Vec<String>> { pub fn install(self) -> Result<Vec<String>, Error> {
let cmake_generator = DEFAULT_CMAKE_GENERATOR; let cmake_generator = DEFAULT_CMAKE_GENERATOR;
let mut exports: Vec<String> = Vec::new(); let mut exports: Vec<String> = Vec::new();
// A closure to specify which tools `idf-tools.py` should install. // A closure to specify which tools `idf-tools.py` should install.
let make_tools = move |repo: &git::Repository, let make_tools = move |repo: &git::Repository,
version: &Result<espidf::EspIdfVersion>| version: &anyhow::Result<espidf::EspIdfVersion>|
-> Result<Vec<espidf::Tools>> { -> anyhow::Result<Vec<espidf::Tools>> {
let version_str = match version {
Ok(v) => format!("v{v}"),
Err(_) => "(unknown version)".to_string(),
};
info!( info!(
"{} Using esp-idf {} at '{}'", "{} Using esp-idf {} at '{}'",
emoji::INFO, emoji::INFO,
espidf::EspIdfVersion::format(version), version_str,
repo.worktree().display() repo.worktree().display()
); );
@ -99,7 +104,8 @@ impl EspIdfRepo {
subtools.push("cmake".to_string()) subtools.push("cmake".to_string())
} }
_ => { _ => {
tools.push(espidf::Tools::cmake()?); tools
.push(espidf::Tools::cmake().map_err(|_| Error::FailedToInstantiateCmake)?);
} }
} }
#[cfg(windows)] #[cfg(windows)]
@ -119,13 +125,15 @@ impl EspIdfRepo {
Ok(tools) Ok(tools)
}; };
let install = |esp_idf_origin: espidf::EspIdfOrigin| -> Result<espidf::EspIdf> {
espidf::Installer::new(esp_idf_origin) let install =
.install_dir(Some(self.install_path.clone())) |esp_idf_origin: espidf::EspIdfOrigin| -> anyhow::Result<espidf::EspIdf, Error> {
.with_tools(make_tools) espidf::Installer::new(esp_idf_origin)
.install() .install_dir(Some(self.install_path.clone()))
.context("Could not install esp-idf") .with_tools(make_tools)
}; .install()
.map_err(|_| Error::FailedToCreateEspIdfInstallClosure)
};
let repo = espidf::EspIdfRemote { let repo = espidf::EspIdfRemote {
git_ref: espidf::parse_esp_idf_git_ref(&self.version), git_ref: espidf::parse_esp_idf_git_ref(&self.version),
@ -134,7 +142,7 @@ impl EspIdfRepo {
let espidf_origin = espidf::EspIdfOrigin::Managed(repo.clone()); let espidf_origin = espidf::EspIdfOrigin::Managed(repo.clone());
#[cfg(unix)] #[cfg(unix)]
let espidf = install(espidf_origin)?; let espidf = install(espidf_origin).map_err(|_| Error::FailedToInstallEspIdf)?;
#[cfg(windows)] #[cfg(windows)]
install(espidf_origin)?; install(espidf_origin)?;
let espidf_dir = get_install_path(repo); let espidf_dir = get_install_path(repo);

View File

@ -2,13 +2,14 @@
use crate::{ use crate::{
emoji, emoji,
error::Error,
host_triple::HostTriple, host_triple::HostTriple,
targets::Target, targets::Target,
toolchain::{download_file, espidf::get_tool_path}, toolchain::{download_file, espidf::get_tool_path},
}; };
use anyhow::Result;
use embuild::espidf::EspIdfVersion; use embuild::espidf::EspIdfVersion;
use log::{debug, info, warn}; use log::{debug, info, warn};
use miette::Result;
use std::{ use std::{
collections::HashSet, collections::HashSet,
path::{Path, PathBuf}, path::{Path, PathBuf},
@ -43,7 +44,7 @@ impl Gcc {
} }
/// Installs the gcc toolchain. /// 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 target_dir = format!("{}/{}-{}", self.toolchain_name, self.release, self.version);
let gcc_path = get_tool_path(&target_dir); let gcc_path = get_tool_path(&target_dir);
let extension = get_artifact_extension(&self.host_triple); 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( pub fn install_gcc_targets(
targets: &HashSet<Target>, targets: &HashSet<Target>,
host_triple: &HostTriple, host_triple: &HostTriple,
) -> Result<Vec<String>> { ) -> Result<Vec<String>, Error> {
info!("{} Installing gcc for build targets", emoji::WRENCH); info!("{} Installing gcc for build targets", emoji::WRENCH);
let mut exports: Vec<String> = Vec::new(); let mut exports: Vec<String> = Vec::new();
for target in targets { for target in targets {

View File

@ -2,11 +2,12 @@
use crate::{ use crate::{
emoji, emoji,
error::Error,
host_triple::HostTriple, host_triple::HostTriple,
toolchain::{download_file, espidf::get_tool_path}, toolchain::{download_file, espidf::get_tool_path},
}; };
use anyhow::{Ok, Result};
use log::{info, warn}; use log::{info, warn};
use miette::Result;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
const DEFAULT_LLVM_REPOSITORY: &str = "https://github.com/espressif/llvm-project/releases/download"; const DEFAULT_LLVM_REPOSITORY: &str = "https://github.com/espressif/llvm-project/releases/download";
@ -48,7 +49,7 @@ impl Llvm {
} }
/// Installs the LLVM toolchain. /// Installs the LLVM toolchain.
pub fn install(&self) -> Result<Vec<String>> { pub fn install(&self) -> Result<Vec<String>, Error> {
let mut exports: Vec<String> = Vec::new(); let mut exports: Vec<String> = Vec::new();
if Path::new(&self.path).exists() { if Path::new(&self.path).exists() {

View File

@ -1,8 +1,8 @@
use crate::emoji; use crate::{emoji, error::Error};
use anyhow::{bail, Result};
use dirs::home_dir; use dirs::home_dir;
use flate2::bufread::GzDecoder; use flate2::bufread::GzDecoder;
use log::info; use log::info;
use miette::Result;
use std::{ use std::{
fs::{create_dir_all, File}, fs::{create_dir_all, File},
io::{copy, BufReader}, io::{copy, BufReader},
@ -27,7 +27,7 @@ pub fn download_file(
file_name: &str, file_name: &str,
output_directory: &str, output_directory: &str,
uncompress: bool, uncompress: bool,
) -> Result<String> { ) -> Result<String, Error> {
let file_path = format!("{}/{}", output_directory, file_name); let file_path = format!("{}/{}", output_directory, file_name);
if Path::new(&file_path).exists() { if Path::new(&file_path).exists() {
info!("{} Using cached file: '{}'", emoji::INFO, file_path); info!("{} Using cached file: '{}'", emoji::INFO, file_path);
@ -39,11 +39,7 @@ pub fn download_file(
output_directory output_directory
); );
if let Err(_e) = create_dir_all(output_directory) { if let Err(_e) = create_dir_all(output_directory) {
bail!( return Err(Error::FailedToCreateDirectory(output_directory.to_string()));
"{} Creating directory '{}' failed",
emoji::ERROR,
output_directory
);
} }
} }
info!( info!(
@ -86,11 +82,7 @@ pub fn download_file(
archive.unpack(output_directory).unwrap(); archive.unpack(output_directory).unwrap();
} }
_ => { _ => {
bail!( return Err(Error::UnsuportedFileExtension(extension.to_string()));
"{} Unsuported file extension: '{}'",
emoji::ERROR,
extension
);
} }
} }
} else { } else {

View File

@ -2,12 +2,13 @@
use crate::{ use crate::{
emoji, emoji,
error::Error,
host_triple::HostTriple, host_triple::HostTriple,
toolchain::{download_file, espidf::get_dist_path, get_home_dir}, toolchain::{download_file, espidf::get_dist_path, get_home_dir},
}; };
use anyhow::{bail, Result};
use embuild::cmd; use embuild::cmd;
use log::{debug, info, warn}; use log::{debug, info, warn};
use miette::Result;
use regex::Regex; use regex::Regex;
use reqwest::header; use reqwest::header;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -48,7 +49,7 @@ pub struct XtensaRust {
impl XtensaRust { impl XtensaRust {
/// Get the latest version of Xtensa Rust toolchain. /// Get the latest version of Xtensa Rust toolchain.
pub fn get_latest_version() -> Result<String> { pub fn get_latest_version() -> Result<String, Error> {
let mut headers = header::HeaderMap::new(); let mut headers = header::HeaderMap::new();
headers.insert("Accept", "application/vnd.github.v3+json".parse().unwrap()); headers.insert("Accept", "application/vnd.github.v3+json".parse().unwrap());
@ -62,7 +63,8 @@ impl XtensaRust {
.headers(headers) .headers(headers)
.send()? .send()?
.text()?; .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(); let mut version = json["tag_name"].to_string();
version.retain(|c| c != 'v' && c != '"'); version.retain(|c| c != 'v' && c != '"');
@ -72,17 +74,15 @@ impl XtensaRust {
} }
/// Installs the Xtensa Rust toolchain. /// Installs the Xtensa Rust toolchain.
pub fn install(&self) -> Result<()> { pub fn install(&self) -> Result<(), Error> {
#[cfg(unix)] #[cfg(unix)]
let toolchain_path = self.toolchain_destination.clone(); let toolchain_path = self.toolchain_destination.clone();
#[cfg(windows)] #[cfg(windows)]
let toolchain_path = self.toolchain_destination.clone().join("esp"); let toolchain_path = self.toolchain_destination.clone().join("esp");
if toolchain_path.exists() { if toolchain_path.exists() {
bail!( return Err(Error::XtensaToolchainAlreadyInstalled(
"{} The previous installation of Rust Toolchain exists in: '{}'. Please, remove the directory before the new installation.", toolchain_path.display().to_string(),
emoji::WARN, ));
toolchain_path.display()
);
} }
info!( info!(
"{} Installing Xtensa Rust {} toolchain", "{} Installing Xtensa Rust {} toolchain",
@ -178,20 +178,17 @@ impl XtensaRust {
} }
/// Parses the version of the Xtensa toolchain. /// Parses the version of the Xtensa toolchain.
pub fn parse_version(arg: &str) -> Result<String> { pub fn parse_version(arg: &str) -> Result<String, Error> {
debug!("{} Parsing Xtensa Rust version: {}", emoji::DEBUG, arg); debug!("{} Parsing Xtensa Rust version: {}", emoji::DEBUG, arg);
let re = Regex::new(RE_TOOLCHAIN_VERSION).unwrap(); let re = Regex::new(RE_TOOLCHAIN_VERSION).unwrap();
if !re.is_match(arg) { if !re.is_match(arg) {
bail!( return Err(Error::InvalidXtensaToolchanVersion(arg.to_string()));
"{} Invalid toolchain version, must be in the form of '<major>.<minor>.<patch>.<subpatch>'",
emoji::ERROR
);
} }
Ok(arg.to_string()) Ok(arg.to_string())
} }
/// Removes the Xtensa Rust toolchain. /// Removes the Xtensa Rust toolchain.
pub fn uninstall(&self) -> Result<()> { pub fn uninstall(&self) -> Result<(), Error> {
info!("{} Uninstalling Xtensa Rust toolchain", emoji::WRENCH); info!("{} Uninstalling Xtensa Rust toolchain", emoji::WRENCH);
remove_dir_all(&self.toolchain_destination)?; remove_dir_all(&self.toolchain_destination)?;
Ok(()) Ok(())
@ -206,7 +203,7 @@ pub struct Crate {
impl Crate { impl Crate {
/// Installs a crate. /// Installs a crate.
pub fn install(&self) -> Result<()> { pub fn install(&self) -> Result<(), Error> {
#[cfg(unix)] #[cfg(unix)]
let crate_path = format!("{}/bin/{}", get_cargo_home().display(), self.name); let crate_path = format!("{}/bin/{}", get_cargo_home().display(), self.name);
#[cfg(windows)] #[cfg(windows)]
@ -233,7 +230,7 @@ impl Crate {
} }
} }
pub fn install_extra_crates(crates: &HashSet<Crate>) -> Result<()> { pub fn install_extra_crates(crates: &HashSet<Crate>) -> Result<(), Error> {
debug!( debug!(
"{} Installing the following crates: {:#?}", "{} Installing the following crates: {:#?}",
emoji::DEBUG, 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, /// 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. /// it returns an error. If nigthly version is not installed, proceed to install it.
pub fn check_rust_installation(nightly_version: &str) -> Result<()> { pub fn check_rust_installation(nightly_version: &str) -> Result<(), Error> {
info!("{} Checking existing Rust installation", emoji::WRENCH); info!("{} Checking existing Rust installation", emoji::WRENCH);
match cmd!("rustup", "toolchain", "list") 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); warn!("{} rustup was not found.", emoji::WARN);
install_rustup(nightly_version)?; install_rustup(nightly_version)?;
} else { } 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 /// Installs rustup
fn install_rustup(nightly_version: &str) -> Result<()> { fn install_rustup(nightly_version: &str) -> Result<(), Error> {
#[cfg(windows)] #[cfg(windows)]
let rustup_init_path = download_file( let rustup_init_path = download_file(
"https://win.rustup.rs/x86_64".to_string(), "https://win.rustup.rs/x86_64".to_string(),
@ -359,7 +356,7 @@ fn install_rustup(nightly_version: &str) -> Result<()> {
} }
/// Installs the RiscV target. /// 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); info!("{} Installing Riscv target", emoji::WRENCH);
cmd!( cmd!(
"rustup", "rustup",
@ -383,7 +380,7 @@ pub fn install_riscv_target(nightly_version: &str) -> Result<()> {
} }
/// Installs the desired version of the nightly toolchain. /// 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); info!("{} Installing {} toolchain", emoji::WRENCH, version);
cmd!( cmd!(
"rustup", "rustup",