diff --git a/Cargo.lock b/Cargo.lock index 4b7d53b8..b6296625 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -540,6 +540,7 @@ dependencies = [ "warg-server", "wasm-metadata", "wasmparser 0.120.0", + "wasmprinter", "wat", "which 6.0.0", "wit-bindgen-core", @@ -3729,6 +3730,16 @@ dependencies = [ "semver", ] +[[package]] +name = "wasmprinter" +version = "0.2.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8389a95eb0b3165fea0537a6988960cc23a33d9be650e63fc3d63065fe20dcb" +dependencies = [ + "anyhow", + "wasmparser 0.120.0", +] + [[package]] name = "wast" version = "70.0.1" diff --git a/Cargo.toml b/Cargo.toml index 81a347e9..a36f6511 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,6 +58,7 @@ wasmparser = { workspace = true } wat = { workspace = true } warg-server = { workspace = true } tempfile = { workspace = true } +wasmprinter = { workspace = true } [workspace] members = ["crates/core", "crates/wit"] @@ -104,3 +105,4 @@ predicates = "3.1.0" wasmparser = "0.120.0" wat = "1.0.84" warg-server = "0.2.0" +wasmprinter = "0.2.77" diff --git a/adapters/ab5a448/wasi_snapshot_preview1.proxy.wasm b/adapters/ab5a448/wasi_snapshot_preview1.proxy.wasm new file mode 100644 index 00000000..5f71e3e1 Binary files /dev/null and b/adapters/ab5a448/wasi_snapshot_preview1.proxy.wasm differ diff --git a/src/commands/new.rs b/src/commands/new.rs index b8912a5d..6d046d37 100644 --- a/src/commands/new.rs +++ b/src/commands/new.rs @@ -47,13 +47,17 @@ pub struct NewCommand { pub vcs: Option, /// Create a CLI command component [default] - #[clap(long = "command", conflicts_with("lib"))] - pub command: bool, + #[clap(long = "bin", alias = "command", conflicts_with = "lib")] + pub bin: bool, /// Create a library (reactor) component #[clap(long = "lib", alias = "reactor")] pub lib: bool, + /// Use the built-in `wasi:http/proxy` module adapter + #[clap(long = "proxy", requires = "lib")] + pub proxy: bool, + /// Edition to set for the generated crate #[clap(long = "edition", value_name = "YEAR", value_parser = ["2015", "2018", "2021"])] pub edition: Option, @@ -277,6 +281,10 @@ impl NewCommand { component["registries"] = Item::Table(table); } + if self.proxy { + component["proxy"] = value(true); + } + let mut metadata = Table::new(); metadata.set_implicit(true); metadata.set_position(doc.len()); @@ -315,7 +323,7 @@ impl NewCommand { } fn is_command(&self) -> bool { - self.command || !self.lib + self.bin || !self.lib } fn generate_source( diff --git a/src/lib.rs b/src/lib.rs index 16f2b483..2f66b13b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -606,8 +606,18 @@ async fn generate_package_bindings( Ok(()) } -fn adapter_bytes(metadata: &ComponentMetadata, binary: bool) -> Result> { +fn adapter_bytes<'a>( + config: &Config, + metadata: &'a ComponentMetadata, + binary: bool, +) -> Result> { if let Some(adapter) = &metadata.section.adapter { + if metadata.section.proxy { + config.terminal().warn( + "ignoring `proxy` setting due to `adapter` setting being present in `Cargo.toml`", + )?; + } + return Ok(fs::read(adapter) .with_context(|| { format!( @@ -619,11 +629,23 @@ fn adapter_bytes(metadata: &ComponentMetadata, binary: bool) -> Result } if binary { + if metadata.section.proxy { + config + .terminal() + .warn("ignoring `proxy` setting in `Cargo.toml` for command component")?; + } + Ok(Cow::Borrowed(include_bytes!(concat!( "../adapters/", env!("WASI_ADAPTER_VERSION"), "/wasi_snapshot_preview1.command.wasm" )))) + } else if metadata.section.proxy { + Ok(Cow::Borrowed(include_bytes!(concat!( + "../adapters/", + env!("WASI_ADAPTER_VERSION"), + "/wasi_snapshot_preview1.proxy.wasm" + )))) } else { Ok(Cow::Borrowed(include_bytes!(concat!( "../adapters/", @@ -663,7 +685,10 @@ fn create_component( let encoder = ComponentEncoder::default() .module(&module)? - .adapter("wasi_snapshot_preview1", &adapter_bytes(metadata, binary)?) + .adapter( + "wasi_snapshot_preview1", + &adapter_bytes(config, metadata, binary)?, + ) .with_context(|| { format!( "failed to load adapter module `{path}`", diff --git a/src/metadata.rs b/src/metadata.rs index df0db26b..4f93d88f 100644 --- a/src/metadata.rs +++ b/src/metadata.rs @@ -280,6 +280,10 @@ pub struct ComponentSection { pub registries: HashMap, /// The configuration for bindings generation. pub bindings: Bindings, + /// Whether to use the built-in `wasi:http/proxy` adapter for the component. + /// + /// This should only be `true` when `adapter` is None. + pub proxy: bool, } /// Represents cargo metadata for a WebAssembly component. diff --git a/tests/build.rs b/tests/build.rs index 03985cd0..0d46470e 100644 --- a/tests/build.rs +++ b/tests/build.rs @@ -834,3 +834,67 @@ fn it_builds_with_versioned_wit() -> Result<()> { Ok(()) } + +#[test] +fn it_warns_on_proxy_setting_for_command() -> Result<()> { + let project = Project::new_bin("foo")?; + project.update_manifest(|mut doc| { + doc["package"]["metadata"]["component"]["proxy"] = value(true); + Ok(doc) + })?; + + project + .cargo_component("build") + .assert() + .stderr(contains( + "warning: ignoring `proxy` setting in `Cargo.toml` for command component", + )) + .success(); + + validate_component(&project.debug_wasm("foo"))?; + + Ok(()) +} + +#[test] +fn it_warns_with_proxy_and_adapter_settings() -> Result<()> { + let project = Project::new("foo")?; + project.update_manifest(|mut doc| { + doc["package"]["metadata"]["component"]["proxy"] = value(true); + doc["package"]["metadata"]["component"]["adapter"] = + value(adapter_path().to_str().unwrap()); + Ok(doc) + })?; + + project + .cargo_component("build") + .assert() + .stderr(contains("warning: ignoring `proxy` setting due to `adapter` setting being present in `Cargo.toml`")) + .success(); + + validate_component(&project.debug_wasm("foo"))?; + + Ok(()) +} + +#[test] +fn it_builds_with_proxy_adapter() -> Result<()> { + let dir = Rc::new(TempDir::new()?); + let project = Project::with_dir(dir.clone(), "foo", "--proxy")?; + + project + .cargo_component("build") + .assert() + .stderr(contains("Finished dev [unoptimized + debuginfo] target(s)")) + .success(); + + validate_component(&project.debug_wasm("foo"))?; + + let text = wasmprinter::print_file(project.debug_wasm("foo"))?; + assert!( + !text.contains("wasi:cli/environment"), + "proxy wasm should have no reference to `wasi:cli/environment`" + ); + + Ok(()) +} diff --git a/tests/new.rs b/tests/new.rs index 9fdd9e73..db4ec058 100644 --- a/tests/new.rs +++ b/tests/new.rs @@ -23,7 +23,7 @@ fn help() { fn it_creates_the_expected_files_for_bin() -> Result<()> { let dir = TempDir::new()?; - cargo_component("new --command foo") + cargo_component("new --bin foo") .current_dir(dir.path()) .assert() .stderr(contains("Updated manifest of package `foo")) @@ -179,6 +179,18 @@ async fn it_errors_if_target_does_not_exist() -> Result<()> { Ok(()) } +#[test] +fn it_supports_the_command_option() -> Result<()> { + let dir = TempDir::new()?; + + cargo_component("new --command foo") + .current_dir(dir.path()) + .assert() + .try_success()?; + + Ok(()) +} + #[test] fn it_supports_the_reactor_option() -> Result<()> { let dir = TempDir::new()?; @@ -190,3 +202,17 @@ fn it_supports_the_reactor_option() -> Result<()> { Ok(()) } + +#[test] +fn it_supports_the_proxy_option() -> Result<()> { + let dir: TempDir = TempDir::new()?; + + cargo_component("new --lib --proxy foo") + .current_dir(dir.path()) + .assert() + .try_success()?; + + assert!(fs::read_to_string(dir.path().join("foo/Cargo.toml"))?.contains("proxy = true")); + + Ok(()) +}