crates/extension_api/src/extension_api.rs 🔗
@@ -1,6 +1,7 @@
//! The Zed Rust Extension API allows you write extensions for [Zed](https://zed.dev/) in Rust.
pub mod http_client;
+pub mod process;
pub mod settings;
use core::fmt;
Marshall Bowers created
This PR adds a simple API for working with processes to the extension
API.
The API is designed to mirror Rust's
[`std::process::Command`](https://doc.rust-lang.org/std/process/struct.Command.html).
Release Notes:
- N/A
crates/extension_api/src/extension_api.rs | 1
crates/extension_api/src/process.rs | 44 +++++++++++
crates/extension_api/wit/since_v0.3.0/common.wit | 3
crates/extension_api/wit/since_v0.3.0/extension.wit | 17 ---
crates/extension_api/wit/since_v0.3.0/process.wit | 29 +++++++
crates/extension_host/src/wasm_host/wit/since_v0_3_0.rs | 29 +++++++
6 files changed, 109 insertions(+), 14 deletions(-)
@@ -1,6 +1,7 @@
//! The Zed Rust Extension API allows you write extensions for [Zed](https://zed.dev/) in Rust.
pub mod http_client;
+pub mod process;
pub mod settings;
use core::fmt;
@@ -0,0 +1,44 @@
+//! A module for working with processes.
+
+use crate::wit::zed::extension::process;
+pub use crate::wit::zed::extension::process::{Command, Output};
+
+impl Command {
+ pub fn new(program: impl Into<String>) -> Self {
+ Self {
+ command: program.into(),
+ args: Vec::new(),
+ env: Vec::new(),
+ }
+ }
+
+ pub fn arg(mut self, arg: impl Into<String>) -> Self {
+ self.args.push(arg.into());
+ self
+ }
+
+ pub fn args(mut self, args: impl IntoIterator<Item = impl Into<String>>) -> Self {
+ self.args.extend(args.into_iter().map(Into::into));
+ self
+ }
+
+ pub fn env(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
+ self.env.push((key.into(), value.into()));
+ self
+ }
+
+ pub fn envs(
+ mut self,
+ envs: impl IntoIterator<Item = (impl Into<String>, impl Into<String>)>,
+ ) -> Self {
+ self.env.extend(
+ envs.into_iter()
+ .map(|(key, value)| (key.into(), value.into())),
+ );
+ self
+ }
+
+ pub fn output(&mut self) -> Result<Output, String> {
+ process::run_command(self)
+ }
+}
@@ -6,4 +6,7 @@ interface common {
/// The end of the range (exclusive).
end: u32,
}
+
+ /// A list of environment variables.
+ type env-vars = list<tuple<string, string>>;
}
@@ -4,10 +4,12 @@ world extension {
import github;
import http-client;
import platform;
+ import process;
import nodejs;
- use common.{range};
+ use common.{env-vars, range};
use lsp.{completion, symbol};
+ use process.{command};
use slash-command.{slash-command, slash-command-argument-completion, slash-command-output};
/// Initializes the extension.
@@ -56,19 +58,6 @@ world extension {
/// Updates the installation status for the given language server.
import set-language-server-installation-status: func(language-server-name: string, status: language-server-installation-status);
- /// A list of environment variables.
- type env-vars = list<tuple<string, string>>;
-
- /// A command.
- record command {
- /// The command to execute.
- command: string,
- /// The arguments to pass to the command.
- args: list<string>,
- /// The environment variables to set for the command.
- env: env-vars,
- }
-
/// A Zed worktree.
resource worktree {
/// Returns the ID of the worktree.
@@ -0,0 +1,29 @@
+interface process {
+ use common.{env-vars};
+
+ /// A command.
+ record command {
+ /// The command to execute.
+ command: string,
+ /// The arguments to pass to the command.
+ args: list<string>,
+ /// The environment variables to set for the command.
+ env: env-vars,
+ }
+
+ /// The output of a finished process.
+ record output {
+ /// The status (exit code) of the process.
+ ///
+ /// On Unix, this will be `None` if the process was terminated by a signal.
+ status: option<s32>,
+ /// The data that the process wrote to stdout.
+ stdout: list<u8>,
+ /// The data that the process wrote to stderr.
+ stderr: list<u8>,
+ }
+
+ /// Executes the given command as a child process, waiting for it to finish
+ /// and collecting all of its output.
+ run-command: func(command: command) -> result<output, string>;
+}
@@ -576,6 +576,35 @@ impl platform::Host for WasmState {
}
}
+impl From<std::process::Output> for process::Output {
+ fn from(output: std::process::Output) -> Self {
+ Self {
+ status: output.status.code(),
+ stdout: output.stdout,
+ stderr: output.stderr,
+ }
+ }
+}
+
+impl process::Host for WasmState {
+ async fn run_command(
+ &mut self,
+ command: process::Command,
+ ) -> wasmtime::Result<Result<process::Output, String>> {
+ maybe!(async {
+ let output = util::command::new_smol_command(command.command.as_str())
+ .args(&command.args)
+ .envs(command.env)
+ .output()
+ .await?;
+
+ Ok(output.into())
+ })
+ .await
+ .to_wasmtime_result()
+ }
+}
+
#[async_trait]
impl slash_command::Host for WasmState {}