Repository: FuelLabs/fuelup (rust)

Issue:
Remove usage of `target_from_name`
Initially `TargetTriple` wasn't scoped out yet but now that we're reusing `target_from_name` everywhere, we should use the `TargetTriple` struct instead of `Option<String>` as input to `DownloadCfg`.

Pull request:
Refactor usage of `TargetTriple`
Closes #127 

- `target_from_name` was a very vague name. Refactored it to `from_component` and place it under `impl TargetTriple`, since the main purpose of that function was to format the `TargetTriple` string from the appropriate component given (eg. forc binaries = `linux_amd64` versus `fuel-core` = `x86_64-linux-gnu`)

Note that the logic within the refactored fn did not change; only the naming and the usage of it.

Also,

- Made `DownloadCfg` struct accept `TargetTriple` for `target` instead of `String`
- Moved all the code related to `TargetTriple` into its own dedicated file `target_triple.rs`

Patch:
diff --git a/src/commands/default.rs b/src/commands/default.rs
index af0d90ed..6b7f9237 100644
--- a/src/commands/default.rs
+++ b/src/commands/default.rs
@@ -27,7 +27,7 @@ pub fn exec(command: DefaultCommand) -> Result<()> {
     let mut new_default = Toolchain::from(&toolchain)?;
 
     if RESERVED_TOOLCHAIN_NAMES.contains(&toolchain.as_str()) {
-        new_default = Toolchain::new(&toolchain, None)?;
+        new_default = Toolchain::new(&toolchain)?;
     } else if !new_default.path.exists() {
         bail!("Toolchain with name '{}' does not exist", &new_default.name)
     };
diff --git a/src/commands/toolchain.rs b/src/commands/toolchain.rs
index 12067958..4f7bf428 100644
--- a/src/commands/toolchain.rs
+++ b/src/commands/toolchain.rs
@@ -4,7 +4,8 @@ use clap::Parser;
 use crate::ops::fuelup_toolchain::install::install;
 use crate::ops::fuelup_toolchain::new::new;
 use crate::ops::fuelup_toolchain::uninstall::uninstall;
-use crate::toolchain::{TargetTriple, RESERVED_TOOLCHAIN_NAMES};
+use crate::target_triple::TargetTriple;
+use crate::toolchain::RESERVED_TOOLCHAIN_NAMES;
 
 #[derive(Debug, Parser)]
 pub enum ToolchainCommand {
@@ -41,7 +42,7 @@ pub struct UninstallCommand {
 fn name_allowed(s: &str) -> Result<String> {
     let name = match s.split_once('-') {
         Some((prefix, target_triple)) => {
-            if TargetTriple::from_host()? == TargetTriple::new(target_triple) {
+            if TargetTriple::from_host()?.to_string() == target_triple {
                 prefix
             } else {
                 s
diff --git a/src/download.rs b/src/download.rs
index ec265771..8cac6781 100644
--- a/src/download.rs
+++ b/src/download.rs
@@ -21,6 +21,7 @@ use crate::constants::{
 };
 use crate::file::hard_or_symlink_file;
 use crate::path::fuelup_bin;
+use crate::target_triple::TargetTriple;
 
 #[derive(Debug, Serialize, Deserialize)]
 struct LatestReleaseApiResponse {
@@ -32,7 +33,7 @@ struct LatestReleaseApiResponse {
 #[derive(Debug, PartialEq, Eq)]
 pub struct DownloadCfg {
     pub name: String,
-    pub target: String,
+    pub target: TargetTriple,
     pub version: Version,
     tarball_name: String,
     tarball_url: String,
@@ -40,17 +41,13 @@ pub struct DownloadCfg {
 }
 
 impl DownloadCfg {
-    pub fn new(name: &str, target: Option<String>, version: Option<Version>) -> Result<Self> {
+    pub fn new(name: &str, target: TargetTriple, version: Option<Version>) -> Result<Self> {
         let version = match version {
             Some(version) => version,
             None => get_latest_tag(name).map_err(|e| {
                 anyhow!("Error getting latest tag for component: {:?}: {}", name, e)
             })?,
         };
-        let target = match target {
-            Some(target) => target,
-            None => target_from_name(name)?,
-        };
 
         let release_url = match name {
             component::FORC => SWAY_RELEASE_DOWNLOAD_URL.to_string(),
@@ -72,78 +69,22 @@ impl DownloadCfg {
     }
 
     pub fn from_package(name: &str, package: Package) -> Result<Self> {
-        let target = target_from_name(name)?;
+        let target = TargetTriple::from_component(name)?;
+        let tarball_name = tarball_name(name, &package.version, &target)?;
+        let tarball_url = package.target[&target.to_string()].url.clone();
+        let hash = Some(package.target[&target.to_string()].hash.clone());
         Ok(Self {
             name: name.to_string(),
-            target: target.clone(),
-            version: package.version.clone(),
-            tarball_name: tarball_name(name, &package.version, &target)?,
-            tarball_url: package.target[&target].url.clone(),
-            hash: Some(package.target[&target].hash.clone()),
+            target,
+            version: package.version,
+            tarball_name,
+            tarball_url,
+            hash,
         })
     }
 }
 
-pub fn target_from_name(name: &str) -> Result<String> {
-    match name {
-        component::FORC => {
-            let os = match std::env::consts::OS {
-                "macos" => "darwin",
-                "linux" => "linux",
-                unsupported_os => bail!("Unsupported os: {}", unsupported_os),
-            };
-            let architecture = match std::env::consts::ARCH {
-                "aarch64" => "arm64",
-                "x86_64" => "amd64",
-                unsupported_arch => bail!("Unsupported architecture: {}", unsupported_arch),
-            };
-
-            Ok(format!("{}_{}", os, architecture))
-        }
-
-        component::FUEL_CORE => {
-            let architecture = match std::env::consts::ARCH {
-                "aarch64" | "x86_64" => std::env::consts::ARCH,
-                unsupported_arch => bail!("Unsupported architecture: {}", unsupported_arch),
-            };
-
-            let vendor = match std::env::consts::OS {
-                "macos" => "apple",
-                _ => "unknown",
-            };
-
-            let os = match std::env::consts::OS {
-                "macos" => "darwin",
-                "linux" => "linux-gnu",
-                unsupported_os => bail!("Unsupported os: {}", unsupported_os),
-            };
-
-            Ok(format!("{}-{}-{}", architecture, vendor, os))
-        }
-        component::FUELUP => {
-            let architecture = match std::env::consts::ARCH {
-                "aarch64" | "x86_64" => std::env::consts::ARCH,
-                unsupported_arch => bail!("Unsupported architecture: {}", unsupported_arch),
-            };
-
-            let vendor = match std::env::consts::OS {
-                "macos" => "apple",
-                _ => "unknown",
-            };
-
-            let os = match std::env::consts::OS {
-                "macos" => "darwin",
-                "linux" => "linux-gnu",
-                unsupported_os => bail!("Unsupported os: {}", unsupported_os),
-            };
-
-            Ok(format!("{}-{}-{}", architecture, vendor, os))
-        }
-        _ => bail!("Unrecognized component: {}", name),
-    }
-}
-
-pub fn tarball_name(name: &str, version: &Version, target: &str) -> Result<String> {
+pub fn tarball_name(name: &str, version: &Version, target: &TargetTriple) -> Result<String> {
     match name {
         component::FORC => Ok(format!("forc-binaries-{}.tar.gz", target)),
         component::FUEL_CORE => Ok(format!("fuel-core-{}-{}.tar.gz", version, target)),
diff --git a/src/lib.rs b/src/lib.rs
index 4fe30988..18ad3d9b 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -11,4 +11,5 @@ pub mod ops;
 pub mod path;
 pub mod proxy_cli;
 pub mod settings;
+pub mod target_triple;
 pub mod toolchain;
diff --git a/src/ops/fuelup_check.rs b/src/ops/fuelup_check.rs
index 05946908..6930c511 100644
--- a/src/ops/fuelup_check.rs
+++ b/src/ops/fuelup_check.rs
@@ -9,8 +9,8 @@ use crate::{
     commands::check::CheckCommand,
     component::SUPPORTED_PLUGINS,
     config::Config,
-    download::target_from_name,
     fmt::{bold, colored_bold},
+    target_triple::TargetTriple,
     toolchain::{DistToolchainName, Toolchain},
 };
 use anyhow::Result;
@@ -87,7 +87,7 @@ fn check_fuelup() -> Result<()> {
 
     if let Ok(fuelup_download_cfg) = DownloadCfg::new(
         component::FUELUP,
-        target_from_name(component::FUELUP).ok(),
+        TargetTriple::from_component(component::FUELUP)?,
         None,
     ) {
         bold(|s| write!(s, "{} - ", component::FUELUP));
@@ -115,9 +115,14 @@ fn check_toolchain(toolchain: &str, verbose: bool) -> Result<()> {
                 .map(|&c| {
                     (
                         c.to_owned(),
-                        DownloadCfg::new(c, target_from_name(c).ok(), None)
-                            .unwrap()
-                            .version,
+                        DownloadCfg::new(
+                            c,
+                            TargetTriple::from_component(c)
+                                .expect("Failed to create DownloadCfg from component"),
+                            None,
+                        )
+                        .unwrap()
+                        .version,
                     )
                 })
                 .collect()
diff --git a/src/ops/fuelup_component/add.rs b/src/ops/fuelup_component/add.rs
index fd5e9f14..ab906745 100644
--- a/src/ops/fuelup_component/add.rs
+++ b/src/ops/fuelup_component/add.rs
@@ -6,7 +6,8 @@ use tracing::info;
 
 use crate::{
     commands::component::AddCommand,
-    download::{target_from_name, DownloadCfg},
+    download::DownloadCfg,
+    target_triple::TargetTriple,
     toolchain::{DistToolchainName, Toolchain},
 };
 
@@ -48,7 +49,8 @@ You may create a custom toolchain using 'fuelup toolchain new <toolchain>'.",
             None => (&maybe_versioned_component, None),
         };
 
-    let download_cfg = DownloadCfg::new(component, Some(target_from_name(component)?), version)?;
+    let download_cfg =
+        DownloadCfg::new(component, TargetTriple::from_component(component)?, version)?;
     toolchain.add_component(download_cfg)?;
 
     Ok(())
diff --git a/src/ops/fuelup_self.rs b/src/ops/fuelup_self.rs
index 88e342d8..fd88e2b1 100644
--- a/src/ops/fuelup_self.rs
+++ b/src/ops/fuelup_self.rs
@@ -5,8 +5,9 @@ use tracing::{error, info};
 
 use crate::{
     component,
-    download::{download_file_and_unpack, target_from_name, unpack_bins, DownloadCfg},
+    download::{download_file_and_unpack, unpack_bins, DownloadCfg},
     path::{fuelup_bin, fuelup_bin_dir},
+    target_triple::TargetTriple,
 };
 
 pub fn attempt_install_self(download_cfg: DownloadCfg, dst: &Path) -> Result<()> {
@@ -19,7 +20,7 @@ pub fn attempt_install_self(download_cfg: DownloadCfg, dst: &Path) -> Result<()>
 pub fn self_update() -> Result<()> {
     let download_cfg = DownloadCfg::new(
         component::FUELUP,
-        target_from_name(component::FUELUP).ok(),
+        TargetTriple::from_component(component::FUELUP)?,
         None,
     )?;
 
diff --git a/src/ops/fuelup_toolchain/install.rs b/src/ops/fuelup_toolchain/install.rs
index 36737ac9..739bde7c 100644
--- a/src/ops/fuelup_toolchain/install.rs
+++ b/src/ops/fuelup_toolchain/install.rs
@@ -1,7 +1,8 @@
 use crate::component;
-use crate::download::{target_from_name, DownloadCfg};
+use crate::download::DownloadCfg;
 use crate::path::settings_file;
 use crate::settings::SettingsFile;
+use crate::target_triple::TargetTriple;
 use crate::toolchain::{DistToolchainName, Toolchain};
 use crate::{channel::Channel, commands::toolchain::InstallCommand};
 use anyhow::Result;
@@ -11,7 +12,7 @@ use tracing::{error, info};
 pub fn install(command: InstallCommand) -> Result<()> {
     let InstallCommand { name } = command;
 
-    let toolchain = Toolchain::new(&name, None)?;
+    let toolchain = Toolchain::new(&name)?;
 
     let settings = SettingsFile::new(settings_file());
     settings.with_mut(|s| {
@@ -32,8 +33,13 @@ pub fn install(command: InstallCommand) -> Result<()> {
             [component::FORC, component::FUEL_CORE, component::FORC_LSP]
                 .iter()
                 .map(|c| {
-                    DownloadCfg::new(c, target_from_name(c).ok(), None)
-                        .expect("Failed to create DownloadCfg from component")
+                    DownloadCfg::new(
+                        c,
+                        TargetTriple::from_component(c)
+                            .expect("Failed to create DownloadCfg from component"),
+                        None,
+                    )
+                    .unwrap()
                 })
                 .collect()
         }
diff --git a/src/target_triple.rs b/src/target_triple.rs
new file mode 100644
index 00000000..44068de2
--- /dev/null
+++ b/src/target_triple.rs
@@ -0,0 +1,93 @@
+use crate::component;
+use anyhow::{bail, Result};
+use std::fmt;
+
+#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+pub struct TargetTriple(String);
+
+impl fmt::Display for TargetTriple {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        self.0.fmt(f)
+    }
+}
+
+impl TargetTriple {
+    pub fn from_host() -> Result<Self> {
+        let architecture = match std::env::consts::ARCH {
+            "aarch64" | "x86_64" => std::env::consts::ARCH,
+            unsupported_arch => bail!("Unsupported architecture: {}", unsupported_arch),
+        };
+        let vendor = match std::env::consts::OS {
+            "macos" => "apple",
+            _ => "unknown",
+        };
+        let os = match std::env::consts::OS {
+            "macos" => "darwin",
+            "linux" => "linux-gnu",
+            unsupported_os => bail!("Unsupported os: {}", unsupported_os),
+        };
+
+        let target_triple = format!("{}-{}-{}", architecture, vendor, os);
+
+        Ok(Self(target_triple))
+    }
+
+    pub fn from_component(component: &str) -> Result<Self> {
+        match component {
+            component::FORC => {
+                let os = match std::env::consts::OS {
+                    "macos" => "darwin",
+                    "linux" => "linux",
+                    unsupported_os => bail!("Unsupported os: {}", unsupported_os),
+                };
+                let architecture = match std::env::consts::ARCH {
+                    "aarch64" => "arm64",
+                    "x86_64" => "amd64",
+                    unsupported_arch => bail!("Unsupported architecture: {}", unsupported_arch),
+                };
+
+                Ok(Self(format!("{}_{}", os, architecture)))
+            }
+
+            component::FUEL_CORE => {
+                let architecture = match std::env::consts::ARCH {
+                    "aarch64" | "x86_64" => std::env::consts::ARCH,
+                    unsupported_arch => bail!("Unsupported architecture: {}", unsupported_arch),
+                };
+
+                let vendor = match std::env::consts::OS {
+                    "macos" => "apple",
+                    _ => "unknown",
+                };
+
+                let os = match std::env::consts::OS {
+                    "macos" => "darwin",
+                    "linux" => "linux-gnu",
+                    unsupported_os => bail!("Unsupported os: {}", unsupported_os),
+                };
+
+                Ok(Self(format!("{}-{}-{}", architecture, vendor, os)))
+            }
+            component::FUELUP => {
+                let architecture = match std::env::consts::ARCH {
+                    "aarch64" | "x86_64" => std::env::consts::ARCH,
+                    unsupported_arch => bail!("Unsupported architecture: {}", unsupported_arch),
+                };
+
+                let vendor = match std::env::consts::OS {
+                    "macos" => "apple",
+                    _ => "unknown",
+                };
+
+                let os = match std::env::consts::OS {
+                    "macos" => "darwin",
+                    "linux" => "linux-gnu",
+                    unsupported_os => bail!("Unsupported os: {}", unsupported_os),
+                };
+
+                Ok(Self(format!("{}-{}-{}", architecture, vendor, os)))
+            }
+            _ => bail!("Unrecognized component: {}", component),
+        }
+    }
+}
diff --git a/src/toolchain.rs b/src/toolchain.rs
index 78a398a7..75c0f7d2 100644
--- a/src/toolchain.rs
+++ b/src/toolchain.rs
@@ -12,6 +12,7 @@ use crate::path::{
     ensure_dir_exists, fuelup_bin, fuelup_bin_dir, settings_file, toolchain_bin_dir,
 };
 use crate::settings::SettingsFile;
+use crate::target_triple::TargetTriple;
 use crate::{channel, component};
 
 pub const RESERVED_TOOLCHAIN_NAMES: &[&str] = &[
@@ -50,47 +51,9 @@ pub struct Toolchain {
     pub path: PathBuf,
 }
 
-#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
-pub struct TargetTriple(String);
-
-impl fmt::Display for TargetTriple {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        self.0.fmt(f)
-    }
-}
-
-impl TargetTriple {
-    pub fn new(name: &str) -> Self {
-        Self(name.to_string())
-    }
-
-    pub fn from_host() -> Result<Self> {
-        let architecture = match std::env::consts::ARCH {
-            "aarch64" | "x86_64" => std::env::consts::ARCH,
-            unsupported_arch => bail!("Unsupported architecture: {}", unsupported_arch),
-        };
-        let vendor = match std::env::consts::OS {
-            "macos" => "apple",
-            _ => "unknown",
-        };
-        let os = match std::env::consts::OS {
-            "macos" => "darwin",
-            "linux" => "linux-gnu",
-            unsupported_os => bail!("Unsupported os: {}", unsupported_os),
-        };
-
-        let target_triple = format!("{}-{}-{}", architecture, vendor, os);
-
-        Ok(Self(target_triple))
-    }
-}
-
 impl Toolchain {
-    pub fn new(name: &str, target: Option<String>) -> Result<Self> {
-        let target = match target {
-            Some(t) => TargetTriple(t),
-            None => TargetTriple::from_host()?,
-        };
+    pub fn new(name: &str) -> Result<Self> {
+        let target = TargetTriple::from_host()?;
         let toolchain = format!("{}-{}", name, target);
         let path = toolchain_bin_dir(&toolchain);
         Ok(Self {


Test patch:
diff --git a/tests/commands.rs b/tests/commands.rs
index f3d7a5db..846f7541 100644
--- a/tests/commands.rs
+++ b/tests/commands.rs
@@ -1,5 +1,5 @@
 use anyhow::Result;
-use fuelup::toolchain::TargetTriple;
+use fuelup::target_triple::TargetTriple;
 use std::{env, path::Path};
 
 mod testcfg;