From 1dd70688719f6d9baa3517159959451d500fd172 Mon Sep 17 00:00:00 2001 From: Michael-F-Bryan Date: Tue, 20 Jun 2023 19:35:50 +0800 Subject: [PATCH] Added a WasmerDir abstraction --- lib/cli/src/commands/add.rs | 28 ++---- lib/cli/src/commands/config.rs | 46 +++------- lib/cli/src/commands/init.rs | 11 ++- lib/cli/src/commands/login.rs | 148 +++++++++++++++++++------------ lib/cli/src/commands/publish.rs | 30 +++---- lib/cli/src/commands/run.rs | 18 ++-- lib/cli/src/commands/run/wasi.rs | 30 +++---- lib/cli/src/commands/whoami.rs | 13 ++- lib/cli/src/lib.rs | 3 + lib/cli/src/wasmer_dir.rs | 125 ++++++++++++++++++++++++++ 10 files changed, 287 insertions(+), 165 deletions(-) create mode 100644 lib/cli/src/wasmer_dir.rs diff --git a/lib/cli/src/commands/add.rs b/lib/cli/src/commands/add.rs index 04f6038e143..d6960a5f73d 100644 --- a/lib/cli/src/commands/add.rs +++ b/lib/cli/src/commands/add.rs @@ -2,14 +2,15 @@ use std::process::{Command, Stdio}; use anyhow::{Context, Error}; use clap::Parser; -use wasmer_registry::{Bindings, ProgrammingLanguage, WasmerConfig}; +use wasmer_registry::{Bindings, ProgrammingLanguage}; + +use crate::WasmerDir; /// Add a Wasmer package's bindings to your application. #[derive(Debug, Parser)] pub struct Add { - /// The registry to fetch bindings from. - #[clap(long, env = "WASMER_REGISTRY")] - registry: Option, + #[clap(flatten)] + wasmer_dir: WasmerDir, /// Add the JavaScript bindings using "npm install". #[clap(long, groups = &["bindings", "js"])] npm: bool, @@ -32,10 +33,11 @@ impl Add { anyhow::ensure!(!self.packages.is_empty(), "No packages specified"); let registry = self - .registry() + .wasmer_dir + .registry_endpoint() .context("Unable to determine which registry to use")?; - let bindings = self.lookup_bindings(®istry)?; + let bindings = self.lookup_bindings(registry.as_str())?; let mut cmd = self.target()?.command(&bindings)?; cmd.stdin(Stdio::null()) @@ -71,20 +73,6 @@ impl Add { Ok(bindings_to_add) } - fn registry(&self) -> Result { - match &self.registry { - Some(r) => Ok(r.clone()), - None => { - let wasmer_dir = - WasmerConfig::get_wasmer_dir().map_err(|e| anyhow::anyhow!("{e}"))?; - let cfg = WasmerConfig::from_file(&wasmer_dir) - .map_err(Error::msg) - .context("Unable to load Wasmer config file")?; - Ok(cfg.registry.get_current_registry()) - } - } - } - fn target(&self) -> Result { match (self.pip, self.npm, self.yarn) { (false, false, false) => Err(anyhow::anyhow!( diff --git a/lib/cli/src/commands/config.rs b/lib/cli/src/commands/config.rs index 0de850a6839..2c4ae8ae402 100644 --- a/lib/cli/src/commands/config.rs +++ b/lib/cli/src/commands/config.rs @@ -1,14 +1,15 @@ -use crate::VERSION; +use crate::{WasmerDir, VERSION}; use anyhow::{Context, Result}; use clap::Parser; -use std::env; -use std::path::PathBuf; use std::str::ParseBoolError; use wasmer_registry::WasmerConfig; #[derive(Debug, Parser)] /// The options for the `wasmer config` subcommand: `wasmer config get --OPTION` or `wasmer config set [FLAG]` pub struct Config { + #[clap(flatten)] + wasmer_dir: WasmerDir, + #[clap(flatten)] flags: Flags, /// Subcommand for `wasmer config get | set` @@ -168,29 +169,12 @@ impl Config { fn inner_execute(&self) -> Result<()> { if let Some(s) = self.set.as_ref() { - return s.execute(); + return s.execute(&self.wasmer_dir); } let flags = &self.flags; - let key = "WASMER_DIR"; - let wasmer_dir = env::var(key) - .ok() - .or_else(|| option_env!("WASMER_INSTALL_PREFIX").map(str::to_string)) - .or_else(|| { - // Allowing deprecated function home_dir since it works fine, - // and will never be removed from std. - #[allow(deprecated)] - let dir = std::env::home_dir()?.join(".wasmer").to_str()?.to_string(); - - Some(dir) - }) - .context(format!( - "failed to retrieve the {} environment variables", - key - ))?; - - let prefix = PathBuf::from(wasmer_dir); + let prefix = self.wasmer_dir.dir(); let prefixdir = prefix.display().to_string(); let bindir = prefix.join("bin").display().to_string(); @@ -233,9 +217,7 @@ impl Config { } if flags.config_path { - let wasmer_dir = WasmerConfig::get_wasmer_dir() - .map_err(|e| anyhow::anyhow!("could not find wasmer dir: {e}"))?; - let path = WasmerConfig::get_file_location(&wasmer_dir); + let path = WasmerConfig::get_file_location(self.wasmer_dir.dir()); println!("{}", path.display()); } @@ -244,16 +226,10 @@ impl Config { } impl GetOrSet { - fn execute(&self) -> Result<()> { - let wasmer_dir = WasmerConfig::get_wasmer_dir() - .map_err(|e| anyhow::anyhow!("could not find wasmer dir: {e}"))?; - let config_file = WasmerConfig::get_file_location(&wasmer_dir); - let mut config = WasmerConfig::from_file(&wasmer_dir).map_err(|e| { - anyhow::anyhow!( - "could not find config file {e} at {}", - config_file.display() - ) - })?; + fn execute(&self, wasmer_dir: &WasmerDir) -> Result<()> { + let config_file = WasmerConfig::get_file_location(wasmer_dir.dir()); + let mut config = wasmer_dir.config()?; + match self { GetOrSet::Get(g) => match g { RetrievableConfigField::RegistryUrl => { diff --git a/lib/cli/src/commands/init.rs b/lib/cli/src/commands/init.rs index 5b7586f9495..fff6c701d72 100644 --- a/lib/cli/src/commands/init.rs +++ b/lib/cli/src/commands/init.rs @@ -5,7 +5,8 @@ use indexmap::IndexMap; use std::collections::HashMap; use std::path::Path; use std::path::PathBuf; -use wasmer_registry::WasmerConfig; + +use crate::WasmerDir; static NOTE: &str = "# See more keys and definitions at https://docs.wasmer.io/registry/manifest"; @@ -14,6 +15,9 @@ const NEWLINE: &str = if cfg!(windows) { "\r\n" } else { "\n" }; /// CLI args for the `wasmer init` command #[derive(Debug, Parser)] pub struct Init { + #[clap(flatten)] + wasmer_dir: WasmerDir, + /// Initialize wasmer.toml for a library package #[clap(long, group = "crate-type")] pub lib: bool, @@ -136,6 +140,7 @@ impl Init { self.template.as_ref(), self.include.as_slice(), self.quiet, + self.wasmer_dir.dir(), )?; if let Some(parent) = target_file.parent() { @@ -347,6 +352,7 @@ fn construct_manifest( template: Option<&Template>, include_fs: &[String], quiet: bool, + wasmer_dir: &Path, ) -> Result { if let Some(ct) = cargo_toml.as_ref() { let msg = format!( @@ -365,9 +371,8 @@ fn construct_manifest( .map(|p| &p.name) .unwrap_or(fallback_package_name) }); - let wasmer_dir = WasmerConfig::get_wasmer_dir().map_err(|e| anyhow::anyhow!("{e}"))?; let namespace = namespace.or_else(|| { - wasmer_registry::whoami(&wasmer_dir, None, None) + wasmer_registry::whoami(wasmer_dir, None, None) .ok() .map(|o| o.1) }); diff --git a/lib/cli/src/commands/login.rs b/lib/cli/src/commands/login.rs index 06a90d9b315..2f7c4bea197 100644 --- a/lib/cli/src/commands/login.rs +++ b/lib/cli/src/commands/login.rs @@ -1,86 +1,122 @@ +use std::path::PathBuf; + use clap::Parser; #[cfg(not(test))] use dialoguer::Input; -use wasmer_registry::WasmerConfig; + +use crate::{Registry, WasmerDir}; /// Subcommand for listing packages #[derive(Debug, Clone, Parser)] pub struct Login { - /// Registry to log into (default: wasmer.io) - #[clap(long, default_value = "wasmer.io")] - pub registry: String, - /// Login token - #[clap(name = "TOKEN")] + /// Set Wasmer's home directory + #[clap(long, env = "WASMER_DIR", default_value = crate::WASMER_DIR.as_os_str())] + pub wasmer_dir: PathBuf, + /// The registry to fetch packages from (inferred from the environment by + /// default) + #[clap(long, env = "WASMER_REGISTRY")] + pub registry: Option, + /// The API token to use when communicating with the registry (inferred from + /// the environment by default) pub token: Option, } impl Login { - fn get_token_or_ask_user(&self) -> Result { - match self.token.as_ref() { - Some(s) => Ok(s.clone()), - None => { - let registry_host = wasmer_registry::format_graphql(&self.registry); - let registry_tld = tldextract::TldExtractor::new(tldextract::TldOption::default()) - .extract(®istry_host) - .map_err(|e| { - std::io::Error::new( - std::io::ErrorKind::Other, - format!("Invalid registry for login {}: {e}", self.registry), - ) - })?; - let login_prompt = match ( - registry_tld.domain.as_deref(), - registry_tld.suffix.as_deref(), - ) { - (Some(d), Some(s)) => { - format!("Please paste the login token from https://{d}.{s}/settings/access-tokens") - } - _ => "Please paste the login token".to_string(), - }; - #[cfg(test)] - { - Ok(login_prompt) - } - #[cfg(not(test))] - { - Input::new().with_prompt(&login_prompt).interact_text() - } + fn get_token_or_ask_user(&self, wasmer_dir: &WasmerDir) -> Result { + if let Some(token) = &self.token { + return Ok(token.clone()); + } + + let registry_host = wasmer_dir.registry_endpoint()?; + let registry_tld = tldextract::TldExtractor::new(tldextract::TldOption::default()) + .extract(registry_host.as_str()) + .map_err(|e| { + std::io::Error::new( + std::io::ErrorKind::Other, + format!("Invalid registry for login {}: {e}", registry_host), + ) + })?; + let login_prompt = match ( + registry_tld.domain.as_deref(), + registry_tld.suffix.as_deref(), + ) { + (Some(d), Some(s)) => { + format!("Please paste the login token from https://{d}.{s}/settings/access-tokens") } + _ => "Please paste the login token".to_string(), + }; + #[cfg(test)] + { + Ok(login_prompt) + } + #[cfg(not(test))] + { + let token = Input::new().with_prompt(&login_prompt).interact_text()?; + Ok(token) } } + fn wasmer_dir(&self) -> WasmerDir { + WasmerDir::new( + self.wasmer_dir.clone(), + self.registry.clone(), + self.token.clone(), + ) + } + /// execute [List] pub fn execute(&self) -> Result<(), anyhow::Error> { - let token = self.get_token_or_ask_user()?; - let wasmer_dir = - WasmerConfig::get_wasmer_dir().map_err(|e| anyhow::anyhow!("no wasmer dir: {e}"))?; - match wasmer_registry::login::login_and_save_token(&wasmer_dir, &self.registry, &token)? { + let wasmer_dir = self.wasmer_dir(); + let token = self.get_token_or_ask_user(&wasmer_dir)?; + + let registry = wasmer_dir.registry_endpoint()?; + match wasmer_registry::login::login_and_save_token( + wasmer_dir.dir(), + registry.as_str(), + &token, + )? { Some(s) => println!("Login for Wasmer user {:?} saved", s), None => println!( "Error: no user found on registry {:?} with token {:?}. Token saved regardless.", - self.registry, token + registry, token ), } Ok(()) } } -#[test] -fn test_login_2() { - let login = Login { - registry: "wasmer.wtf".to_string(), - token: None, - }; +#[cfg(test)] +mod tests { + use tempfile::TempDir; + + use super::*; - assert_eq!( - login.get_token_or_ask_user().unwrap(), - "Please paste the login token from https://wasmer.wtf/settings/access-tokens" - ); + #[test] + fn interactive_login() { + let temp = TempDir::new().unwrap(); + let login = Login { + registry: Some("wasmer.wtf".into()), + wasmer_dir: temp.path().to_path_buf(), + token: None, + }; + let wasmer_dir = login.wasmer_dir(); - let login = Login { - registry: "wasmer.wtf".to_string(), - token: Some("abc".to_string()), - }; + assert_eq!( + login.get_token_or_ask_user(&wasmer_dir).unwrap(), + "Please paste the login token from https://wasmer.wtf/settings/access-tokens" + ); + } + + #[test] + fn login_with_token() { + let temp = TempDir::new().unwrap(); + let login = Login { + registry: Some("wasmer.wtf".into()), + wasmer_dir: temp.path().to_path_buf(), + token: Some("abc".to_string()), + }; + let wasmer_dir = login.wasmer_dir(); - assert_eq!(login.get_token_or_ask_user().unwrap(), "abc"); + assert_eq!(login.get_token_or_ask_user(&wasmer_dir).unwrap(), "abc"); + } } diff --git a/lib/cli/src/commands/publish.rs b/lib/cli/src/commands/publish.rs index bd332d6276c..5ceb1eec26f 100644 --- a/lib/cli/src/commands/publish.rs +++ b/lib/cli/src/commands/publish.rs @@ -1,14 +1,15 @@ -use anyhow::Context; +use std::path::Path; + use clap::Parser; -use wasmer_registry::WasmerConfig; use wasmer_wasix::runtime::resolver::WapmSource; +use crate::WasmerDir; + /// Publish a package to the package registry. #[derive(Debug, Parser)] pub struct Publish { - /// Registry to publish to - #[clap(long)] - pub registry: Option, + #[clap(flatten)] + wasmer_dir: WasmerDir, /// Run the publish logic without sending anything to the registry server #[clap(long, name = "dry-run")] pub dry_run: bool, @@ -21,9 +22,6 @@ pub struct Publish { /// Override the package version of the uploaded package in the wasmer.toml #[clap(long)] pub version: Option, - /// Override the token (by default, it will use the current logged in user) - #[clap(long)] - pub token: Option, /// Skip validation of the uploaded package #[clap(long)] pub no_validate: bool, @@ -38,18 +36,22 @@ impl Publish { /// Executes `wasmer publish` pub fn execute(&self) -> Result<(), anyhow::Error> { let publish = wasmer_registry::package::builder::Publish { - registry: self.registry.clone(), + registry: self + .wasmer_dir + .registry_endpoint() + .map(|u| u.to_string()) + .ok(), dry_run: self.dry_run, quiet: self.quiet, package_name: self.package_name.clone(), version: self.version.clone(), - token: self.token.clone(), + token: self.wasmer_dir.token(), no_validate: self.no_validate, package_path: self.package_path.clone(), }; publish.execute().map_err(on_error)?; - if let Err(e) = invalidate_graphql_query_cache() { + if let Err(e) = invalidate_graphql_query_cache(self.wasmer_dir.dir()) { tracing::warn!( error = &*e, "Unable to invalidate the cache used for package version queries", @@ -72,11 +74,7 @@ fn on_error(e: anyhow::Error) -> anyhow::Error { // are cleaner ways to achieve this, but for now we're just going to // clear out the whole GraphQL query cache. // See https://github.com/wasmerio/wasmer/pull/3983 for more -fn invalidate_graphql_query_cache() -> Result<(), anyhow::Error> { - let wasmer_dir = WasmerConfig::get_wasmer_dir() - .map_err(anyhow::Error::msg) - .context("Unable to determine the wasmer dir")?; - +fn invalidate_graphql_query_cache(wasmer_dir: &Path) -> Result<(), anyhow::Error> { WapmSource::invalidate_local_cache(wasmer_dir)?; Ok(()) diff --git a/lib/cli/src/commands/run.rs b/lib/cli/src/commands/run.rs index 82d0b17ff65..502ce4bdfbc 100644 --- a/lib/cli/src/commands/run.rs +++ b/lib/cli/src/commands/run.rs @@ -49,23 +49,17 @@ use wasmer_wasix::{ }; use webc::{metadata::Manifest, Container}; -use crate::{commands::run::wasi::Wasi, error::PrettyError, logging::Output, store::StoreOptions}; +use crate::{ + commands::run::wasi::Wasi, error::PrettyError, logging::Output, store::StoreOptions, WasmerDir, +}; const TICK: Duration = Duration::from_millis(250); -static WASMER_HOME: Lazy = Lazy::new(|| { - wasmer_registry::WasmerConfig::get_wasmer_dir() - .ok() - .or_else(|| dirs::home_dir().map(|home| home.join(".wasmer"))) - .unwrap_or_else(|| PathBuf::from(".wasmer")) -}); - /// The unstable `wasmer run` subcommand. #[derive(Debug, Parser)] pub struct Run { - /// The Wasmer home directory. - #[clap(long = "wasmer-dir", env = "WASMER_DIR", default_value = WASMER_HOME.as_os_str())] - wasmer_dir: PathBuf, + #[clap(flatten)] + wasmer_dir: WasmerDir, #[clap(flatten)] store: StoreOptions, #[clap(flatten)] @@ -358,7 +352,7 @@ impl Run { }; let store = StoreOptions::default(); Ok(Run { - wasmer_dir: WASMER_HOME.clone(), + wasmer_dir: WasmerDir::default(), store, wasi: Wasi::for_binfmt_interpreter()?, wcgi: WcgiOptions::default(), diff --git a/lib/cli/src/commands/run/wasi.rs b/lib/cli/src/commands/run/wasi.rs index 273329926b1..d5a03da1a1a 100644 --- a/lib/cli/src/commands/run/wasi.rs +++ b/lib/cli/src/commands/run/wasi.rs @@ -35,7 +35,10 @@ use wasmer_wasix::{ WasiVersion, }; -use crate::utils::{parse_envvar, parse_mapdir}; +use crate::{ + utils::{parse_envvar, parse_mapdir}, + WasmerDir, +}; const WAPM_SOURCE_CACHE_TIMEOUT: Duration = Duration::from_secs(10 * 60); @@ -110,10 +113,6 @@ pub struct Wasi { /// Require WASI modules to only import 1 version of WASI. #[clap(long = "deny-multiple-wasi-versions")] pub deny_multiple_wasi_versions: bool, - - /// The registry to use. - #[clap(long, env = "WASMER_REGISTRY", value_parser = parse_registry)] - pub registry: Option, } pub struct RunProperties { @@ -249,7 +248,7 @@ impl Wasi { pub fn prepare_runtime( &self, engine: Engine, - wasmer_dir: &Path, + wasmer_dir: &WasmerDir, handle: Handle, ) -> Result { let mut rt = PluggableRuntime::new(Arc::new(TokioTaskManager::new(handle))); @@ -271,12 +270,12 @@ impl Wasi { let client = Arc::new(client); let package_loader = self - .prepare_package_loader(wasmer_dir, client.clone()) + .prepare_package_loader(wasmer_dir.dir(), client.clone()) .context("Unable to prepare the package loader")?; let registry = self.prepare_source(wasmer_dir, client)?; - let cache_dir = FileSystemCache::default_cache_dir(wasmer_dir); + let cache_dir = FileSystemCache::default_cache_dir(wasmer_dir.dir()); let module_cache = wasmer_wasix::runtime::module_cache::in_memory() .with_fallback(FileSystemCache::new(cache_dir)); @@ -328,7 +327,7 @@ impl Wasi { fn prepare_source( &self, - wasmer_dir: &Path, + wasmer_dir: &WasmerDir, client: Arc, ) -> Result { let mut source = MultiSource::new(); @@ -344,12 +343,12 @@ impl Wasi { source.add_source(preloaded); let graphql_endpoint = self.graphql_endpoint(wasmer_dir)?; - let cache_dir = WapmSource::default_cache_dir(wasmer_dir); + let cache_dir = WapmSource::default_cache_dir(wasmer_dir.dir()); let wapm_source = WapmSource::new(graphql_endpoint, Arc::clone(&client)) .with_local_cache(cache_dir, WAPM_SOURCE_CACHE_TIMEOUT); source.add_source(wapm_source); - let cache_dir = WebSource::default_cache_dir(wasmer_dir); + let cache_dir = WebSource::default_cache_dir(wasmer_dir.dir()); source.add_source(WebSource::new(cache_dir, client)); source.add_source(FileSystemSource::default()); @@ -357,13 +356,12 @@ impl Wasi { Ok(source) } - fn graphql_endpoint(&self, wasmer_dir: &Path) -> Result { - if let Some(endpoint) = &self.registry { - return Ok(endpoint.clone()); + fn graphql_endpoint(&self, wasmer_dir: &WasmerDir) -> Result { + if let Ok(endpoint) = wasmer_dir.registry_endpoint() { + return Ok(endpoint); } - let config = - wasmer_registry::WasmerConfig::from_file(wasmer_dir).map_err(anyhow::Error::msg)?; + let config = wasmer_dir.config()?; let graphql_endpoint = config.registry.get_graphql_url(); let graphql_endpoint = graphql_endpoint .parse() diff --git a/lib/cli/src/commands/whoami.rs b/lib/cli/src/commands/whoami.rs index 8d5b27fedee..adab954d0fb 100644 --- a/lib/cli/src/commands/whoami.rs +++ b/lib/cli/src/commands/whoami.rs @@ -1,21 +1,20 @@ use clap::Parser; -use wasmer_registry::WasmerConfig; + +use crate::WasmerDir; #[derive(Debug, Parser)] /// The options for the `wasmer whoami` subcommand pub struct Whoami { - /// Which registry to check the logged in username for - #[clap(long, name = "registry")] - pub registry: Option, + #[clap(flatten)] + wasmer_dir: WasmerDir, } impl Whoami { /// Execute `wasmer whoami` pub fn execute(&self) -> Result<(), anyhow::Error> { - let wasmer_dir = - WasmerConfig::get_wasmer_dir().map_err(|e| anyhow::anyhow!("no wasmer dir: {e}"))?; + let registry = self.wasmer_dir.registry_endpoint()?; let (registry, username) = - wasmer_registry::whoami(&wasmer_dir, self.registry.as_deref(), None)?; + wasmer_registry::whoami(self.wasmer_dir.dir(), Some(registry.as_str()), None)?; println!("logged into registry {registry:?} as user {username:?}"); Ok(()) } diff --git a/lib/cli/src/lib.rs b/lib/cli/src/lib.rs index 5e9d4597aad..5e0a5cae53c 100644 --- a/lib/cli/src/lib.rs +++ b/lib/cli/src/lib.rs @@ -27,6 +27,9 @@ pub mod package_source; pub mod store; pub mod suggestions; pub mod utils; +mod wasmer_dir; /// Version number of this crate. pub const VERSION: &str = env!("CARGO_PKG_VERSION"); + +pub use crate::wasmer_dir::{Registry, WasmerDir, WASMER_DIR}; diff --git a/lib/cli/src/wasmer_dir.rs b/lib/cli/src/wasmer_dir.rs new file mode 100644 index 00000000000..cdd3b9fbf9d --- /dev/null +++ b/lib/cli/src/wasmer_dir.rs @@ -0,0 +1,125 @@ +use std::path::{Path, PathBuf}; + +use anyhow::{Context, Error}; +use once_cell::sync::Lazy; +use url::Url; +use wasmer_registry::WasmerConfig; + +/// Command-line flags for determining `$WASMER_DIR` and interactions with the +/// registry. +#[derive(Debug, Clone, PartialEq, clap::Parser)] +pub struct WasmerDir { + /// Set Wasmer's home directory + #[clap(long, env = "WASMER_DIR", default_value = WASMER_DIR.as_os_str())] + wasmer_dir: PathBuf, + /// The registry to fetch packages from (inferred from the environment by + /// default) + #[clap(long, env = "WASMER_REGISTRY")] + registry: Option, + /// The API token to use when communicating with the registry (inferred from + /// the environment by default) + #[clap(long, env = "WASMER_TOKEN")] + token: Option, +} + +impl WasmerDir { + pub(crate) fn new( + wasmer_dir: PathBuf, + registry: Option, + token: Option, + ) -> Self { + WasmerDir { + wasmer_dir, + registry, + token, + } + } + + /// Get the GraphQL endpoint used to query the registry. + pub fn registry_endpoint(&self) -> Result { + if let Some(registry) = &self.registry { + return registry.graphql_endpoint(); + } + + let config = self.config()?; + let url = config.registry.get_current_registry().parse()?; + + Ok(url) + } + + /// Load the current Wasmer config. + pub fn config(&self) -> Result { + WasmerConfig::from_file(self.dir()) + .map_err(Error::msg) + .with_context(|| { + format!( + "Unable to load the config from the \"{}\" directory", + self.dir().display() + ) + }) + } + + /// The directory all Wasmer artifacts are stored in. + pub fn dir(&self) -> &Path { + &self.wasmer_dir + } + + /// The API token for the active registry. + pub fn token(&self) -> Option { + let config = self.config().ok()?; + let login = config.registry.current_login()?; + Some(login.token.clone()) + } +} + +impl Default for WasmerDir { + fn default() -> Self { + Self { + wasmer_dir: WASMER_DIR.clone(), + registry: None, + token: None, + } + } +} + +/// The default value for `$WASMER_DIR`. +pub static WASMER_DIR: Lazy = + Lazy::new(|| match wasmer_registry::WasmerConfig::get_wasmer_dir() { + Ok(path) => path, + Err(e) => { + if let Some(install_prefix) = std::env::var_os("WASMER_INSTALL_PREFIX") { + PathBuf::from(install_prefix) + } else { + panic!("Unable to determine the wasmer dir: {e}"); + } + } + }); + +/// A registry as specified by the user. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Registry(String); + +impl Registry { + /// Get the [`Registry`]'s string representation. + pub fn as_str(&self) -> &str { + self.0.as_str() + } + + /// Get the GraphQL endpoint for this [`Registry`]. + pub fn graphql_endpoint(&self) -> Result { + let url = wasmer_registry::format_graphql(self.as_str()).parse()?; + Ok(url) + } +} + +impl From for Registry { + fn from(value: String) -> Self { + Registry(value) + } +} + +impl From<&str> for Registry { + fn from(value: &str) -> Self { + Registry(value.to_string()) + } +}