From f216a69346f746b4282af70593ab9417b9e082af Mon Sep 17 00:00:00 2001 From: Miles Johnson Date: Mon, 12 Aug 2024 19:14:09 -0400 Subject: [PATCH] deps(schematic): Upgrade to v0.17. (#586) --- CHANGELOG.md | 7 ++ Cargo.lock | 26 ++-- Cargo.toml | 2 +- crates/cli/src/printer.rs | 16 +-- crates/cli/tests/plugin_add_test.rs | 10 +- crates/cli/tests/plugin_remove_test.rs | 10 +- crates/cli/tests/plugins_test.rs | 19 +-- crates/codegen/Cargo.toml | 1 + crates/core/Cargo.toml | 8 +- crates/core/src/lib.rs | 1 + crates/core/src/proto_config.rs | 73 +++++------ crates/core/tests/proto_config_test.rs | 49 +++----- crates/pdk-api/Cargo.toml | 2 +- crates/pdk-api/src/api/mod.rs | 9 ++ crates/warpgate-api/Cargo.toml | 2 +- crates/warpgate-api/src/lib.rs | 2 + crates/warpgate-api/src/locator.rs | 86 ++++++++----- crates/warpgate-api/src/locator_error.rs | 20 +++ crates/warpgate-api/tests/locator_test.rs | 54 ++++---- crates/warpgate/src/client.rs | 8 +- crates/warpgate/src/endpoints.rs | 24 ++++ crates/warpgate/src/error.rs | 12 +- crates/warpgate/src/host_funcs.rs | 4 +- crates/warpgate/src/id.rs | 2 +- crates/warpgate/src/lib.rs | 4 +- crates/warpgate/src/loader.rs | 145 +++++++++++----------- crates/warpgate/src/plugin.rs | 12 +- crates/warpgate/tests/loader_test.rs | 25 ++-- 28 files changed, 355 insertions(+), 278 deletions(-) create mode 100644 crates/warpgate-api/src/locator_error.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 432ecb0b7..8168a7253 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,13 @@ - [Rust](https://github.com/moonrepo/tools/blob/master/tools/rust/CHANGELOG.md) - [TOML schema](https://github.com/moonrepo/tools/blob/master/tools/internal-schema/CHANGELOG.md) +## Unreleased + +#### 🚀 Updates + +- WASM API + - Added `ToolMetadataOutput.config_schema`, which can be used to define a JSON schema for the plugins configuration. + ## 0.39.5 #### 💥 Breaking diff --git a/Cargo.lock b/Cargo.lock index 83b2dc508..3cbae1495 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -346,9 +346,9 @@ dependencies = [ [[package]] name = "castaway" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a17ed5635fc8536268e5d4de1e22e81ac34419e5f052d4d51f4e01dcc263fcc" +checksum = "0abae9be0aaf9ea96a3b1b8b1b55c602ca751eba1b1500220cea4ecbafe7c0d5" dependencies = [ "rustversion", ] @@ -2860,9 +2860,9 @@ dependencies = [ [[package]] name = "schematic" -version = "0.16.6" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39662af6f427584874872ecc0e42888bccd0c42510d32d519c4ac9319f4c52f3" +checksum = "4952b2fab03cea740a66c0ca502d16f31ad5d3cc0a803541778f20715ebd0088" dependencies = [ "garde", "indexmap 2.2.6", @@ -2882,9 +2882,9 @@ dependencies = [ [[package]] name = "schematic_macros" -version = "0.16.6" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9747db30b2549f7a3d5c5c61e00a4da45d6fe7f4a413594ffc5e598f5ced1330" +checksum = "4568cc8e3cc136c4231041062419bebcf69f13e8c05fb34d4c715cc5c33ebbc4" dependencies = [ "convert_case", "darling", @@ -2895,12 +2895,13 @@ dependencies = [ [[package]] name = "schematic_types" -version = "0.8.4" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6196a0b23e5b4ee02e3894a937606fc49ccc9211e1650a9bef759062cfff3d58" +checksum = "9939a1953cd54de031828aabb6b9e37191c45328c0620f7286ccc76a35a6ac05" dependencies = [ "indexmap 2.2.6", "semver", + "serde", "serde_json", "toml 0.8.16", "url", @@ -2966,12 +2967,13 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.120" +version = "1.0.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5" +checksum = "784b6203951c57ff748476b126ccb5e8e2959a5c19e5c617ab1956be3dbc68da" dependencies = [ "indexmap 2.2.6", "itoa", + "memchr", "ryu", "serde", ] @@ -3205,9 +3207,9 @@ dependencies = [ [[package]] name = "starbase_styles" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcdc25102288ba49a8ae037a3de74dde8bcd519dfe00dc00dae4ed7344475983" +checksum = "44854a14e28e3b1d602d802576162380504df73efae50d4b901934d25579524b" dependencies = [ "dirs 5.0.1", "miette", diff --git a/Cargo.toml b/Cargo.toml index a7ce3b489..d91da7c99 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ reqwest = { version = "0.12.5", default-features = false, features = [ "macos-system-configuration", ] } rustc-hash = "2.0.0" -schematic = { version = "0.16.6", default-features = false } +schematic = { version = "0.17.1", default-features = false } semver = { version = "1.0.23", features = ["serde"] } serde = { version = "1.0.204", features = ["derive"] } serde_json = "1.0.120" diff --git a/crates/cli/src/printer.rs b/crates/cli/src/printer.rs index 05189a12a..90402a199 100644 --- a/crates/cli/src/printer.rs +++ b/crates/cli/src/printer.rs @@ -170,21 +170,23 @@ impl<'std> Printer<'std> { pub fn locator>(&mut self, locator: L) { match locator.as_ref() { - PluginLocator::File { path, .. } => { - self.entry( - "File", - color::path(path.as_ref().unwrap().canonicalize().unwrap()), - ); + PluginLocator::File(file) => { + self.entry("File", color::path(file.get_resolved_path())); } PluginLocator::GitHub(github) => { self.entry("GitHub", color::label(&github.repo_slug)); + + if let Some(name) = &github.project_name { + self.entry("Project", color::label(name)); + } + self.entry( "Tag", color::hash(github.tag.as_deref().unwrap_or("latest")), ); } - PluginLocator::Url { url } => { - self.entry("URL", color::url(url)); + PluginLocator::Url(url) => { + self.entry("URL", color::url(&url.url)); } }; } diff --git a/crates/cli/tests/plugin_add_test.rs b/crates/cli/tests/plugin_add_test.rs index b10e28614..2580a9f2e 100644 --- a/crates/cli/tests/plugin_add_test.rs +++ b/crates/cli/tests/plugin_add_test.rs @@ -1,6 +1,6 @@ mod utils; -use proto_core::PluginLocator; +use proto_core::{warpgate::UrlLocator, PluginLocator}; use starbase_sandbox::predicates::prelude::*; use utils::*; @@ -45,11 +45,11 @@ mod plugin_add { assert_eq!( config.plugins.get("id").unwrap(), - &PluginLocator::Url { + &PluginLocator::Url(Box::new(UrlLocator { url: "https://github.com/moonrepo/tools/releases/latest/download/example_plugin.wasm" .into() - } + })) ); } @@ -76,11 +76,11 @@ mod plugin_add { assert_eq!( config.plugins.get("id").unwrap(), - &PluginLocator::Url { + &PluginLocator::Url(Box::new(UrlLocator { url: "https://github.com/moonrepo/tools/releases/latest/download/example_plugin.wasm" .into() - } + })) ); } } diff --git a/crates/cli/tests/plugin_remove_test.rs b/crates/cli/tests/plugin_remove_test.rs index 2e26ced0c..1cadc235e 100644 --- a/crates/cli/tests/plugin_remove_test.rs +++ b/crates/cli/tests/plugin_remove_test.rs @@ -1,6 +1,6 @@ mod utils; -use proto_core::{Id, PluginLocator, ProtoConfig}; +use proto_core::{warpgate::UrlLocator, Id, PluginLocator, ProtoConfig}; use starbase_sandbox::predicates::prelude::*; use utils::*; @@ -30,9 +30,9 @@ mod plugin_remove { .get_or_insert(Default::default()) .insert( Id::raw("id"), - PluginLocator::Url { + PluginLocator::Url(Box::new(UrlLocator { url: "https://github.com/moonrepo/tools/releases/latest/download/example_plugin.wasm".into() - }, + })), ); }) .unwrap(); @@ -58,9 +58,9 @@ mod plugin_remove { .get_or_insert(Default::default()) .insert( Id::raw("id"), - PluginLocator::Url { + PluginLocator::Url(Box::new(UrlLocator { url: "https://github.com/moonrepo/tools/releases/latest/download/example_plugin.wasm".into() - }, + })), ); }) .unwrap(); diff --git a/crates/cli/tests/plugins_test.rs b/crates/cli/tests/plugins_test.rs index 3fd5ec318..2c0cbca3a 100644 --- a/crates/cli/tests/plugins_test.rs +++ b/crates/cli/tests/plugins_test.rs @@ -1,7 +1,8 @@ mod utils; use proto_core::{ - load_tool_from_locator, Id, PluginLocator, ProtoEnvironment, Tool, UnresolvedVersionSpec, + load_tool_from_locator, warpgate::FileLocator, warpgate::UrlLocator, Id, PluginLocator, + ProtoEnvironment, Tool, UnresolvedVersionSpec, }; use starbase_sandbox::assert_snapshot; use starbase_sandbox::predicates::prelude::*; @@ -53,10 +54,10 @@ mod plugins { load_tool_from_locator( Id::raw("moon"), env.to_owned(), - PluginLocator::File { + PluginLocator::File(Box::new(FileLocator { file: "./tests/fixtures/moon-schema.toml".into(), path: Some(root_dir.join("./tests/fixtures/moon-schema.toml")), - }, + })), ) }) .await; @@ -71,10 +72,10 @@ mod plugins { load_tool_from_locator( Id::raw("moon"), env.to_owned(), - PluginLocator::File { + PluginLocator::File(Box::new(FileLocator { file: "./some/fake/path.toml".into(), path: Some(root_dir.join("./some/fake/path.toml")), - }, + })), ) }) .await; @@ -86,10 +87,10 @@ mod plugins { load_tool_from_locator( Id::raw("moon"), env.to_owned(), - PluginLocator::Url { + PluginLocator::Url(Box::new(UrlLocator { url: "https://raw.githubusercontent.com/moonrepo/moon/master/proto-plugin.toml" .into(), - }, + })), ) }) .await; @@ -102,10 +103,10 @@ mod plugins { load_tool_from_locator( Id::raw("moon"), env.to_owned(), - PluginLocator::Url { + PluginLocator::Url(Box::new(UrlLocator { url: "https://raw.githubusercontent.com/moonrepo/moon/some/fake/path.toml" .into(), - }, + })), ) }) .await; diff --git a/crates/codegen/Cargo.toml b/crates/codegen/Cargo.toml index c584a6998..0b4a0abec 100644 --- a/crates/codegen/Cargo.toml +++ b/crates/codegen/Cargo.toml @@ -14,6 +14,7 @@ proto_pdk_api = { version = "0.22.0", path = "../pdk-api", features = [ "schematic", ] } schematic = { workspace = true, features = [ + "schema", "renderer_json_schema", "renderer_typescript", ] } diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index 15c5de5ef..5af0b7914 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -8,7 +8,9 @@ homepage = "https://moonrepo.dev/proto" repository = "https://github.com/moonrepo/proto" [dependencies] -proto_pdk_api = { version = "0.22.0", path = "../pdk-api" } +proto_pdk_api = { version = "0.22.0", path = "../pdk-api", features = [ + "schematic", +] } proto_shim = { version = "0.4.3", path = "../shim" } version_spec = { version = "0.6.1", path = "../version-spec", features = [ "schematic", @@ -25,11 +27,11 @@ reqwest = { workspace = true } rustc-hash = { workspace = true } schematic = { workspace = true, features = [ "config", + "env", "toml", "type_indexmap", - "type_serde_json", - "type_serde_toml", "type_url", + "validate", ] } semver = { workspace = true } serde = { workspace = true } diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index 6d9569f74..af103c219 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -25,4 +25,5 @@ pub use version_spec::*; // Only export things consumers will actually need! pub use semver::{Version, VersionReq}; +pub use warpgate; pub use warpgate::{Id, PluginLocator}; diff --git a/crates/core/src/proto_config.rs b/crates/core/src/proto_config.rs index e49e02947..eece60389 100644 --- a/crates/core/src/proto_config.rs +++ b/crates/core/src/proto_config.rs @@ -4,7 +4,7 @@ use once_cell::sync::OnceCell; use rustc_hash::FxHashMap; use schematic::{ derive_enum, env, merge, Config, ConfigEnum, ConfigError, ConfigLoader, DefaultValueResult, - Format, HandlerError, MergeResult, PartialConfig, ValidateError, ValidateErrorType, + Format, MergeError, MergeResult, PartialConfig, Path as ErrorPath, ValidateError, ValidateResult, ValidatorError, }; use serde::{Deserialize, Serialize}; @@ -19,7 +19,7 @@ use std::path::{Path, PathBuf}; use std::sync::Arc; use tracing::{debug, instrument, trace}; use version_spec::*; -use warpgate::{HttpOptions, Id, PluginLocator}; +use warpgate::{HttpOptions, Id, PluginLocator, UrlLocator}; pub const PROTO_CONFIG_NAME: &str = ".prototools"; pub const SCHEMA_PLUGIN_KEY: &str = "internal-schema"; @@ -33,7 +33,7 @@ fn merge_tools( prev.entry(key) .or_default() .merge(context, value) - .map_err(HandlerError::new)?; + .map_err(MergeError::new)?; } Ok(Some(prev)) @@ -221,36 +221,36 @@ impl ProtoConfig { if !self.plugins.contains_key("bun") && is_allowed("bun") { self.plugins.insert( Id::raw("bun"), - PluginLocator::Url { + PluginLocator::Url(Box::new(UrlLocator { url: "https://github.com/moonrepo/tools/releases/download/bun_tool-v0.12.3/bun_tool.wasm".into() - } + })) ); } if !self.plugins.contains_key("deno") && is_allowed("deno") { self.plugins.insert( Id::raw("deno"), - PluginLocator::Url { + PluginLocator::Url(Box::new(UrlLocator { url: "https://github.com/moonrepo/tools/releases/download/deno_tool-v0.11.4/deno_tool.wasm".into() - } + })) ); } if !self.plugins.contains_key("go") && is_allowed("go") { self.plugins.insert( Id::raw("go"), - PluginLocator::Url { + PluginLocator::Url(Box::new(UrlLocator { url: "https://github.com/moonrepo/tools/releases/download/go_tool-v0.12.0/go_tool.wasm".into() - } + })) ); } if !self.plugins.contains_key("node") && is_allowed("node") { self.plugins.insert( Id::raw("node"), - PluginLocator::Url { + PluginLocator::Url(Box::new(UrlLocator { url: "https://github.com/moonrepo/tools/releases/download/node_tool-v0.11.8/node_tool.wasm".into() - } + })) ); } @@ -258,9 +258,9 @@ impl ProtoConfig { if !self.plugins.contains_key(depman) && is_allowed(depman) { self.plugins.insert( Id::raw(depman), - PluginLocator::Url { + PluginLocator::Url(Box::new(UrlLocator { url: "https://github.com/moonrepo/tools/releases/download/node_depman_tool-v0.12.0/node_depman_tool.wasm".into() - } + })) ); } } @@ -268,27 +268,27 @@ impl ProtoConfig { if !self.plugins.contains_key("python") && is_allowed("python") { self.plugins.insert( Id::raw("python"), - PluginLocator::Url { + PluginLocator::Url(Box::new(UrlLocator { url: "https://github.com/moonrepo/tools/releases/download/python_tool-v0.10.5/python_tool.wasm".into() - } + })) ); } if !self.plugins.contains_key("rust") && is_allowed("rust") { self.plugins.insert( Id::raw("rust"), - PluginLocator::Url { + PluginLocator::Url(Box::new(UrlLocator { url: "https://github.com/moonrepo/tools/releases/download/rust_tool-v0.10.6/rust_tool.wasm".into() - } + })) ); } if !self.plugins.contains_key(SCHEMA_PLUGIN_KEY) { self.plugins.insert( Id::raw(SCHEMA_PLUGIN_KEY), - PluginLocator::Url { + PluginLocator::Url(Box::new(UrlLocator { url: "https://github.com/moonrepo/tools/releases/download/schema_tool-v0.14.1/schema_tool.wasm".into() - } + })) ); } } @@ -333,21 +333,19 @@ impl ProtoConfig { .code(config_content, Format::Toml)? .load_partial(&())?; - config - .validate(&(), true) - .map_err(|error| ConfigError::Validator { - config: config_path.to_string(), - error: Box::new(error), + config.validate(&(), true).map_err(|error| match error { + ConfigError::Validator { error, .. } => ConfigError::Validator { + location: config_path.to_string(), + error, help: Some(color::muted_light("https://moonrepo.dev/docs/proto/config")), - })?; + }, + _ => error, + })?; // Because of serde flatten, unknown and invalid fields // do not trigger validation, so we need to manually handle it if let Some(fields) = &config.unknown { - let mut error = ValidatorError { - path: schematic::Path::new(vec![]), - errors: vec![], - }; + let mut error = ValidatorError { errors: vec![] }; for (field, value) in fields { // Versions show up in both flattened maps... @@ -368,15 +366,15 @@ impl ProtoConfig { } }; - error.errors.push(ValidateErrorType::setting( - error.path.join_key(field), - ValidateError::new(message), + error.errors.push(ValidateError::with_path( + message, + ErrorPath::default().join_key(field), )); } if !error.errors.is_empty() { return Err(ConfigError::Validator { - config: config_path.to_string(), + location: config_path.to_string(), error: Box::new(error), help: Some(color::muted_light("https://moonrepo.dev/docs/proto/config")), } @@ -397,13 +395,8 @@ impl ProtoConfig { if let Some(plugins) = &mut config.plugins { for locator in plugins.values_mut() { - if let PluginLocator::File { - file, - path: ref mut source_path, - .. - } = locator - { - let _ = source_path.insert(make_absolute(&PathBuf::from(&file))); + if let PluginLocator::File(ref mut inner) = locator { + inner.path = Some(make_absolute(&inner.get_unresolved_path())); } } } diff --git a/crates/core/tests/proto_config_test.rs b/crates/core/tests/proto_config_test.rs index b531ffe08..bdb331f24 100644 --- a/crates/core/tests/proto_config_test.rs +++ b/crates/core/tests/proto_config_test.rs @@ -9,7 +9,7 @@ use starbase_utils::json::JsonValue; use std::collections::BTreeMap; use std::env; use version_spec::UnresolvedVersionSpec; -use warpgate::{GitHubLocator, HttpOptions, Id, PluginLocator}; +use warpgate::{FileLocator, GitHubLocator, HttpOptions, Id, PluginLocator, UrlLocator}; fn handle_error(report: miette::Report) { panic!( @@ -42,15 +42,6 @@ mod proto_config { handle_error(ProtoConfig::load_from(sandbox.path(), false).unwrap_err()); } - #[test] - #[should_panic(expected = "must be a valid kebab-case string.")] - fn errors_for_non_kebab_id() { - let sandbox = create_empty_sandbox(); - sandbox.create_file(".prototools", "fooBar = \"1.2.3\""); - - handle_error(ProtoConfig::load_from(sandbox.path(), false).unwrap_err()); - } - #[test] #[should_panic(expected = "proto is a reserved keyword, cannot use as a plugin identifier")] fn errors_for_reserved_plugin_words() { @@ -163,9 +154,9 @@ bar = "https://moonrepo.dev/path/file.wasm" BTreeMap::from_iter([ ( Id::raw("bar"), - PluginLocator::Url { + PluginLocator::Url(Box::new(UrlLocator { url: "https://moonrepo.dev/path/file.wasm".into() - } + })) ), ( Id::raw("foo"), @@ -196,10 +187,10 @@ foo = "file://../file.wasm" config.plugins.unwrap(), BTreeMap::from_iter([( Id::raw("foo"), - PluginLocator::File { - file: "../file.wasm".into(), + PluginLocator::File(Box::new(FileLocator { + file: "file://../file.wasm".into(), path: Some(sandbox.path().join("../file.wasm")) - } + })) )]) ); } @@ -262,17 +253,17 @@ kebab-case = "file://./camel.toml" BTreeMap::from_iter([ ( Id::raw("foo"), - PluginLocator::File { - file: "./test.toml".into(), + PluginLocator::File(Box::new(FileLocator { + file: "file://./test.toml".into(), path: Some(sandbox.path().join("./test.toml")) - } + })) ), ( Id::raw("kebab-case"), - PluginLocator::File { - file: "./camel.toml".into(), + PluginLocator::File(Box::new(FileLocator { + file: "file://./camel.toml".into(), path: Some(sandbox.path().join("./camel.toml")) - } + })) ) ]) ); @@ -297,10 +288,10 @@ kebab-case = "file://./camel.toml" plugins.insert( Id::raw("foo"), - PluginLocator::File { + PluginLocator::File(Box::new(FileLocator { file: "./test.toml".into(), path: Some(sandbox.path().join("./test.toml")), - }, + })), ); let path = ProtoConfig::save_to(sandbox.path(), config).unwrap(); @@ -658,18 +649,18 @@ deno = "7.8.9" assert_eq!( config.plugins.get("node").unwrap(), - &PluginLocator::File { - file: "./node.toml".into(), + &PluginLocator::File(Box::new(FileLocator { + file: "file://./node.toml".into(), path: Some(sandbox.path().join("one/two/three/./node.toml")) - } + })) ); assert_eq!( config.plugins.get("bun").unwrap(), - &PluginLocator::File { - file: "../bun.wasm".into(), + &PluginLocator::File(Box::new(FileLocator { + file: "file://../bun.wasm".into(), path: Some(sandbox.path().join("one/two/../bun.wasm")) - } + })) ); } diff --git a/crates/pdk-api/Cargo.toml b/crates/pdk-api/Cargo.toml index 444179dd2..1cc1b86a8 100644 --- a/crates/pdk-api/Cargo.toml +++ b/crates/pdk-api/Cargo.toml @@ -14,8 +14,8 @@ warpgate_api = { version = "0.9.0", path = "../warpgate-api" } rustc-hash = { workspace = true } schematic = { workspace = true, optional = true, features = [ "schema", + "schema_serde", "type_semver", - "type_serde_json", ] } semver = { workspace = true } serde = { workspace = true } diff --git a/crates/pdk-api/src/api/mod.rs b/crates/pdk-api/src/api/mod.rs index 55894efd6..a638dae5e 100644 --- a/crates/pdk-api/src/api/mod.rs +++ b/crates/pdk-api/src/api/mod.rs @@ -36,6 +36,7 @@ api_enum!( Language, DependencyManager, CLI, + VersionManager, } ); @@ -69,6 +70,14 @@ api_struct!( api_struct!( /// Output returned by the `register_tool` function. pub struct ToolMetadataOutput { + /// Schema shape of the tool's configuration. + // #[cfg(feature = "schematic")] + // #[cfg_attr( + // feature = "schematic", + // serde(default, skip_serializing_if = "Option::is_none") + // )] + // pub config_schema: Option, + /// Default alias or version to use as a fallback. #[serde(default, skip_serializing_if = "Option::is_none")] pub default_version: Option, diff --git a/crates/warpgate-api/Cargo.toml b/crates/warpgate-api/Cargo.toml index 6f41021fb..b36eaddf4 100644 --- a/crates/warpgate-api/Cargo.toml +++ b/crates/warpgate-api/Cargo.toml @@ -11,7 +11,7 @@ repository = "https://github.com/moonrepo/proto" system_env = { version = "0.5.0", path = "../system-env" } anyhow = { workspace = true } rustc-hash = { workspace = true } -schematic = { workspace = true, optional = true, features = ["schema"] } +schematic = { workspace = true, optional = true, features = ["schema", "json"] } serde = { workspace = true } serde_json = { workspace = true } thiserror = { workspace = true } diff --git a/crates/warpgate-api/src/lib.rs b/crates/warpgate-api/src/lib.rs index 84f95aac9..1928271b5 100644 --- a/crates/warpgate-api/src/lib.rs +++ b/crates/warpgate-api/src/lib.rs @@ -1,12 +1,14 @@ mod host; mod host_funcs; mod locator; +mod locator_error; mod virtual_path; pub use anyhow::anyhow; pub use host::*; pub use host_funcs::*; pub use locator::*; +pub use locator_error::*; pub use virtual_path::*; /// Wrap a struct with common derives and serde required attributes. diff --git a/crates/warpgate-api/src/locator.rs b/crates/warpgate-api/src/locator.rs index 9252f6d5a..94c08c32e 100644 --- a/crates/warpgate-api/src/locator.rs +++ b/crates/warpgate-api/src/locator.rs @@ -1,8 +1,42 @@ +use crate::locator_error::PluginLocatorError; use serde::{Deserialize, Serialize}; use std::fmt::Display; use std::path::PathBuf; use std::str::FromStr; +/// A file system locator. +#[derive(Clone, Debug, Default, Eq, PartialEq)] +pub struct FileLocator { + /// Path explicitly configured by a user (with file://). + pub file: String, + + /// The file (above) resolved to an absolute path. + /// This must be done manually on the host side. + pub path: Option, +} + +#[cfg(not(target_arch = "wasm32"))] +impl FileLocator { + pub fn get_unresolved_path(&self) -> PathBuf { + PathBuf::from(self.file.strip_prefix("file://").unwrap_or(&self.file)) + } + + pub fn get_resolved_path(&self) -> PathBuf { + let mut path = self + .path + .clone() + .unwrap_or_else(|| self.get_unresolved_path()); + + if !path.is_absolute() { + path = std::env::current_dir() + .expect("Could not determine working directory!") + .join(path); + } + + path + } +} + /// A GitHub release locator. #[derive(Clone, Debug, Default, Eq, PartialEq)] pub struct GitHubLocator { @@ -16,23 +50,11 @@ pub struct GitHubLocator { pub project_name: Option, } -/// Errors during plugin locator parsing. -#[derive(thiserror::Error, Debug)] -pub enum PluginLocatorError { - #[error("GitHub release locator requires a repository with organization scope (org/repo).")] - GitHubMissingOrg, - - #[error("Missing plugin location (after protocol).")] - MissingLocation, - - #[error("Missing plugin protocol.")] - MissingProtocol, - - #[error("Only https URLs are supported for plugins.")] - SecureUrlsOnly, - - #[error("Unknown plugin protocol `{0}`.")] - UnknownProtocol(String), +/// A HTTPS URL locator. +#[derive(Clone, Debug, Default, Eq, PartialEq)] +pub struct UrlLocator { + /// URL explicitly configured by a user (with https://). + pub url: String, } /// Strategies and protocols for locating plugins. @@ -41,12 +63,7 @@ pub enum PluginLocatorError { pub enum PluginLocator { /// file:///abs/path/to/file.wasm /// file://../rel/path/to/file.wasm - File { - /// Configured path (without file://). - file: String, - /// Resolved absolute path. - path: Option, - }, + File(Box), /// github://owner/repo /// github://owner/repo@tag @@ -54,10 +71,7 @@ pub enum PluginLocator { GitHub(Box), /// https://url/to/file.wasm - Url { - /// Configured URL (with https://). - url: String, - }, + Url(Box), } #[cfg(feature = "schematic")] @@ -75,8 +89,14 @@ impl schematic::Schematic for PluginLocator { impl Display for PluginLocator { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - PluginLocator::File { file, .. } => write!(f, "file://{}", file), - PluginLocator::Url { url } => write!(f, "{}", url), + PluginLocator::File(file) => { + if file.file.starts_with("file://") { + write!(f, "{}", file.file) + } else { + write!(f, "file://{}", file.file) + } + } + PluginLocator::Url(url) => write!(f, "{}", url.url), PluginLocator::GitHub(github) => write!( f, "github://{}{}{}", @@ -138,10 +158,10 @@ impl TryFrom for PluginLocator { } match protocol { - "file" => Ok(PluginLocator::File { - file: location.to_owned(), + "file" => Ok(PluginLocator::File(Box::new(FileLocator { + file: value, path: None, - }), + }))), "github" => { if !location.contains('/') { return Err(PluginLocatorError::GitHubMissingOrg); @@ -166,7 +186,7 @@ impl TryFrom for PluginLocator { Ok(PluginLocator::GitHub(Box::new(github))) } "http" => Err(PluginLocatorError::SecureUrlsOnly), - "https" => Ok(PluginLocator::Url { url: value }), + "https" => Ok(PluginLocator::Url(Box::new(UrlLocator { url: value }))), unknown => Err(PluginLocatorError::UnknownProtocol(unknown.to_owned())), } } diff --git a/crates/warpgate-api/src/locator_error.rs b/crates/warpgate-api/src/locator_error.rs new file mode 100644 index 000000000..829823354 --- /dev/null +++ b/crates/warpgate-api/src/locator_error.rs @@ -0,0 +1,20 @@ +/// Errors during plugin locator parsing. +#[derive(thiserror::Error, Debug)] +pub enum PluginLocatorError { + #[error( + "GitHub release locator requires a repository name with organization scope (org/repo)." + )] + GitHubMissingOrg, + + #[error("Missing plugin location (after protocol).")] + MissingLocation, + + #[error("Missing plugin protocol. Supports file://, https://, and github://.")] + MissingProtocol, + + #[error("Only secure URLs (https://) are supported for plugins.")] + SecureUrlsOnly, + + #[error("Unknown plugin protocol `{0}`.")] + UnknownProtocol(String), +} diff --git a/crates/warpgate-api/tests/locator_test.rs b/crates/warpgate-api/tests/locator_test.rs index 0ddd4848e..911959efa 100644 --- a/crates/warpgate-api/tests/locator_test.rs +++ b/crates/warpgate-api/tests/locator_test.rs @@ -1,5 +1,5 @@ use std::path::PathBuf; -use warpgate_api::{GitHubLocator, PluginLocator}; +use warpgate_api::{FileLocator, GitHubLocator, PluginLocator, UrlLocator}; mod locator { use super::*; @@ -7,18 +7,18 @@ mod locator { #[test] fn displays_correctly() { assert_eq!( - PluginLocator::File { + PluginLocator::File(Box::new(FileLocator { file: "foo.wasm".into(), path: Some(PathBuf::from("/abs/foo.wasm")), - } + })) .to_string(), "file://foo.wasm" ); assert_eq!( - PluginLocator::Url { + PluginLocator::Url(Box::new(UrlLocator { url: "https://download.com/bar.wasm".into() - } + })) .to_string(), "https://download.com/bar.wasm" ); @@ -101,10 +101,10 @@ mod locator { fn parses_file() { assert_eq!( PluginLocator::try_from("file://file.wasm".to_string()).unwrap(), - PluginLocator::File { - file: "file.wasm".into(), + PluginLocator::File(Box::new(FileLocator { + file: "file://file.wasm".into(), path: None, - } + })) ); } @@ -112,10 +112,10 @@ mod locator { fn parses_file_legacy() { assert_eq!( PluginLocator::try_from("source:file.wasm".to_string()).unwrap(), - PluginLocator::File { - file: "file.wasm".into(), + PluginLocator::File(Box::new(FileLocator { + file: "file://file.wasm".into(), path: None, - } + })) ); } @@ -123,17 +123,17 @@ mod locator { fn parses_file_rel() { assert_eq!( PluginLocator::try_from("file://../file.wasm".to_string()).unwrap(), - PluginLocator::File { - file: "../file.wasm".into(), + PluginLocator::File(Box::new(FileLocator { + file: "file://../file.wasm".into(), path: None, - } + })) ); assert_eq!( PluginLocator::try_from("file://./file.wasm".to_string()).unwrap(), - PluginLocator::File { - file: "./file.wasm".into(), + PluginLocator::File(Box::new(FileLocator { + file: "file://./file.wasm".into(), path: None, - } + })) ); } @@ -141,17 +141,17 @@ mod locator { fn parses_file_rel_legacy() { assert_eq!( PluginLocator::try_from("source:../file.wasm".to_string()).unwrap(), - PluginLocator::File { - file: "../file.wasm".into(), + PluginLocator::File(Box::new(FileLocator { + file: "file://../file.wasm".into(), path: None, - } + })) ); assert_eq!( PluginLocator::try_from("source:./file.wasm".to_string()).unwrap(), - PluginLocator::File { - file: "./file.wasm".into(), + PluginLocator::File(Box::new(FileLocator { + file: "file://./file.wasm".into(), path: None, - } + })) ); } } @@ -252,9 +252,9 @@ mod locator { fn parses_url() { assert_eq!( PluginLocator::try_from("https://domain.com/file.wasm".to_string()).unwrap(), - PluginLocator::Url { + PluginLocator::Url(Box::new(UrlLocator { url: "https://domain.com/file.wasm".into() - } + })) ); } @@ -262,9 +262,9 @@ mod locator { fn parses_url_legacy() { assert_eq!( PluginLocator::try_from("source:https://domain.com/file.wasm".to_string()).unwrap(), - PluginLocator::Url { + PluginLocator::Url(Box::new(UrlLocator { url: "https://domain.com/file.wasm".into() - } + })) ); } } diff --git a/crates/warpgate/src/client.rs b/crates/warpgate/src/client.rs index 4032e0ff7..596ef13cf 100644 --- a/crates/warpgate/src/client.rs +++ b/crates/warpgate/src/client.rs @@ -4,12 +4,18 @@ use starbase_utils::fs; use std::path::PathBuf; use tracing::{debug, trace, warn}; +/// Configures the HTTPS client used for making requests. #[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)] #[serde(default, rename_all = "kebab-case")] #[cfg_attr(feature = "schematic", derive(schematic::Schematic))] pub struct HttpOptions { + /// Allow invalid certificates. This is dangerous and should only be used as a last resort! pub allow_invalid_certs: bool, + + /// A list of proxy URLs that all requests should pass through. pub proxies: Vec, + + /// Absolute path to the root certificate. Supports `.pem` and `.der` files. pub root_cert: Option, } @@ -59,7 +65,7 @@ pub fn create_http_client_with_options(options: &HttpOptions) -> miette::Result< } for proxy in &options.proxies { - trace!(proxy = &proxy, "Adding proxy to http client"); + trace!(proxy, "Adding proxy to http client"); if proxy.starts_with("http:") { client = client.proxy(reqwest::Proxy::http(proxy).into_diagnostic()?); diff --git a/crates/warpgate/src/endpoints.rs b/crates/warpgate/src/endpoints.rs index 73139bd10..c10cf00d1 100644 --- a/crates/warpgate/src/endpoints.rs +++ b/crates/warpgate/src/endpoints.rs @@ -1,4 +1,7 @@ +use crate::error::WarpgateError; +use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; +use std::env; #[derive(Debug, Default, Deserialize, Serialize)] pub struct Empty {} @@ -22,3 +25,24 @@ pub struct GitHubApiTag { pub struct GitHubApiRelease { pub assets: Vec, } + +pub async fn send_github_request( + client: &reqwest::Client, + url: &str, +) -> miette::Result { + let mut request = client.get(url).query(&[("per_page", "100")]); + + if let Ok(auth_token) = env::var("GITHUB_TOKEN") { + request = request.bearer_auth(auth_token); + } + + let handle_error = |error: reqwest::Error| WarpgateError::Http { + error: Box::new(error), + url: url.to_owned(), + }; + + let response = request.send().await.map_err(handle_error)?; + let data: T = response.json().await.map_err(handle_error)?; + + Ok(data) +} diff --git a/crates/warpgate/src/error.rs b/crates/warpgate/src/error.rs index e019bb4d7..bba4c6989 100644 --- a/crates/warpgate/src/error.rs +++ b/crates/warpgate/src/error.rs @@ -14,7 +14,6 @@ pub enum WarpgateError { #[error("Failed to make HTTP request for {}.", .url.style(Style::Url))] Http { url: String, - #[source] error: Box, }, @@ -24,7 +23,10 @@ pub enum WarpgateError { InternetConnectionRequired { message: String, url: String }, #[diagnostic(code(plugin::invalid_id))] - #[error("Invalid plugin identifier {}, must be a valid kebab-case string.", .0.style(Style::Id))] + #[error( + "Invalid plugin identifier {}. May only contain letters, numbers, dashes, and underscores.", + .0.style(Style::Id), + )] InvalidID(String), #[diagnostic(code(plugin::source::file_missing))] @@ -37,7 +39,7 @@ pub enum WarpgateError { #[diagnostic(code(plugin::github::asset_missing))] #[error( - "Cannot download {} plugin from GitHub ({}), no tag found or provided.", + "Cannot download {} plugin from GitHub ({}), no tag found, matched, or provided.", .id.style(Style::Id), .repo_slug.style(Style::Id), )] @@ -109,11 +111,11 @@ pub enum WarpgateError { }, #[diagnostic( - code(plugin::download::missing), + code(plugin::download::not_found), help = "Please refer to the plugin's official documentation." )] #[error( - "Plugin download {} does not exist. This version may not be supported for your current operating system or architecture, or the URL is incorrect.", + "Plugin download {} does not exist. Either this version may not be supported for your current operating system or architecture, or the URL is incorrect or malformed.", .url.style(Style::Url), )] DownloadNotFound { url: String }, diff --git a/crates/warpgate/src/host_funcs.rs b/crates/warpgate/src/host_funcs.rs index 41b72b216..e93e3f84b 100644 --- a/crates/warpgate/src/host_funcs.rs +++ b/crates/warpgate/src/host_funcs.rs @@ -176,7 +176,7 @@ fn exec_command( ExecCommandOutput { command: input.command.clone(), - exit_code: result.code().unwrap_or(0), + exit_code: result.code().unwrap_or(-1), stderr: String::new(), stdout: String::new(), } @@ -185,7 +185,7 @@ fn exec_command( ExecCommandOutput { command: input.command.clone(), - exit_code: result.status.code().unwrap_or(0), + exit_code: result.status.code().unwrap_or(-1), stderr: String::from_utf8_lossy(&result.stderr).to_string(), stdout: String::from_utf8_lossy(&result.stdout).to_string(), } diff --git a/crates/warpgate/src/id.rs b/crates/warpgate/src/id.rs index 06e6fc770..f0279ade9 100644 --- a/crates/warpgate/src/id.rs +++ b/crates/warpgate/src/id.rs @@ -5,7 +5,7 @@ use std::sync::LazyLock; use std::{borrow::Borrow, fmt, ops::Deref, str::FromStr}; pub static ID_PATTERN: LazyLock = - LazyLock::new(|| Regex::new("^[a-z][a-z0-9-_]*$").unwrap()); + LazyLock::new(|| Regex::new("^[a-zA-Z][a-zA-Z0-9-_]*$").unwrap()); /// An identifier for plugins. #[derive(Clone, Default, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] diff --git a/crates/warpgate/src/lib.rs b/crates/warpgate/src/lib.rs index 96f7ac242..a966eddb4 100644 --- a/crates/warpgate/src/lib.rs +++ b/crates/warpgate/src/lib.rs @@ -17,4 +17,6 @@ pub use plugin::*; pub use extism::{Manifest as PluginManifest, Wasm}; pub use warpgate_api as api; -pub use warpgate_api::{GitHubLocator, PluginLocator, PluginLocatorError, VirtualPath}; +pub use warpgate_api::{ + FileLocator, GitHubLocator, PluginLocator, PluginLocatorError, UrlLocator, VirtualPath, +}; diff --git a/crates/warpgate/src/loader.rs b/crates/warpgate/src/loader.rs index 151f1f790..619b3a6cc 100644 --- a/crates/warpgate/src/loader.rs +++ b/crates/warpgate/src/loader.rs @@ -6,18 +6,16 @@ use crate::helpers::{ }; use crate::id::Id; use once_cell::sync::OnceCell; -use serde::de::DeserializeOwned; use sha2::{Digest, Sha256}; use starbase_archive::is_supported_archive_extension; use starbase_styles::color; use starbase_utils::fs; -use std::env; use std::fmt::Debug; use std::path::{Path, PathBuf}; use std::sync::Arc; use std::time::{Duration, SystemTime}; use tracing::{instrument, trace}; -use warpgate_api::{GitHubLocator, PluginLocator}; +use warpgate_api::{FileLocator, GitHubLocator, PluginLocator, UrlLocator}; pub type OfflineChecker = Arc bool>; @@ -80,52 +78,75 @@ impl PluginLoader { trace!( id = id.as_str(), + locator = locator.to_string(), "Loading plugin {}", color::id(id.as_str()) ); match locator { - PluginLocator::File { file, path, .. } => { - let path = path.as_ref(); - let path = path - .ok_or_else(|| WarpgateError::SourceFileMissing { - id: id.to_owned(), - path: PathBuf::from(file), - })? - .canonicalize() - .map_err(|_| WarpgateError::SourceFileMissing { - id: id.to_owned(), - path: path.unwrap().to_path_buf(), - })?; - - if path.exists() { - trace!( - id = id.as_str(), - path = ?path, - "Using source file", - ); + PluginLocator::File(file) => self.load_plugin_from_file(id, file).await, + PluginLocator::GitHub(github) => self.load_plugin_from_github(id, github).await, + PluginLocator::Url(url) => self.load_plugin_from_url(id, url).await, + } + } - Ok(path) - } else { - Err(WarpgateError::SourceFileMissing { - id: id.to_owned(), - path: path.to_path_buf(), - } - .into()) - } - } - PluginLocator::Url { url } => { - self.download_plugin( - id, - url, - self.create_cache_path(id, url, url.contains("latest")), - ) - .await + /// Load a plugin from the file system. + #[instrument(skip(self))] + pub async fn load_plugin_from_file + Debug>( + &self, + id: I, + locator: &FileLocator, + ) -> miette::Result { + let id = id.as_ref(); + let path = locator.get_resolved_path(); + + if path.exists() { + trace!( + id = id.as_str(), + path = ?path, + "Using source file", + ); + + Ok(path) + } else { + Err(WarpgateError::SourceFileMissing { + id: id.to_owned(), + path: path.to_path_buf(), } - PluginLocator::GitHub(github) => self.download_plugin_from_github(id, github).await, + .into()) } } + /// Load a plugin from a GitHub release. + #[instrument(skip(self))] + pub async fn load_plugin_from_github + Debug>( + &self, + id: I, + locator: &GitHubLocator, + ) -> miette::Result { + let id = id.as_ref(); + + self.download_plugin_from_github(id, locator).await + } + + /// Load a plugin from a secure URL. + #[instrument(skip(self))] + pub async fn load_plugin_from_url + Debug>( + &self, + id: I, + locator: &UrlLocator, + ) -> miette::Result { + let id = id.as_ref(); + let url = &locator.url; + + self.download_plugin( + id, + url, + self.create_cache_path(id, url, url.contains("latest")), + ) + .await + } + /// Create an absolute path to the plugin's destination file, located in the plugins directory. /// Hash the source URL to ensure uniqueness of each plugin + version combination. pub fn create_cache_path(&self, id: &Id, url: &str, is_latest: bool) -> PathBuf { @@ -159,25 +180,19 @@ impl PluginLoader { return Ok(false); } - let metadata = fs::metadata(path)?; - - let mut cached = if let Ok(filetime) = metadata.created().or_else(|_| metadata.modified()) { - let days = if fs::file_name(path).contains("-latest-") { - 7 - } else { - 30 - }; - - filetime > SystemTime::now() - Duration::from_secs(86400 * days) - } else { - false - }; + let mut cached = fs::is_stale( + path, + false, + Duration::from_secs(86400 * 30), // 30 days + SystemTime::now(), + )? + .is_none(); if !cached && self.is_offline() { cached = true; } - if !cached { + if !cached && path.exists() { fs::remove_file(path)?; } @@ -291,8 +306,7 @@ impl PluginLoader { if let Some(tag) = &github.tag { found_tag = Some(tag.to_owned()) } else if let Some(tag_prefix) = &github.project_name { - found_tag = self - .send_github_request::>(tags_url) + found_tag = send_github_request::>(self.get_client()?, &tags_url) .await? .into_iter() .find(|row| { @@ -331,7 +345,8 @@ impl PluginLoader { "Attempting to download plugin from GitHub release", ); - let release: GitHubApiRelease = self.send_github_request(release_url).await?; + let release: GitHubApiRelease = + send_github_request(self.get_client()?, &release_url).await?; // Find a direct WASM asset first for asset in &release.assets { @@ -375,22 +390,4 @@ impl PluginLoader { } .into()) } - - async fn send_github_request(&self, url: String) -> miette::Result { - let mut request = self.get_client()?.get(&url).query(&[("per_page", "100")]); - - if let Ok(auth_token) = env::var("GITHUB_TOKEN") { - request = request.bearer_auth(auth_token); - } - - let handle_error = |error: reqwest::Error| WarpgateError::Http { - error: Box::new(error), - url: url.clone(), - }; - - let response = request.send().await.map_err(handle_error)?; - let data: T = response.json().await.map_err(handle_error)?; - - Ok(data) - } } diff --git a/crates/warpgate/src/plugin.rs b/crates/warpgate/src/plugin.rs index fee2584a2..6b10ca556 100644 --- a/crates/warpgate/src/plugin.rs +++ b/crates/warpgate/src/plugin.rs @@ -53,7 +53,7 @@ pub fn inject_default_manifest_config( .config .insert("plugin_id".to_string(), id.to_string()); - trace!(env = %env, "Storing host environment"); + trace!(id = id.as_str(), env = %env, "Storing host environment"); manifest.config.insert("host_environment".to_string(), env); @@ -82,6 +82,8 @@ impl PluginContainer { manifest: Manifest, functions: impl IntoIterator, ) -> miette::Result { + trace!(id = id.as_str(), "Creating plugin container"); + let plugin = Plugin::new(&manifest, functions, true).map_err(|error| { if is_incompatible_runtime(&error) { WarpgateError::IncompatibleRuntime { id: id.clone() } @@ -132,11 +134,9 @@ impl PluginContainer { let input = self.format_input(func, input)?; let cache_key = format!("{func}-{input}"); - // Check if cache exists already in read-only mode - { - if let Some(data) = self.func_cache.get(&cache_key) { - return self.parse_output(func, data); - } + // Check if cache exists already + if let Some(data) = self.func_cache.get(&cache_key) { + return self.parse_output(func, data); } // Otherwise call the function and cache the result diff --git a/crates/warpgate/tests/loader_test.rs b/crates/warpgate/tests/loader_test.rs index c96a11796..7d767d996 100644 --- a/crates/warpgate/tests/loader_test.rs +++ b/crates/warpgate/tests/loader_test.rs @@ -1,6 +1,6 @@ use starbase_sandbox::{create_empty_sandbox, locate_fixture, Sandbox}; use std::path::PathBuf; -use warpgate::{GitHubLocator, Id, PluginLoader, PluginLocator}; +use warpgate::{FileLocator, GitHubLocator, Id, PluginLoader, PluginLocator, UrlLocator}; fn create_loader() -> (Sandbox, PluginLoader) { let sandbox = create_empty_sandbox(); @@ -16,17 +16,17 @@ mod loader { use super::*; #[tokio::test] - #[should_panic(expected = "Cannot load test plugin, source file fake-file does not exist.")] + #[should_panic(expected = "Cannot load test plugin, source file")] async fn errors_missing_file() { let (_sandbox, loader) = create_loader(); loader .load_plugin( Id::raw("test"), - PluginLocator::File { + PluginLocator::File(Box::new(FileLocator { file: "".into(), path: Some(PathBuf::from("fake-file")), - }, + })), ) .await .unwrap(); @@ -40,20 +40,15 @@ mod loader { let path = loader .load_plugin( Id::raw("test"), - PluginLocator::File { + PluginLocator::File(Box::new(FileLocator { file: "".into(), path: Some(fixture.join("test.wasm")), - }, + })), ) .await .unwrap(); - // Path is UNC prefixed - if cfg!(windows) { - assert!(path.ends_with("loader\\test.wasm")); - } else { - assert_eq!(path, fixture.join("test.wasm")); - } + assert_eq!(path, fixture.join("test.wasm")); } } @@ -68,7 +63,7 @@ mod loader { loader .load_plugin( Id::raw("test"), - PluginLocator::Url { url: "https://github.com/moonrepo/deno-plugin/releases/download/v0.0.2/deno_plugin_invalid_name.wasm".into() }, + PluginLocator::Url(Box::new(UrlLocator { url: "https://github.com/moonrepo/deno-plugin/releases/download/v0.0.2/deno_plugin_invalid_name.wasm".into() })), ) .await .unwrap(); @@ -81,7 +76,7 @@ mod loader { let path = loader .load_plugin( Id::raw("test"), - PluginLocator::Url { url: "https://github.com/moonrepo/deno-plugin/releases/download/v0.0.2/deno_plugin.wasm".into() }, + PluginLocator::Url(Box::new(UrlLocator { url: "https://github.com/moonrepo/deno-plugin/releases/download/v0.0.2/deno_plugin.wasm".into() })), ) .await .unwrap(); @@ -96,7 +91,7 @@ mod loader { let path = loader .load_plugin( Id::raw("test"), - PluginLocator::Url { url: "https://github.com/moonrepo/deno-plugin/releases/latest/download/deno_plugin.wasm".into() }, + PluginLocator::Url(Box::new(UrlLocator { url: "https://github.com/moonrepo/deno-plugin/releases/latest/download/deno_plugin.wasm".into() })), ) .await .unwrap();