diff --git a/lib/cli/src/cli.rs b/lib/cli/src/cli.rs index 7ffc193e6..014c69d58 100644 --- a/lib/cli/src/cli.rs +++ b/lib/cli/src/cli.rs @@ -199,21 +199,28 @@ fn parse_cli_args() -> Result<(), anyhow::Error> { #[cfg(feature = "binfmt")] (Some("binfmt"), _) => Binfmt::try_parse_from(args_without_first_arg.iter())?.execute(), (Some("list"), Some("--installed")) => { - use prettytable::{format, Table, row}; + use prettytable::{format, row, Table}; let rows = get_all_local_packages() - .into_iter() - .map(|pkg| { + .into_iter() + .map(|pkg| { + let commands = pkg + .manifest + .command + .unwrap_or_default() + .iter() + .map(|c| c.get_name()) + .collect::>() + .join(" \r\n"); - let commands = pkg.manifest.command - .unwrap_or_default() - .iter() - .map(|c| c.get_name()) - .collect::>() - .join(" \r\n"); - - row![pkg.registry.clone(), pkg.name.clone(), pkg.version.clone(), commands] - }).collect::>(); + row![ + pkg.registry.clone(), + pkg.name.clone(), + pkg.version.clone(), + commands + ] + }) + .collect::>(); let mut table = Table::init(rows); table.set_titles(row!["Registry", "Package", "Version", "Commands"]); @@ -237,7 +244,7 @@ fn parse_cli_args() -> Result<(), anyhow::Error> { if let Ok(Ok(run)) = result { return run.execute(); - } else if let Ok((package, version)) = split_version(package) { + } else if let Ok((package, version)) = split_version(package) { if let Some(package) = wasmer_registry::get_local_package( &package, version.as_ref().map(|s| s.as_str()), @@ -251,13 +258,12 @@ fn parse_cli_args() -> Result<(), anyhow::Error> { } // else: local package not found - let sp = - spinner::SpinnerBuilder::new(format!("Installing package {package} ...")) - .spinner(vec![ - "⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷", " ", "⠁", "⠂", "⠄", "⡀", - "⢀", "⠠", "⠐", "⠈", - ]) - .start(); + let sp = spinner::SpinnerBuilder::new(format!("Installing package {package} ...")) + .spinner(vec![ + "⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷", " ", "⠁", "⠂", "⠄", "⡀", "⢀", "⠠", + "⠐", "⠈", + ]) + .start(); let v = version.as_ref().map(|s| s.as_str()); let result = wasmer_registry::install_package(&package, v); @@ -271,7 +277,7 @@ fn parse_cli_args() -> Result<(), anyhow::Error> { return RunWithoutFile::try_parse_from(args_without_package.iter())? .into_run_args(o) .execute(); - }, + } Err(e) => { println!("{e}"); return Ok(()); diff --git a/lib/registry/src/lib.rs b/lib/registry/src/lib.rs index 0fec5ab53..87c1a1465 100644 --- a/lib/registry/src/lib.rs +++ b/lib/registry/src/lib.rs @@ -1,19 +1,19 @@ -use std::path::{Path, PathBuf}; -use std::env; use std::collections::BTreeMap; +use std::env; +use std::path::{Path, PathBuf}; use serde::Deserialize; use serde::Serialize; pub mod graphql { + use core::time; use graphql_client::*; #[cfg(not(target_os = "wasi"))] use reqwest::{ blocking::{multipart::Form, Client}, header::USER_AGENT, }; - use core::time; use std::env; use std::time::Duration; #[cfg(target_os = "wasi")] @@ -161,7 +161,7 @@ pub mod graphql { .multipart(form) .bearer_auth(env::var("WAPM_REGISTRY_TOKEN").unwrap_or(login_token.to_string())) .header(USER_AGENT, user_agent); - + if let Some(t) = timeout { res = res.timeout(t); } @@ -217,7 +217,7 @@ pub mod graphql { .multipart(form) .bearer_auth(env::var("WAPM_REGISTRY_TOKEN").unwrap_or(login_token.to_string())) .header(USER_AGENT, user_agent); - + if let Some(t) = timeout { res = res.timeout(t); } @@ -253,13 +253,7 @@ pub mod graphql { for<'de> R: serde::Deserialize<'de>, V: serde::Serialize, { - execute_query_modifier_inner( - registry_url, - login_token, - query, - Some(timeout), - |f| f - ) + execute_query_modifier_inner(registry_url, login_token, query, Some(timeout), |f| f) } } @@ -356,14 +350,12 @@ fn format_graphql(registry: &str) -> String { } impl PartialWapmConfig { - pub fn from_file() -> Result { let path = Self::get_file_location()?; match std::fs::read_to_string(&path) { Ok(config_toml) => { - toml::from_str(&config_toml) - .map_err(|e| format!("could not parse {path:?}: {e}")) + toml::from_str(&config_toml).map_err(|e| format!("could not parse {path:?}: {e}")) } Err(_e) => Ok(Self::default()), } @@ -379,10 +371,7 @@ impl PartialWapmConfig { pub fn get_folder() -> Result { Ok( - if let Some(folder_str) = env::var("WASMER_DIR") - .ok() - .filter(|s| !s.is_empty()) - { + if let Some(folder_str) = env::var("WASMER_DIR").ok().filter(|s| !s.is_empty()) { let folder = PathBuf::from(folder_str); std::fs::create_dir_all(folder.clone()) .map_err(|e| format!("cannot create config directory: {e}"))?; @@ -426,13 +415,26 @@ pub fn get_command_local(name: &str) -> Result { Err(format!("unimplemented")) } -pub fn get_package_local_dir(registry_host: &str, name: &str, version: &str) -> Result { - if !name.contains("/") { - return Err(format!("package name has to be in the format namespace/package: {name:?}")); +pub fn get_package_local_dir( + registry_host: &str, + name: &str, + version: &str, +) -> Result { + if !name.contains("/") { + return Err(format!( + "package name has to be in the format namespace/package: {name:?}" + )); } - let namespace = name.split("/").nth(0).ok_or(format!("missing namespace for {name:?}"))?; - let name = name.split("/").nth(1).ok_or(format!("missing name for {name:?}"))?; - let install_dir = get_global_install_dir(registry_host).ok_or(format!("no install dir for {name:?}"))?; + let namespace = name + .split("/") + .nth(0) + .ok_or(format!("missing namespace for {name:?}"))?; + let name = name + .split("/") + .nth(1) + .ok_or(format!("missing name for {name:?}"))?; + let install_dir = + get_global_install_dir(registry_host).ok_or(format!("no install dir for {name:?}"))?; Ok(install_dir.join(namespace).join(name).join(version)) } @@ -446,17 +448,18 @@ pub struct LocalPackage { } fn get_all_names_in_dir(dir: &PathBuf) -> Vec<(PathBuf, String)> { + if !dir.is_dir() { + return Vec::new(); + } - if !dir.is_dir() { return Vec::new(); } - let read_dir = match std::fs::read_dir(dir) { Ok(o) => o, Err(_) => return Vec::new(), }; let entries = read_dir - .map(|res| res.map(|e| e.path())) - .collect::, std::io::Error>>(); + .map(|res| res.map(|e| e.path())) + .collect::, std::io::Error>>(); let registry_entries = match entries { Ok(o) => o, @@ -464,25 +467,21 @@ fn get_all_names_in_dir(dir: &PathBuf) -> Vec<(PathBuf, String)> { }; registry_entries - .into_iter() - .filter_map(|re| { - Some((re.clone(), re.file_name()?.to_str()?.to_string())) - }) - .collect() + .into_iter() + .filter_map(|re| Some((re.clone(), re.file_name()?.to_str()?.to_string()))) + .collect() } /// Returns a list of all locally installed packages pub fn get_all_local_packages() -> Vec { - let mut packages = Vec::new(); 'outer: for registry in get_all_available_registries().unwrap_or_default() { - let root_dir = match get_global_install_dir(®istry) { Some(o) => o, None => continue 'outer, }; - + for (registry_path, registry_host) in get_all_names_in_dir(&root_dir) { for (package_path, package_name) in get_all_names_in_dir(®istry_path) { for (version_path, package_version) in get_all_names_in_dir(&package_path) { @@ -494,9 +493,9 @@ pub fn get_all_local_packages() -> Vec { Ok(o) => o, Err(_) => continue, }; - packages.push(LocalPackage { - registry: registry_host.clone(), - name: package_name.clone(), + packages.push(LocalPackage { + registry: registry_host.clone(), + name: package_name.clone(), version: package_version, manifest: manifest, path: version_path, @@ -511,34 +510,44 @@ pub fn get_all_local_packages() -> Vec { pub fn get_local_package(name: &str, version: Option<&str>) -> Option { get_all_local_packages() - .iter() - .filter(|p| { - if p.name != name { return false; } - if let Some(v) = version { - if p.version != v { return false; } - } - true - }) - .cloned() - .next() + .iter() + .filter(|p| { + if p.name != name { + return false; + } + if let Some(v) = version { + if p.version != v { + return false; + } + } + true + }) + .cloned() + .next() } -pub fn get_package_local_wasm_file(registry_host: &str, name: &str, version: &str) -> Result { - +pub fn get_package_local_wasm_file( + registry_host: &str, + name: &str, + version: &str, +) -> Result { let dir = get_package_local_dir(registry_host, name, version)?; let wapm_toml_path = dir.join("wapm.toml"); let wapm_toml_str = std::fs::read_to_string(&wapm_toml_path) .map_err(|e| format!("cannot parse wapm.toml for {name}@{version}"))?; let wapm = toml::from_str::(&wapm_toml_str) .map_err(|e| format!("cannot parse wapm.toml for {name}@{version}"))?; - + // TODO: this will just return the path for the first command, so this might not be correct - let command_name = wapm.command + let command_name = wapm + .command .unwrap_or_default() .first() .map(|m| m.get_module()) - .ok_or(format!("cannot get entrypoint for {name}@{version}: package has no commands"))?; - + .ok_or(format!( + "cannot get entrypoint for {name}@{version}: package has no commands" + ))?; + Ok(dir.join(command_name)) } @@ -553,11 +562,7 @@ pub fn query_package_from_registry( name: &str, version: Option<&str>, ) -> Result, String)> { - - use crate::graphql::{ - GetPackagesQuery, get_packages_query, - execute_query - }; + use crate::graphql::{execute_query, get_packages_query, GetPackagesQuery}; use graphql_client::GraphQLQuery; let q = if name.contains("/") { @@ -572,61 +577,72 @@ pub fn query_package_from_registry( }; let response: get_packages_query::ResponseData = execute_query(registry_url, "", &q) - .map_err(|e| (Vec::new(), format!("Error sending GetPackagesQuery:  {e}")))?; + .map_err(|e| (Vec::new(), format!("Error sending GetPackagesQuery:  {e}")))?; - let available_packages = response.package - .iter() - .filter_map(|p| { - - let p = p.as_ref()?; - let mut versions = Vec::new(); - - for v in p.versions.iter() { - for v in v.iter() { - let v = match v.as_ref() { - Some(s) => s, - None => continue, - }; + let available_packages = response + .package + .iter() + .filter_map(|p| { + let p = p.as_ref()?; + let mut versions = Vec::new(); - versions.push(PackageDownloadInfo { - registry: registry_url.to_string(), - package: p.name.clone(), - - version: v.version.clone(), - - commands: toml::from_str::( - &v.manifest - ).ok()? - .command.unwrap_or_default() - .iter().map(|s| s.get_name()).collect(), - - url: v.distribution.download_url.clone(), - }); + for v in p.versions.iter() { + for v in v.iter() { + let v = match v.as_ref() { + Some(s) => s, + None => continue, + }; + + versions.push(PackageDownloadInfo { + registry: registry_url.to_string(), + package: p.name.clone(), + + version: v.version.clone(), + + commands: toml::from_str::(&v.manifest) + .ok()? + .command + .unwrap_or_default() + .iter() + .map(|s| s.get_name()) + .collect(), + + url: v.distribution.download_url.clone(), + }); + } } - } - Some(versions) - }) - .collect::>() - .into_iter() - .flat_map(|v| v.into_iter()) - .collect::>(); + Some(versions) + }) + .collect::>() + .into_iter() + .flat_map(|v| v.into_iter()) + .collect::>(); - let queried_package = available_packages.iter() - .filter_map(|v| { - if name.contains("/") && v.package != name { - return None; - } + let queried_package = available_packages + .iter() + .filter_map(|v| { + if name.contains("/") && v.package != name { + return None; + } - if version.is_some() && v.version != version.clone().unwrap() { - return None; - } + if version.is_some() && v.version != version.clone().unwrap() { + return None; + } - Some(v) - }).next().cloned(); + Some(v) + }) + .next() + .cloned(); match queried_package { - None => Err((available_packages, format!("No package found for {name}@{}", version.unwrap_or("latest")))), + None => Err(( + available_packages, + format!( + "No package found for {name}@{}", + version.unwrap_or("latest") + ), + )), Some(s) => Ok(s), } } @@ -634,18 +650,18 @@ pub fn query_package_from_registry( /// Returs the path to the directory where all packages on this computer are being stored pub fn get_global_install_dir(registry_host: &str) -> Option { Some( - PartialWapmConfig::get_folder().ok()? + PartialWapmConfig::get_folder() + .ok()? .join("checkouts") .join(registry_host), ) } pub fn download_and_unpack_targz(url: &str, target_path: &Path) -> Result { - let target_targz_path = target_path.to_path_buf().join("package.tar.gz"); - let mut resp = reqwest::blocking::get(url) - .map_err(|e| format!("failed to download {url}: {e}"))?; + let mut resp = + reqwest::blocking::get(url).map_err(|e| format!("failed to download {url}: {e}"))?; if !target_targz_path.exists() { // create all the parent paths, only remove the created directory, not the parent dirs @@ -654,22 +670,29 @@ pub fn download_and_unpack_targz(url: &str, target_path: &Path) -> Result Result) -> Result { - let registries = get_all_available_registries()?; let mut url_of_package = None; let mut error_packages = Vec::new(); - + for r in registries.iter() { let registry_test = test_if_registry_present(r); if !registry_test.clone().unwrap_or(false) { @@ -691,88 +713,93 @@ pub fn install_package(name: &str, version: Option<&str>) -> Result { url_of_package = Some((r, o)); break; - }, + } Err(e) => { error_packages.push(e); - }, + } } } - + let version_str = match version { None => format!("{name}"), Some(v) => format!("{name}@{v}"), }; - + let registries_searched = registries .iter() .filter_map(|s| url::Url::parse(s).ok()) .filter_map(|s| Some(format!("{}", s.host_str()?))) .collect::>(); - let did_you_mean = error_packages.iter() - .flat_map(|(packages, _)| { - packages.iter().filter_map(|f| { - let from = url::Url::parse(&f.registry).ok()?.host_str()?.to_string(); - Some(format!(" {}@{} (from {from})", f.package, f.version)) + let did_you_mean = error_packages + .iter() + .flat_map(|(packages, _)| { + packages.iter().filter_map(|f| { + let from = url::Url::parse(&f.registry).ok()?.host_str()?.to_string(); + Some(format!(" {}@{} (from {from})", f.package, f.version)) + }) }) - }).collect::>() - .join("\r\n"); - + .collect::>() + .join("\r\n"); + let (_, package_info) = url_of_package .ok_or(format!("Package {version_str} not found in registries: {registries_searched:?}.\r\n\r\nDid you mean:\r\n{did_you_mean}\r\n"))?; let host = url::Url::parse(&package_info.url) - .map_err(|e| format!("invalid url: {}: {e}", package_info.url))? - .host_str() - .ok_or(format!("invalid url: {}", package_info.url))? - .to_string(); + .map_err(|e| format!("invalid url: {}: {e}", package_info.url))? + .host_str() + .ok_or(format!("invalid url: {}", package_info.url))? + .to_string(); - let dir = get_package_local_dir( - &host, - &package_info.package, - &package_info.version - )?; + let dir = get_package_local_dir(&host, &package_info.package, &package_info.version)?; let version = package_info.version; let name = package_info.package; let target_path = download_and_unpack_targz(&package_info.url, &dir)?; - let wapm_toml = std::fs::read_to_string(target_path.join("wapm.toml")) - .map_err(|_| format!("Package {name}@{version} has no wapm.toml (path: {})", target_path.display()))?; + let wapm_toml = std::fs::read_to_string(target_path.join("wapm.toml")).map_err(|_| { + format!( + "Package {name}@{version} has no wapm.toml (path: {})", + target_path.display() + ) + })?; let wapm_toml = toml::from_str::(&wapm_toml) - .map_err(|e| format!("Could not parse toml for {name}@{version}: {e}"))?; + .map_err(|e| format!("Could not parse toml for {name}@{version}: {e}"))?; let commands = wapm_toml.command.unwrap_or_default(); - let entrypoint_module = commands.first() - .ok_or(format!("Cannot run {name}@{version}: package has no commands"))?; - + let entrypoint_module = commands.first().ok_or(format!( + "Cannot run {name}@{version}: package has no commands" + ))?; + let module_name = entrypoint_module.get_module(); let modules = wapm_toml.module.unwrap_or_default(); let entrypoint_module = modules - .iter() - .filter(|m| m.name == module_name) - .next() - .ok_or(format!("Cannot run {name}@{version}: module {module_name} not found in wapm.toml"))?; - + .iter() + .filter(|m| m.name == module_name) + .next() + .ok_or(format!( + "Cannot run {name}@{version}: module {module_name} not found in wapm.toml" + ))?; + Ok(target_path.join(&entrypoint_module.source)) } pub fn test_if_registry_present(registry: &str) -> Result { - + use crate::graphql::{test_if_registry_present, TestIfRegistryPresent}; use graphql_client::GraphQLQuery; use std::time::Duration; - use crate::graphql::{TestIfRegistryPresent, test_if_registry_present}; let q = TestIfRegistryPresent::build_query(test_if_registry_present::Variables {}); let _ = crate::graphql::execute_query_modifier_inner_check_json( - registry, - "", - &q, - Some(Duration::from_secs(1)), - |f| f - ).map_err(|e| format!("{e}"))?; + registry, + "", + &q, + Some(Duration::from_secs(1)), + |f| f, + ) + .map_err(|e| format!("{e}"))?; Ok(true) } @@ -783,12 +810,12 @@ pub fn get_all_available_registries() -> Result, String> { match config.registry { Registries::Single(s) => { registries.push(format_graphql(&s.url)); - }, + } Registries::Multi(m) => { for key in m.tokens.keys() { registries.push(format_graphql(&key)); } - } + } } Ok(registries) }