diff --git a/Cargo.lock b/Cargo.lock index 92349a2..8ebf515 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1525,9 +1525,9 @@ checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" [[package]] name = "slab" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" [[package]] name = "smallvec" @@ -1575,7 +1575,7 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "suiup" -version = "0.0.5" +version = "0.0.6" dependencies = [ "anyhow", "assert_cmd", diff --git a/Cargo.toml b/Cargo.toml index 21fe7c9..279246b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,8 +1,8 @@ [package] name = "suiup" description = "Sui Tooling Version Manager." -version = "0.0.5" -edition = "2021" +version = "0.0.6" +edition = "2024" [dependencies] anyhow = "1.0.98" diff --git a/src/commands/default/set.rs b/src/commands/default/set.rs index 8dfdc3a..3ca179e 100644 --- a/src/commands/default/set.rs +++ b/src/commands/default/set.rs @@ -1,12 +1,12 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use anyhow::{anyhow, bail, Result}; +use anyhow::{Result, anyhow, bail}; use clap::Args; use tracing::{debug, info}; use crate::{ - commands::{parse_component_with_version, BinaryName, CommandMetadata}, + commands::{BinaryName, CommandMetadata, parse_component_with_version}, handlers::{installed_binaries_grouped_by_network, update_default_version_file}, paths::{binaries_dir, get_default_bin_dir}, }; @@ -42,7 +42,9 @@ impl Command { } = self; if name.is_empty() && nightly.is_none() { - bail!("Invalid number of arguments. Version is required: 'sui@testnet-1.39.3', 'sui@testnet' -- this will use an installed binary that has the highest testnet version. \n For `mvr` only pass the version: `mvr@0.0.5`") + bail!( + "Invalid number of arguments. Version is required: 'sui@testnet-1.39.3', 'sui@testnet' -- this will use an installed binary that has the highest testnet version. \n For `mvr` only pass the version: `mvr@0.0.5`" + ) } let CommandMetadata { @@ -52,12 +54,12 @@ impl Command { } = parse_component_with_version(name)?; let network = if name == BinaryName::Mvr { - if let Some(ref nightly) = nightly { + if let Some(nightly) = nightly { nightly } else { "standalone" } - } else if let Some(ref nightly) = nightly { + } else if let Some(nightly) = nightly { nightly } else { &network @@ -74,7 +76,9 @@ impl Command { .values() .any(|bins| bins.iter().any(|x| x.binary_name == name.to_string())); if !binary_exists { - bail!("Binary {name} not found in installed binaries. Use `suiup show` to see installed binaries."); + bail!( + "Binary {name} not found in installed binaries. Use `suiup show` to see installed binaries." + ); } let version = if let Some(version) = version { diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 908619c..d8cce6d 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -15,7 +15,7 @@ mod which; use crate::{handlers::self_::check_for_updates, types::BinaryVersion}; -use anyhow::{anyhow, bail, Result}; +use anyhow::{Result, anyhow, bail}; use clap::{Parser, Subcommand, ValueEnum}; use comfy_table::Table; pub const TABLE_FORMAT: &str = " ── ══ ── "; diff --git a/src/component/doctor.rs b/src/component/doctor.rs index 3912522..8ddd5c3 100644 --- a/src/component/doctor.rs +++ b/src/component/doctor.rs @@ -95,16 +95,23 @@ fn check_path_variables(check: &mut impl FnMut(&str, Result)) { // Check PATH order let cargo_bin_dir = dirs::home_dir().map(|p| p.join(".cargo/bin")); - if let Some(cargo_bin) = cargo_bin_dir { - if paths.contains(&cargo_bin) { - let suiup_pos = paths.iter().position(|p| p == &default_bin_dir); - let cargo_pos = paths.iter().position(|p| p == &cargo_bin); - if let (Some(s_pos), Some(c_pos)) = (suiup_pos, cargo_pos) { - if s_pos > c_pos { - check("PATH order", Err(format!("WARN: Default binary directory ({}) is after cargo's binary directory ({}). This may cause conflicts if you have also installed sui via `cargo install`.", default_bin_dir.display(), cargo_bin.display()))); - } else { - check("PATH order", Ok("is correct".to_string())); - } + if let Some(cargo_bin) = cargo_bin_dir + && paths.contains(&cargo_bin) + { + let suiup_pos = paths.iter().position(|p| p == &default_bin_dir); + let cargo_pos = paths.iter().position(|p| p == &cargo_bin); + if let (Some(s_pos), Some(c_pos)) = (suiup_pos, cargo_pos) { + if s_pos > c_pos { + check( + "PATH order", + Err(format!( + "WARN: Default binary directory ({}) is after cargo's binary directory ({}). This may cause conflicts if you have also installed sui via `cargo install`.", + default_bin_dir.display(), + cargo_bin.display() + )), + ); + } else { + check("PATH order", Ok("is correct".to_string())); } } } @@ -239,6 +246,8 @@ async fn check_network_connectivity(check: &mut impl FnMut(&str, Result Repo::Mvr, _ => { - return Err(anyhow!("Invalid binary name for standalone installation")) + return Err(anyhow!("Invalid binary name for standalone installation")); } }, yes, diff --git a/src/component/mod.rs b/src/component/mod.rs index 1892743..9324f52 100644 --- a/src/component/mod.rs +++ b/src/component/mod.rs @@ -9,7 +9,7 @@ mod remove; use anyhow::Result; use crate::commands::{ - parse_component_with_version, BinaryName, CommandMetadata, ComponentCommands, + BinaryName, CommandMetadata, ComponentCommands, parse_component_with_version, }; /// ComponentManager handles all component-related operations diff --git a/src/component/remove.rs b/src/component/remove.rs index 5c2e51e..0eda71a 100644 --- a/src/component/remove.rs +++ b/src/component/remove.rs @@ -4,7 +4,7 @@ use std::collections::HashSet; use std::path::PathBuf; -use anyhow::{anyhow, Result}; +use anyhow::{Result, anyhow}; use tracing::debug; use crate::commands::BinaryName; @@ -31,11 +31,11 @@ pub async fn remove_component(binary: BinaryName) -> Result<()> { // Verify all binaries exist before removing any for p in &binaries_to_remove { - if let Some(p) = p.path.as_ref() { - if !PathBuf::from(p).exists() { - println!("Binary {p} does not exist. Aborting the command."); - return Ok(()); - } + if let Some(p) = p.path.as_ref() + && !PathBuf::from(p).exists() + { + println!("Binary {p} does not exist. Aborting the command."); + return Ok(()); } } diff --git a/src/fs_utils.rs b/src/fs_utils.rs index 12d796c..28fac41 100644 --- a/src/fs_utils.rs +++ b/src/fs_utils.rs @@ -1,8 +1,8 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use anyhow::{anyhow, Result}; -use serde::{de::DeserializeOwned, Serialize}; +use anyhow::{Result, anyhow}; +use serde::{Serialize, de::DeserializeOwned}; use std::fs::File; use std::io::Write; use std::path::Path; diff --git a/src/handlers/download.rs b/src/handlers/download.rs index d8590a5..c814519 100644 --- a/src/handlers/download.rs +++ b/src/handlers/download.rs @@ -7,13 +7,13 @@ use crate::handlers::release::{ use crate::handlers::version::extract_version_from_release; use crate::types::Repo; use crate::{handlers::release::release_list, paths::release_archive_dir, types::Release}; -use anyhow::{anyhow, bail, Error}; +use anyhow::{Error, anyhow, bail}; use futures_util::StreamExt; use indicatif::{HumanBytes, ProgressBar, ProgressStyle}; use md5::Context; use reqwest::{ - header::{HeaderMap, HeaderValue, USER_AGENT}, Client, + header::{HeaderMap, HeaderValue, USER_AGENT}, }; use std::fs::File; use std::io::Read; @@ -204,10 +204,10 @@ pub async fn download_file( let mut request = client.get(url).header("User-Agent", "suiup"); // Add authorization header if token is provided and the URL is from GitHub - if let Some(token) = github_token { - if url.contains("github.com") { - request = request.header("Authorization", format!("token {}", token)); - } + if let Some(token) = github_token + && url.contains("github.com") + { + request = request.header("Authorization", format!("token {}", token)); } let response = request.send().await?; diff --git a/src/handlers/install.rs b/src/handlers/install.rs index 4addab2..2acd905 100644 --- a/src/handlers/install.rs +++ b/src/handlers/install.rs @@ -12,9 +12,9 @@ use crate::handlers::{extract_component, update_after_install}; use crate::paths::binaries_dir; use crate::standalone; use crate::types::{BinaryVersion, InstalledBinaries, Repo}; +use anyhow::Error; use anyhow::anyhow; use anyhow::bail; -use anyhow::Error; use indicatif::{ProgressBar, ProgressStyle}; use std::time::Duration; @@ -74,7 +74,9 @@ pub async fn install_from_release( let binary_path = binaries_dir().join(network).join(binary_filename); install_binary(name, network.to_string(), &version, debug, binary_path, yes)?; } else { - println!("Binary {name}-{version} already installed. Use `suiup default set` to change the default binary."); + println!( + "Binary {name}-{version} already installed. Use `suiup default set` to change the default binary." + ); } Ok(()) } @@ -195,7 +197,9 @@ pub async fn install_standalone( )?; } else { let version = version.unwrap_or_default(); - println!("Binary {binary_name}-{version} already installed. Use `suiup default set {binary_name} {version}` to set the default version to the specified one."); + println!( + "Binary {binary_name}-{version} already installed. Use `suiup default set {binary_name} {version}` to set the default version to the specified one." + ); } Ok(()) diff --git a/src/handlers/mod.rs b/src/handlers/mod.rs index bf197cd..d171d37 100644 --- a/src/handlers/mod.rs +++ b/src/handlers/mod.rs @@ -3,8 +3,8 @@ use crate::paths::{binaries_dir, get_default_bin_dir, release_archive_dir}; use crate::{paths::default_file_path, types::Version}; -use anyhow::anyhow; use anyhow::Error; +use anyhow::anyhow; use flate2::read::GzDecoder; use std::env; use std::io::Write; diff --git a/src/handlers/release.rs b/src/handlers/release.rs index 4250a0b..01736e3 100644 --- a/src/handlers/release.rs +++ b/src/handlers/release.rs @@ -1,9 +1,9 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 +use anyhow::Error; use anyhow::anyhow; use anyhow::bail; -use anyhow::Error; use reqwest::header::ETAG; use reqwest::header::IF_NONE_MATCH; diff --git a/src/handlers/self_.rs b/src/handlers/self_.rs index 38e3d18..9e47dea 100644 --- a/src/handlers/self_.rs +++ b/src/handlers/self_.rs @@ -4,7 +4,7 @@ use super::download::detect_os_arch; use crate::handlers::download::download_file; -use anyhow::{anyhow, Result}; +use anyhow::{Result, anyhow}; use std::{fmt::Display, process::Command}; use tokio::task; diff --git a/src/handlers/switch.rs b/src/handlers/switch.rs index e028ea4..1d7f918 100644 --- a/src/handlers/switch.rs +++ b/src/handlers/switch.rs @@ -1,7 +1,7 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use anyhow::{anyhow, bail, Result}; +use anyhow::{Result, anyhow, bail}; use tracing::info; use crate::{ diff --git a/src/handlers/update.rs b/src/handlers/update.rs index ca47404..43ebbc5 100644 --- a/src/handlers/update.rs +++ b/src/handlers/update.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use crate::{ - commands::{parse_component_with_version, BinaryName, CommandMetadata, ComponentCommands}, + commands::{BinaryName, CommandMetadata, ComponentCommands, parse_component_with_version}, handle_commands::handle_cmd, types::InstalledBinaries, }; @@ -13,7 +13,7 @@ use crate::{ }, types::Repo, }; -use anyhow::{bail, Error}; +use anyhow::{Error, bail}; /// Handles the `update` command pub async fn handle_update( @@ -38,7 +38,9 @@ pub async fn handle_update( let installed_binaries = InstalledBinaries::new()?; let binaries = installed_binaries.binaries(); if !binaries.iter().any(|x| x.binary_name == name.to_str()) { - bail!("Binary {name} not found in installed binaries. Use `suiup show` to see installed binaries and `suiup install` to install the binary.") + bail!( + "Binary {name} not found in installed binaries. Use `suiup show` to see installed binaries and `suiup install` to install the binary." + ) } let binaries_by_network = installed_binaries_grouped_by_network(Some(installed_binaries))?; diff --git a/src/handlers/version.rs b/src/handlers/version.rs index 9311869..df8273f 100644 --- a/src/handlers/version.rs +++ b/src/handlers/version.rs @@ -1,7 +1,7 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use anyhow::{anyhow, Error}; +use anyhow::{Error, anyhow}; use lazy_static::lazy_static; lazy_static! { diff --git a/src/lib.rs b/src/lib.rs index 710fcd6..041959a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,3 +9,35 @@ pub mod handlers; pub mod paths; pub mod standalone; pub mod types; + +/// Macro to safely wrap `std::env::set_var` calls in an unsafe block. +/// This centralizes the unsafe operation and improves code readability. +/// +/// # Example +/// ``` +/// set_env_var!("XDG_DATA_HOME", "/path/to/data"); +/// ``` +#[macro_export] +macro_rules! set_env_var { + ($key:expr, $value:expr) => { + unsafe { + std::env::set_var($key, $value); + } + }; +} + +/// Macro to safely wrap `std::env::remove_var` calls in an unsafe block. +/// This centralizes the unsafe operation and improves code readability. +/// +/// # Example +/// ``` +/// remove_env_var!("XDG_DATA_HOME", "/path/to/data"); +/// ``` +#[macro_export] +macro_rules! remove_env_var { + ($key:expr) => { + unsafe { + std::env::remove_var($key); + } + }; +} diff --git a/src/paths.rs b/src/paths.rs index 8306091..f103c4d 100644 --- a/src/paths.rs +++ b/src/paths.rs @@ -4,7 +4,7 @@ use anyhow::Error; use std::collections::BTreeMap; use std::env; -use std::fs::{create_dir_all, File}; +use std::fs::{File, create_dir_all}; use std::io::Write; use std::path::PathBuf; diff --git a/src/standalone.rs b/src/standalone.rs index af5d213..0bc26bd 100644 --- a/src/standalone.rs +++ b/src/standalone.rs @@ -7,7 +7,7 @@ use crate::{ paths::binaries_dir, types::Repo, }; -use anyhow::{anyhow, Error}; +use anyhow::{Error, anyhow}; use serde::Deserialize; #[derive(Deserialize, Debug)] @@ -88,7 +88,10 @@ impl StandaloneInstaller { cache_folder.join(format!("{}-{}.exe", self.repo.binary_name(), version)); if standalone_binary_path.exists() { - println!("Binary {}-{version} already installed. Use `suiup default set standalone {version}` to set the default version to the desired one", self.repo.binary_name()); + println!( + "Binary {}-{version} already installed. Use `suiup default set standalone {version}` to set the default version to the desired one", + self.repo.binary_name() + ); return Ok(version); } diff --git a/src/types.rs b/src/types.rs index 468b54b..b15aeea 100644 --- a/src/types.rs +++ b/src/types.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use crate::fs_utils::{read_json_file, write_json_file}; -use anyhow::{anyhow, Error}; +use anyhow::{Error, anyhow}; use std::{ collections::BTreeMap, fmt::{self, Display, Formatter}, diff --git a/tests/commands_test.rs b/tests/commands_test.rs index 474a22f..94c2e7d 100644 --- a/tests/commands_test.rs +++ b/tests/commands_test.rs @@ -6,10 +6,11 @@ mod tests { use anyhow::Result; use std::fs; use std::time::{Duration, SystemTime}; - use suiup::commands::{parse_component_with_version, BinaryName, CommandMetadata}; + use suiup::commands::{BinaryName, CommandMetadata, parse_component_with_version}; use suiup::handlers::cleanup::handle_cleanup; use suiup::handlers::switch::parse_binary_spec; use suiup::paths; + use suiup::set_env_var; use tempfile::TempDir; #[test] @@ -82,17 +83,21 @@ mod tests { let result = parse_binary_spec("sui@"); assert!(result.is_err()); - assert!(result - .unwrap_err() - .to_string() - .contains("Binary name and network/release cannot be empty")); + assert!( + result + .unwrap_err() + .to_string() + .contains("Binary name and network/release cannot be empty") + ); let result = parse_binary_spec("@testnet"); assert!(result.is_err()); - assert!(result - .unwrap_err() - .to_string() - .contains("Binary name and network/release cannot be empty")); + assert!( + result + .unwrap_err() + .to_string() + .contains("Binary name and network/release cannot be empty") + ); let result = parse_binary_spec("sui@testnet@extra"); assert!(result.is_err()); @@ -104,7 +109,7 @@ mod tests { #[tokio::test] async fn test_cleanup_empty_directory() -> Result<()> { let temp_dir = TempDir::new()?; - std::env::set_var("XDG_CACHE_HOME", temp_dir.path()); + set_env_var!("XDG_CACHE_HOME", temp_dir.path()); // Test cleanup on empty directory let result = handle_cleanup(false, 30, true).await; @@ -130,7 +135,9 @@ mod tests { let old_time = SystemTime::now() - Duration::from_secs(60 * 60 * 24 * 40); // 40 days ago filetime::set_file_mtime(&old_file, filetime::FileTime::from_system_time(old_time))?; - std::env::set_var("XDG_CACHE_HOME", temp_dir.path()); + unsafe { + std::env::set_var("XDG_CACHE_HOME", temp_dir.path()); + } // Dry run should not remove files let result = handle_cleanup(false, 30, true).await; @@ -145,7 +152,9 @@ mod tests { async fn test_cleanup_remove_old_files() -> Result<()> { let temp_dir = TempDir::new()?; // Set up environment variable for cache directory - std::env::set_var("XDG_CACHE_HOME", temp_dir.path()); + unsafe { + std::env::set_var("XDG_CACHE_HOME", temp_dir.path()); + } // Create cache directory let cache_dir = paths::release_archive_dir(); fs::create_dir_all(&cache_dir)?; @@ -161,7 +170,9 @@ mod tests { let old_time = SystemTime::now() - Duration::from_secs(60 * 60 * 24 * 40); // 40 days ago filetime::set_file_mtime(&old_file, filetime::FileTime::from_system_time(old_time))?; - std::env::set_var("XDG_CACHE_HOME", temp_dir.path()); + unsafe { + std::env::set_var("XDG_CACHE_HOME", temp_dir.path()); + } // Actual cleanup should remove old file but keep new file let result = handle_cleanup(false, 30, false).await; @@ -176,7 +187,9 @@ mod tests { async fn test_cleanup_remove_all() -> Result<()> { let temp_dir = TempDir::new()?; // Set up environment variable for cache directory - std::env::set_var("XDG_CACHE_HOME", temp_dir.path()); + unsafe { + std::env::set_var("XDG_CACHE_HOME", temp_dir.path()); + } // Create cache directory let cache_dir = paths::release_archive_dir(); fs::create_dir_all(&cache_dir)?; @@ -188,7 +201,9 @@ mod tests { fs::write(&file1, b"content1")?; fs::write(&file2, b"content2")?; - std::env::set_var("XDG_CACHE_HOME", temp_dir.path()); + unsafe { + std::env::set_var("XDG_CACHE_HOME", temp_dir.path()); + } // Remove all should clear everything let result = handle_cleanup(true, 30, false).await; diff --git a/tests/test_utils.rs b/tests/test_utils.rs index 4475bd0..e88e181 100644 --- a/tests/test_utils.rs +++ b/tests/test_utils.rs @@ -8,6 +8,7 @@ use std::{env, sync::Mutex}; use suiup::paths::{ get_cache_home, get_config_home, get_data_home, get_default_bin_dir, initialize, }; +use suiup::set_env_var; use tempfile::TempDir; #[derive(Debug)] @@ -88,13 +89,13 @@ impl TestEnv { // Set test env vars #[cfg(windows)] - env::set_var("LOCALAPPDATA", &data_dir); // it is the same for data and config + set_env_var!("LOCALAPPDATA", &data_dir); // it is the same for data and config #[cfg(not(windows))] - env::set_var("XDG_DATA_HOME", &data_dir); + set_env_var!("XDG_DATA_HOME", &data_dir); #[cfg(not(windows))] - env::set_var("XDG_CONFIG_HOME", &config_dir); + set_env_var!("XDG_CONFIG_HOME", &config_dir); #[cfg(not(windows))] - env::set_var("XDG_CACHE_HOME", &cache_dir); + set_env_var!("XDG_CACHE_HOME", &cache_dir); // Add bin dir to PATH let path = env::var("PATH").unwrap_or_default(); @@ -102,7 +103,7 @@ impl TestEnv { let new_path = format!("{};{}", bin_dir.display(), path); #[cfg(not(windows))] let new_path = format!("{}:{}", bin_dir.display(), path); - env::set_var("PATH", new_path); + set_env_var!("PATH", new_path); Ok(Self { temp_dir, @@ -171,7 +172,7 @@ impl Drop for TestEnv { fn drop(&mut self) { // Restore original env vars for (var, val) in &self.original_env { - env::set_var(var, val); + set_env_var!(var, val); } } }