since_v0_6_0.rs

   1use crate::wasm_host::wit::since_v0_6_0::{
   2    dap::{
   3        AttachRequest, BuildTaskDefinition, BuildTaskDefinitionTemplatePayload, LaunchRequest,
   4        StartDebuggingRequestArguments, TcpArguments, TcpArgumentsTemplate,
   5    },
   6    slash_command::SlashCommandOutputSection,
   7};
   8use crate::wasm_host::wit::{CompletionKind, CompletionLabelDetails, InsertTextFormat, SymbolKind};
   9use crate::wasm_host::{WasmState, wit::ToWasmtimeResult};
  10use ::http_client::{AsyncBody, HttpRequestExt};
  11use ::settings::{Settings, WorktreeId};
  12use anyhow::{Context as _, Result, bail};
  13use async_compression::futures::bufread::GzipDecoder;
  14use async_tar::Archive;
  15use async_trait::async_trait;
  16use extension::{
  17    ExtensionLanguageServerProxy, KeyValueStoreDelegate, ProjectDelegate, WorktreeDelegate,
  18};
  19use futures::{AsyncReadExt, lock::Mutex};
  20use futures::{FutureExt as _, io::BufReader};
  21use gpui::{BackgroundExecutor, SharedString};
  22use language::{BinaryStatus, LanguageName, language_settings::AllLanguageSettings};
  23use project::project_settings::ProjectSettings;
  24use semantic_version::SemanticVersion;
  25use std::{
  26    env,
  27    net::Ipv4Addr,
  28    path::{Path, PathBuf},
  29    str::FromStr,
  30    sync::{Arc, OnceLock},
  31};
  32use task::{SpawnInTerminal, ZedDebugConfig};
  33use url::Url;
  34use util::{archive::extract_zip, fs::make_file_executable, maybe};
  35use wasmtime::component::{Linker, Resource};
  36
  37pub const MIN_VERSION: SemanticVersion = SemanticVersion::new(0, 6, 0);
  38pub const MAX_VERSION: SemanticVersion = SemanticVersion::new(0, 7, 0);
  39
  40wasmtime::component::bindgen!({
  41    async: true,
  42    trappable_imports: true,
  43    path: "../extension_api/wit/since_v0.6.0",
  44    with: {
  45         "worktree": ExtensionWorktree,
  46         "project": ExtensionProject,
  47         "key-value-store": ExtensionKeyValueStore,
  48         "zed:extension/http-client/http-response-stream": ExtensionHttpResponseStream
  49    },
  50});
  51
  52pub use self::zed::extension::*;
  53
  54mod settings {
  55    #![allow(dead_code)]
  56    include!(concat!(env!("OUT_DIR"), "/since_v0.6.0/settings.rs"));
  57}
  58
  59pub type ExtensionWorktree = Arc<dyn WorktreeDelegate>;
  60pub type ExtensionProject = Arc<dyn ProjectDelegate>;
  61pub type ExtensionKeyValueStore = Arc<dyn KeyValueStoreDelegate>;
  62pub type ExtensionHttpResponseStream = Arc<Mutex<::http_client::Response<AsyncBody>>>;
  63
  64pub fn linker(executor: &BackgroundExecutor) -> &'static Linker<WasmState> {
  65    static LINKER: OnceLock<Linker<WasmState>> = OnceLock::new();
  66    LINKER.get_or_init(|| super::new_linker(executor, Extension::add_to_linker))
  67}
  68
  69impl From<Range> for std::ops::Range<usize> {
  70    fn from(range: Range) -> Self {
  71        let start = range.start as usize;
  72        let end = range.end as usize;
  73        start..end
  74    }
  75}
  76
  77impl From<Command> for extension::Command {
  78    fn from(value: Command) -> Self {
  79        Self {
  80            command: value.command.into(),
  81            args: value.args,
  82            env: value.env,
  83        }
  84    }
  85}
  86
  87impl From<StartDebuggingRequestArgumentsRequest>
  88    for extension::StartDebuggingRequestArgumentsRequest
  89{
  90    fn from(value: StartDebuggingRequestArgumentsRequest) -> Self {
  91        match value {
  92            StartDebuggingRequestArgumentsRequest::Launch => Self::Launch,
  93            StartDebuggingRequestArgumentsRequest::Attach => Self::Attach,
  94        }
  95    }
  96}
  97impl TryFrom<StartDebuggingRequestArguments> for extension::StartDebuggingRequestArguments {
  98    type Error = anyhow::Error;
  99
 100    fn try_from(value: StartDebuggingRequestArguments) -> Result<Self, Self::Error> {
 101        Ok(Self {
 102            configuration: serde_json::from_str(&value.configuration)?,
 103            request: value.request.into(),
 104        })
 105    }
 106}
 107impl From<TcpArguments> for extension::TcpArguments {
 108    fn from(value: TcpArguments) -> Self {
 109        Self {
 110            host: value.host.into(),
 111            port: value.port,
 112            timeout: value.timeout,
 113        }
 114    }
 115}
 116
 117impl From<extension::TcpArgumentsTemplate> for TcpArgumentsTemplate {
 118    fn from(value: extension::TcpArgumentsTemplate) -> Self {
 119        Self {
 120            host: value.host.map(Ipv4Addr::to_bits),
 121            port: value.port,
 122            timeout: value.timeout,
 123        }
 124    }
 125}
 126
 127impl From<TcpArgumentsTemplate> for extension::TcpArgumentsTemplate {
 128    fn from(value: TcpArgumentsTemplate) -> Self {
 129        Self {
 130            host: value.host.map(Ipv4Addr::from_bits),
 131            port: value.port,
 132            timeout: value.timeout,
 133        }
 134    }
 135}
 136
 137impl TryFrom<extension::DebugTaskDefinition> for DebugTaskDefinition {
 138    type Error = anyhow::Error;
 139    fn try_from(value: extension::DebugTaskDefinition) -> Result<Self, Self::Error> {
 140        Ok(Self {
 141            label: value.label.to_string(),
 142            adapter: value.adapter.to_string(),
 143            config: value.config.to_string(),
 144            tcp_connection: value.tcp_connection.map(Into::into),
 145        })
 146    }
 147}
 148
 149impl From<task::DebugRequest> for DebugRequest {
 150    fn from(value: task::DebugRequest) -> Self {
 151        match value {
 152            task::DebugRequest::Launch(launch_request) => Self::Launch(launch_request.into()),
 153            task::DebugRequest::Attach(attach_request) => Self::Attach(attach_request.into()),
 154        }
 155    }
 156}
 157
 158impl From<DebugRequest> for task::DebugRequest {
 159    fn from(value: DebugRequest) -> Self {
 160        match value {
 161            DebugRequest::Launch(launch_request) => Self::Launch(launch_request.into()),
 162            DebugRequest::Attach(attach_request) => Self::Attach(attach_request.into()),
 163        }
 164    }
 165}
 166
 167impl From<task::LaunchRequest> for LaunchRequest {
 168    fn from(value: task::LaunchRequest) -> Self {
 169        Self {
 170            program: value.program,
 171            cwd: value.cwd.map(|p| p.to_string_lossy().into_owned()),
 172            args: value.args,
 173            envs: value.env.into_iter().collect(),
 174        }
 175    }
 176}
 177
 178impl From<task::AttachRequest> for AttachRequest {
 179    fn from(value: task::AttachRequest) -> Self {
 180        Self {
 181            process_id: value.process_id,
 182        }
 183    }
 184}
 185
 186impl From<LaunchRequest> for task::LaunchRequest {
 187    fn from(value: LaunchRequest) -> Self {
 188        Self {
 189            program: value.program,
 190            cwd: value.cwd.map(|p| p.into()),
 191            args: value.args,
 192            env: value.envs.into_iter().collect(),
 193        }
 194    }
 195}
 196impl From<AttachRequest> for task::AttachRequest {
 197    fn from(value: AttachRequest) -> Self {
 198        Self {
 199            process_id: value.process_id,
 200        }
 201    }
 202}
 203
 204impl From<ZedDebugConfig> for DebugConfig {
 205    fn from(value: ZedDebugConfig) -> Self {
 206        Self {
 207            label: value.label.into(),
 208            adapter: value.adapter.into(),
 209            request: value.request.into(),
 210            stop_on_entry: value.stop_on_entry,
 211        }
 212    }
 213}
 214impl TryFrom<DebugAdapterBinary> for extension::DebugAdapterBinary {
 215    type Error = anyhow::Error;
 216    fn try_from(value: DebugAdapterBinary) -> Result<Self, Self::Error> {
 217        Ok(Self {
 218            command: value.command,
 219            arguments: value.arguments,
 220            envs: value.envs.into_iter().collect(),
 221            cwd: value.cwd.map(|s| s.into()),
 222            connection: value.connection.map(Into::into),
 223            request_args: value.request_args.try_into()?,
 224        })
 225    }
 226}
 227
 228impl From<BuildTaskDefinition> for extension::BuildTaskDefinition {
 229    fn from(value: BuildTaskDefinition) -> Self {
 230        match value {
 231            BuildTaskDefinition::ByName(name) => Self::ByName(name.into()),
 232            BuildTaskDefinition::Template(build_task_template) => Self::Template {
 233                task_template: build_task_template.template.into(),
 234                locator_name: build_task_template.locator_name.map(SharedString::from),
 235            },
 236        }
 237    }
 238}
 239
 240impl From<extension::BuildTaskDefinition> for BuildTaskDefinition {
 241    fn from(value: extension::BuildTaskDefinition) -> Self {
 242        match value {
 243            extension::BuildTaskDefinition::ByName(name) => Self::ByName(name.into()),
 244            extension::BuildTaskDefinition::Template {
 245                task_template,
 246                locator_name,
 247            } => Self::Template(BuildTaskDefinitionTemplatePayload {
 248                template: task_template.into(),
 249                locator_name: locator_name.map(String::from),
 250            }),
 251        }
 252    }
 253}
 254impl From<BuildTaskTemplate> for extension::BuildTaskTemplate {
 255    fn from(value: BuildTaskTemplate) -> Self {
 256        Self {
 257            label: value.label,
 258            command: value.command,
 259            args: value.args,
 260            env: value.env.into_iter().collect(),
 261            cwd: value.cwd,
 262            ..Default::default()
 263        }
 264    }
 265}
 266impl From<extension::BuildTaskTemplate> for BuildTaskTemplate {
 267    fn from(value: extension::BuildTaskTemplate) -> Self {
 268        Self {
 269            label: value.label,
 270            command: value.command,
 271            args: value.args,
 272            env: value.env.into_iter().collect(),
 273            cwd: value.cwd,
 274        }
 275    }
 276}
 277
 278impl TryFrom<DebugScenario> for extension::DebugScenario {
 279    type Error = anyhow::Error;
 280
 281    fn try_from(value: DebugScenario) -> std::result::Result<Self, Self::Error> {
 282        Ok(Self {
 283            adapter: value.adapter.into(),
 284            label: value.label.into(),
 285            build: value.build.map(Into::into),
 286            config: serde_json::Value::from_str(&value.config)?,
 287            tcp_connection: value.tcp_connection.map(Into::into),
 288        })
 289    }
 290}
 291
 292impl From<extension::DebugScenario> for DebugScenario {
 293    fn from(value: extension::DebugScenario) -> Self {
 294        Self {
 295            adapter: value.adapter.into(),
 296            label: value.label.into(),
 297            build: value.build.map(Into::into),
 298            config: value.config.to_string(),
 299            tcp_connection: value.tcp_connection.map(Into::into),
 300        }
 301    }
 302}
 303
 304impl TryFrom<SpawnInTerminal> for ResolvedTask {
 305    type Error = anyhow::Error;
 306
 307    fn try_from(value: SpawnInTerminal) -> Result<Self, Self::Error> {
 308        Ok(Self {
 309            label: value.label,
 310            command: value.command.context("missing command")?,
 311            args: value.args,
 312            env: value.env.into_iter().collect(),
 313            cwd: value.cwd.map(|s| {
 314                let s = s.to_string_lossy();
 315                if cfg!(target_os = "windows") {
 316                    s.replace('\\', "/")
 317                } else {
 318                    s.into_owned()
 319                }
 320            }),
 321        })
 322    }
 323}
 324
 325impl From<CodeLabel> for extension::CodeLabel {
 326    fn from(value: CodeLabel) -> Self {
 327        Self {
 328            code: value.code,
 329            spans: value.spans.into_iter().map(Into::into).collect(),
 330            filter_range: value.filter_range.into(),
 331        }
 332    }
 333}
 334
 335impl From<CodeLabelSpan> for extension::CodeLabelSpan {
 336    fn from(value: CodeLabelSpan) -> Self {
 337        match value {
 338            CodeLabelSpan::CodeRange(range) => Self::CodeRange(range.into()),
 339            CodeLabelSpan::Literal(literal) => Self::Literal(literal.into()),
 340        }
 341    }
 342}
 343
 344impl From<CodeLabelSpanLiteral> for extension::CodeLabelSpanLiteral {
 345    fn from(value: CodeLabelSpanLiteral) -> Self {
 346        Self {
 347            text: value.text,
 348            highlight_name: value.highlight_name,
 349        }
 350    }
 351}
 352
 353impl From<extension::Completion> for Completion {
 354    fn from(value: extension::Completion) -> Self {
 355        Self {
 356            label: value.label,
 357            label_details: value.label_details.map(Into::into),
 358            detail: value.detail,
 359            kind: value.kind.map(Into::into),
 360            insert_text_format: value.insert_text_format.map(Into::into),
 361        }
 362    }
 363}
 364
 365impl From<extension::CompletionLabelDetails> for CompletionLabelDetails {
 366    fn from(value: extension::CompletionLabelDetails) -> Self {
 367        Self {
 368            detail: value.detail,
 369            description: value.description,
 370        }
 371    }
 372}
 373
 374impl From<extension::CompletionKind> for CompletionKind {
 375    fn from(value: extension::CompletionKind) -> Self {
 376        match value {
 377            extension::CompletionKind::Text => Self::Text,
 378            extension::CompletionKind::Method => Self::Method,
 379            extension::CompletionKind::Function => Self::Function,
 380            extension::CompletionKind::Constructor => Self::Constructor,
 381            extension::CompletionKind::Field => Self::Field,
 382            extension::CompletionKind::Variable => Self::Variable,
 383            extension::CompletionKind::Class => Self::Class,
 384            extension::CompletionKind::Interface => Self::Interface,
 385            extension::CompletionKind::Module => Self::Module,
 386            extension::CompletionKind::Property => Self::Property,
 387            extension::CompletionKind::Unit => Self::Unit,
 388            extension::CompletionKind::Value => Self::Value,
 389            extension::CompletionKind::Enum => Self::Enum,
 390            extension::CompletionKind::Keyword => Self::Keyword,
 391            extension::CompletionKind::Snippet => Self::Snippet,
 392            extension::CompletionKind::Color => Self::Color,
 393            extension::CompletionKind::File => Self::File,
 394            extension::CompletionKind::Reference => Self::Reference,
 395            extension::CompletionKind::Folder => Self::Folder,
 396            extension::CompletionKind::EnumMember => Self::EnumMember,
 397            extension::CompletionKind::Constant => Self::Constant,
 398            extension::CompletionKind::Struct => Self::Struct,
 399            extension::CompletionKind::Event => Self::Event,
 400            extension::CompletionKind::Operator => Self::Operator,
 401            extension::CompletionKind::TypeParameter => Self::TypeParameter,
 402            extension::CompletionKind::Other(value) => Self::Other(value),
 403        }
 404    }
 405}
 406
 407impl From<extension::InsertTextFormat> for InsertTextFormat {
 408    fn from(value: extension::InsertTextFormat) -> Self {
 409        match value {
 410            extension::InsertTextFormat::PlainText => Self::PlainText,
 411            extension::InsertTextFormat::Snippet => Self::Snippet,
 412            extension::InsertTextFormat::Other(value) => Self::Other(value),
 413        }
 414    }
 415}
 416
 417impl From<extension::Symbol> for Symbol {
 418    fn from(value: extension::Symbol) -> Self {
 419        Self {
 420            kind: value.kind.into(),
 421            name: value.name,
 422        }
 423    }
 424}
 425
 426impl From<extension::SymbolKind> for SymbolKind {
 427    fn from(value: extension::SymbolKind) -> Self {
 428        match value {
 429            extension::SymbolKind::File => Self::File,
 430            extension::SymbolKind::Module => Self::Module,
 431            extension::SymbolKind::Namespace => Self::Namespace,
 432            extension::SymbolKind::Package => Self::Package,
 433            extension::SymbolKind::Class => Self::Class,
 434            extension::SymbolKind::Method => Self::Method,
 435            extension::SymbolKind::Property => Self::Property,
 436            extension::SymbolKind::Field => Self::Field,
 437            extension::SymbolKind::Constructor => Self::Constructor,
 438            extension::SymbolKind::Enum => Self::Enum,
 439            extension::SymbolKind::Interface => Self::Interface,
 440            extension::SymbolKind::Function => Self::Function,
 441            extension::SymbolKind::Variable => Self::Variable,
 442            extension::SymbolKind::Constant => Self::Constant,
 443            extension::SymbolKind::String => Self::String,
 444            extension::SymbolKind::Number => Self::Number,
 445            extension::SymbolKind::Boolean => Self::Boolean,
 446            extension::SymbolKind::Array => Self::Array,
 447            extension::SymbolKind::Object => Self::Object,
 448            extension::SymbolKind::Key => Self::Key,
 449            extension::SymbolKind::Null => Self::Null,
 450            extension::SymbolKind::EnumMember => Self::EnumMember,
 451            extension::SymbolKind::Struct => Self::Struct,
 452            extension::SymbolKind::Event => Self::Event,
 453            extension::SymbolKind::Operator => Self::Operator,
 454            extension::SymbolKind::TypeParameter => Self::TypeParameter,
 455            extension::SymbolKind::Other(value) => Self::Other(value),
 456        }
 457    }
 458}
 459
 460impl From<extension::SlashCommand> for SlashCommand {
 461    fn from(value: extension::SlashCommand) -> Self {
 462        Self {
 463            name: value.name,
 464            description: value.description,
 465            tooltip_text: value.tooltip_text,
 466            requires_argument: value.requires_argument,
 467        }
 468    }
 469}
 470
 471impl From<SlashCommandOutput> for extension::SlashCommandOutput {
 472    fn from(value: SlashCommandOutput) -> Self {
 473        Self {
 474            text: value.text,
 475            sections: value.sections.into_iter().map(Into::into).collect(),
 476        }
 477    }
 478}
 479
 480impl From<SlashCommandOutputSection> for extension::SlashCommandOutputSection {
 481    fn from(value: SlashCommandOutputSection) -> Self {
 482        Self {
 483            range: value.range.start as usize..value.range.end as usize,
 484            label: value.label,
 485        }
 486    }
 487}
 488
 489impl From<SlashCommandArgumentCompletion> for extension::SlashCommandArgumentCompletion {
 490    fn from(value: SlashCommandArgumentCompletion) -> Self {
 491        Self {
 492            label: value.label,
 493            new_text: value.new_text,
 494            run_command: value.run_command,
 495        }
 496    }
 497}
 498
 499impl TryFrom<ContextServerConfiguration> for extension::ContextServerConfiguration {
 500    type Error = anyhow::Error;
 501
 502    fn try_from(value: ContextServerConfiguration) -> Result<Self, Self::Error> {
 503        let settings_schema: serde_json::Value = serde_json::from_str(&value.settings_schema)
 504            .context("Failed to parse settings_schema")?;
 505
 506        Ok(Self {
 507            installation_instructions: value.installation_instructions,
 508            default_settings: value.default_settings,
 509            settings_schema,
 510        })
 511    }
 512}
 513
 514impl HostKeyValueStore for WasmState {
 515    async fn insert(
 516        &mut self,
 517        kv_store: Resource<ExtensionKeyValueStore>,
 518        key: String,
 519        value: String,
 520    ) -> wasmtime::Result<Result<(), String>> {
 521        let kv_store = self.table.get(&kv_store)?;
 522        kv_store.insert(key, value).await.to_wasmtime_result()
 523    }
 524
 525    async fn drop(&mut self, _worktree: Resource<ExtensionKeyValueStore>) -> Result<()> {
 526        // We only ever hand out borrows of key-value stores.
 527        Ok(())
 528    }
 529}
 530
 531impl HostProject for WasmState {
 532    async fn worktree_ids(
 533        &mut self,
 534        project: Resource<ExtensionProject>,
 535    ) -> wasmtime::Result<Vec<u64>> {
 536        let project = self.table.get(&project)?;
 537        Ok(project.worktree_ids())
 538    }
 539
 540    async fn drop(&mut self, _project: Resource<Project>) -> Result<()> {
 541        // We only ever hand out borrows of projects.
 542        Ok(())
 543    }
 544}
 545
 546impl HostWorktree for WasmState {
 547    async fn id(&mut self, delegate: Resource<Arc<dyn WorktreeDelegate>>) -> wasmtime::Result<u64> {
 548        let delegate = self.table.get(&delegate)?;
 549        Ok(delegate.id())
 550    }
 551
 552    async fn root_path(
 553        &mut self,
 554        delegate: Resource<Arc<dyn WorktreeDelegate>>,
 555    ) -> wasmtime::Result<String> {
 556        let delegate = self.table.get(&delegate)?;
 557        Ok(delegate.root_path())
 558    }
 559
 560    async fn read_text_file(
 561        &mut self,
 562        delegate: Resource<Arc<dyn WorktreeDelegate>>,
 563        path: String,
 564    ) -> wasmtime::Result<Result<String, String>> {
 565        let delegate = self.table.get(&delegate)?;
 566        Ok(delegate
 567            .read_text_file(path.into())
 568            .await
 569            .map_err(|error| error.to_string()))
 570    }
 571
 572    async fn shell_env(
 573        &mut self,
 574        delegate: Resource<Arc<dyn WorktreeDelegate>>,
 575    ) -> wasmtime::Result<EnvVars> {
 576        let delegate = self.table.get(&delegate)?;
 577        Ok(delegate.shell_env().await.into_iter().collect())
 578    }
 579
 580    async fn which(
 581        &mut self,
 582        delegate: Resource<Arc<dyn WorktreeDelegate>>,
 583        binary_name: String,
 584    ) -> wasmtime::Result<Option<String>> {
 585        let delegate = self.table.get(&delegate)?;
 586        Ok(delegate.which(binary_name).await)
 587    }
 588
 589    async fn drop(&mut self, _worktree: Resource<Worktree>) -> Result<()> {
 590        // We only ever hand out borrows of worktrees.
 591        Ok(())
 592    }
 593}
 594
 595impl common::Host for WasmState {}
 596
 597impl http_client::Host for WasmState {
 598    async fn fetch(
 599        &mut self,
 600        request: http_client::HttpRequest,
 601    ) -> wasmtime::Result<Result<http_client::HttpResponse, String>> {
 602        maybe!(async {
 603            let url = &request.url;
 604            let request = convert_request(&request)?;
 605            let mut response = self.host.http_client.send(request).await?;
 606
 607            if response.status().is_client_error() || response.status().is_server_error() {
 608                bail!("failed to fetch '{url}': status code {}", response.status())
 609            }
 610            convert_response(&mut response).await
 611        })
 612        .await
 613        .to_wasmtime_result()
 614    }
 615
 616    async fn fetch_stream(
 617        &mut self,
 618        request: http_client::HttpRequest,
 619    ) -> wasmtime::Result<Result<Resource<ExtensionHttpResponseStream>, String>> {
 620        let request = convert_request(&request)?;
 621        let response = self.host.http_client.send(request);
 622        maybe!(async {
 623            let response = response.await?;
 624            let stream = Arc::new(Mutex::new(response));
 625            let resource = self.table.push(stream)?;
 626            Ok(resource)
 627        })
 628        .await
 629        .to_wasmtime_result()
 630    }
 631}
 632
 633impl http_client::HostHttpResponseStream for WasmState {
 634    async fn next_chunk(
 635        &mut self,
 636        resource: Resource<ExtensionHttpResponseStream>,
 637    ) -> wasmtime::Result<Result<Option<Vec<u8>>, String>> {
 638        let stream = self.table.get(&resource)?.clone();
 639        maybe!(async move {
 640            let mut response = stream.lock().await;
 641            let mut buffer = vec![0; 8192]; // 8KB buffer
 642            let bytes_read = response.body_mut().read(&mut buffer).await?;
 643            if bytes_read == 0 {
 644                Ok(None)
 645            } else {
 646                buffer.truncate(bytes_read);
 647                Ok(Some(buffer))
 648            }
 649        })
 650        .await
 651        .to_wasmtime_result()
 652    }
 653
 654    async fn drop(&mut self, _resource: Resource<ExtensionHttpResponseStream>) -> Result<()> {
 655        Ok(())
 656    }
 657}
 658
 659impl From<http_client::HttpMethod> for ::http_client::Method {
 660    fn from(value: http_client::HttpMethod) -> Self {
 661        match value {
 662            http_client::HttpMethod::Get => Self::GET,
 663            http_client::HttpMethod::Post => Self::POST,
 664            http_client::HttpMethod::Put => Self::PUT,
 665            http_client::HttpMethod::Delete => Self::DELETE,
 666            http_client::HttpMethod::Head => Self::HEAD,
 667            http_client::HttpMethod::Options => Self::OPTIONS,
 668            http_client::HttpMethod::Patch => Self::PATCH,
 669        }
 670    }
 671}
 672
 673fn convert_request(
 674    extension_request: &http_client::HttpRequest,
 675) -> anyhow::Result<::http_client::Request<AsyncBody>> {
 676    let mut request = ::http_client::Request::builder()
 677        .method(::http_client::Method::from(extension_request.method))
 678        .uri(&extension_request.url)
 679        .follow_redirects(match extension_request.redirect_policy {
 680            http_client::RedirectPolicy::NoFollow => ::http_client::RedirectPolicy::NoFollow,
 681            http_client::RedirectPolicy::FollowLimit(limit) => {
 682                ::http_client::RedirectPolicy::FollowLimit(limit)
 683            }
 684            http_client::RedirectPolicy::FollowAll => ::http_client::RedirectPolicy::FollowAll,
 685        });
 686    for (key, value) in &extension_request.headers {
 687        request = request.header(key, value);
 688    }
 689    let body = extension_request
 690        .body
 691        .clone()
 692        .map(AsyncBody::from)
 693        .unwrap_or_default();
 694    request.body(body).map_err(anyhow::Error::from)
 695}
 696
 697async fn convert_response(
 698    response: &mut ::http_client::Response<AsyncBody>,
 699) -> anyhow::Result<http_client::HttpResponse> {
 700    let mut extension_response = http_client::HttpResponse {
 701        body: Vec::new(),
 702        headers: Vec::new(),
 703    };
 704
 705    for (key, value) in response.headers() {
 706        extension_response
 707            .headers
 708            .push((key.to_string(), value.to_str().unwrap_or("").to_string()));
 709    }
 710
 711    response
 712        .body_mut()
 713        .read_to_end(&mut extension_response.body)
 714        .await?;
 715
 716    Ok(extension_response)
 717}
 718
 719impl nodejs::Host for WasmState {
 720    async fn node_binary_path(&mut self) -> wasmtime::Result<Result<String, String>> {
 721        self.host
 722            .node_runtime
 723            .binary_path()
 724            .await
 725            .map(|path| path.to_string_lossy().to_string())
 726            .to_wasmtime_result()
 727    }
 728
 729    async fn npm_package_latest_version(
 730        &mut self,
 731        package_name: String,
 732    ) -> wasmtime::Result<Result<String, String>> {
 733        self.host
 734            .node_runtime
 735            .npm_package_latest_version(&package_name)
 736            .await
 737            .to_wasmtime_result()
 738    }
 739
 740    async fn npm_package_installed_version(
 741        &mut self,
 742        package_name: String,
 743    ) -> wasmtime::Result<Result<Option<String>, String>> {
 744        self.host
 745            .node_runtime
 746            .npm_package_installed_version(&self.work_dir(), &package_name)
 747            .await
 748            .to_wasmtime_result()
 749    }
 750
 751    async fn npm_install_package(
 752        &mut self,
 753        package_name: String,
 754        version: String,
 755    ) -> wasmtime::Result<Result<(), String>> {
 756        self.capability_granter
 757            .grant_npm_install_package(&package_name)?;
 758
 759        self.host
 760            .node_runtime
 761            .npm_install_packages(&self.work_dir(), &[(&package_name, &version)])
 762            .await
 763            .to_wasmtime_result()
 764    }
 765}
 766
 767#[async_trait]
 768impl lsp::Host for WasmState {}
 769
 770impl From<::http_client::github::GithubRelease> for github::GithubRelease {
 771    fn from(value: ::http_client::github::GithubRelease) -> Self {
 772        Self {
 773            version: value.tag_name,
 774            assets: value.assets.into_iter().map(Into::into).collect(),
 775        }
 776    }
 777}
 778
 779impl From<::http_client::github::GithubReleaseAsset> for github::GithubReleaseAsset {
 780    fn from(value: ::http_client::github::GithubReleaseAsset) -> Self {
 781        Self {
 782            name: value.name,
 783            download_url: value.browser_download_url,
 784        }
 785    }
 786}
 787
 788impl github::Host for WasmState {
 789    async fn latest_github_release(
 790        &mut self,
 791        repo: String,
 792        options: github::GithubReleaseOptions,
 793    ) -> wasmtime::Result<Result<github::GithubRelease, String>> {
 794        maybe!(async {
 795            let release = ::http_client::github::latest_github_release(
 796                &repo,
 797                options.require_assets,
 798                options.pre_release,
 799                self.host.http_client.clone(),
 800            )
 801            .await?;
 802            Ok(release.into())
 803        })
 804        .await
 805        .to_wasmtime_result()
 806    }
 807
 808    async fn github_release_by_tag_name(
 809        &mut self,
 810        repo: String,
 811        tag: String,
 812    ) -> wasmtime::Result<Result<github::GithubRelease, String>> {
 813        maybe!(async {
 814            let release = ::http_client::github::get_release_by_tag_name(
 815                &repo,
 816                &tag,
 817                self.host.http_client.clone(),
 818            )
 819            .await?;
 820            Ok(release.into())
 821        })
 822        .await
 823        .to_wasmtime_result()
 824    }
 825}
 826
 827impl platform::Host for WasmState {
 828    async fn current_platform(&mut self) -> Result<(platform::Os, platform::Architecture)> {
 829        Ok((
 830            match env::consts::OS {
 831                "macos" => platform::Os::Mac,
 832                "linux" => platform::Os::Linux,
 833                "windows" => platform::Os::Windows,
 834                _ => panic!("unsupported os"),
 835            },
 836            match env::consts::ARCH {
 837                "aarch64" => platform::Architecture::Aarch64,
 838                "x86" => platform::Architecture::X86,
 839                "x86_64" => platform::Architecture::X8664,
 840                _ => panic!("unsupported architecture"),
 841            },
 842        ))
 843    }
 844}
 845
 846impl From<std::process::Output> for process::Output {
 847    fn from(output: std::process::Output) -> Self {
 848        Self {
 849            status: output.status.code(),
 850            stdout: output.stdout,
 851            stderr: output.stderr,
 852        }
 853    }
 854}
 855
 856impl process::Host for WasmState {
 857    async fn run_command(
 858        &mut self,
 859        command: process::Command,
 860    ) -> wasmtime::Result<Result<process::Output, String>> {
 861        maybe!(async {
 862            self.capability_granter
 863                .grant_exec(&command.command, &command.args)?;
 864
 865            let output = util::command::new_smol_command(command.command.as_str())
 866                .args(&command.args)
 867                .envs(command.env)
 868                .output()
 869                .await?;
 870
 871            Ok(output.into())
 872        })
 873        .await
 874        .to_wasmtime_result()
 875    }
 876}
 877
 878#[async_trait]
 879impl slash_command::Host for WasmState {}
 880
 881#[async_trait]
 882impl context_server::Host for WasmState {}
 883
 884impl dap::Host for WasmState {
 885    async fn resolve_tcp_template(
 886        &mut self,
 887        template: TcpArgumentsTemplate,
 888    ) -> wasmtime::Result<Result<TcpArguments, String>> {
 889        maybe!(async {
 890            let (host, port, timeout) =
 891                ::dap::configure_tcp_connection(task::TcpArgumentsTemplate {
 892                    port: template.port,
 893                    host: template.host.map(Ipv4Addr::from_bits),
 894                    timeout: template.timeout,
 895                })
 896                .await?;
 897            Ok(TcpArguments {
 898                port,
 899                host: host.to_bits(),
 900                timeout,
 901            })
 902        })
 903        .await
 904        .to_wasmtime_result()
 905    }
 906}
 907
 908impl ExtensionImports for WasmState {
 909    async fn get_settings(
 910        &mut self,
 911        location: Option<self::SettingsLocation>,
 912        category: String,
 913        key: Option<String>,
 914    ) -> wasmtime::Result<Result<String, String>> {
 915        self.on_main_thread(|cx| {
 916            async move {
 917                let location = location
 918                    .as_ref()
 919                    .map(|location| ::settings::SettingsLocation {
 920                        worktree_id: WorktreeId::from_proto(location.worktree_id),
 921                        path: Path::new(&location.path),
 922                    });
 923
 924                cx.update(|cx| match category.as_str() {
 925                    "language" => {
 926                        let key = key.map(|k| LanguageName::new(&k));
 927                        let settings = AllLanguageSettings::get(location, cx).language(
 928                            location,
 929                            key.as_ref(),
 930                            cx,
 931                        );
 932                        Ok(serde_json::to_string(&settings::LanguageSettings {
 933                            tab_size: settings.tab_size,
 934                        })?)
 935                    }
 936                    "lsp" => {
 937                        let settings = key
 938                            .and_then(|key| {
 939                                ProjectSettings::get(location, cx)
 940                                    .lsp
 941                                    .get(&::lsp::LanguageServerName::from_proto(key))
 942                            })
 943                            .cloned()
 944                            .unwrap_or_default();
 945                        Ok(serde_json::to_string(&settings::LspSettings {
 946                            binary: settings.binary.map(|binary| settings::CommandSettings {
 947                                path: binary.path,
 948                                arguments: binary.arguments,
 949                                env: binary.env.map(|env| env.into_iter().collect()),
 950                            }),
 951                            settings: settings.settings,
 952                            initialization_options: settings.initialization_options,
 953                        })?)
 954                    }
 955                    "context_servers" => {
 956                        let settings = key
 957                            .and_then(|key| {
 958                                ProjectSettings::get(location, cx)
 959                                    .context_servers
 960                                    .get(key.as_str())
 961                            })
 962                            .cloned()
 963                            .unwrap_or_else(|| {
 964                                project::project_settings::ContextServerSettings::default_extension(
 965                                )
 966                            });
 967
 968                        match settings {
 969                            project::project_settings::ContextServerSettings::Custom {
 970                                enabled: _,
 971                                command,
 972                            } => Ok(serde_json::to_string(&settings::ContextServerSettings {
 973                                command: Some(settings::CommandSettings {
 974                                    path: command.path.to_str().map(|path| path.to_string()),
 975                                    arguments: Some(command.args),
 976                                    env: command.env.map(|env| env.into_iter().collect()),
 977                                }),
 978                                settings: None,
 979                            })?),
 980                            project::project_settings::ContextServerSettings::Extension {
 981                                enabled: _,
 982                                settings,
 983                            } => Ok(serde_json::to_string(&settings::ContextServerSettings {
 984                                command: None,
 985                                settings: Some(settings),
 986                            })?),
 987                        }
 988                    }
 989                    _ => {
 990                        bail!("Unknown settings category: {}", category);
 991                    }
 992                })
 993            }
 994            .boxed_local()
 995        })
 996        .await?
 997        .to_wasmtime_result()
 998    }
 999
1000    async fn set_language_server_installation_status(
1001        &mut self,
1002        server_name: String,
1003        status: LanguageServerInstallationStatus,
1004    ) -> wasmtime::Result<()> {
1005        let status = match status {
1006            LanguageServerInstallationStatus::CheckingForUpdate => BinaryStatus::CheckingForUpdate,
1007            LanguageServerInstallationStatus::Downloading => BinaryStatus::Downloading,
1008            LanguageServerInstallationStatus::None => BinaryStatus::None,
1009            LanguageServerInstallationStatus::Failed(error) => BinaryStatus::Failed { error },
1010        };
1011
1012        self.host
1013            .proxy
1014            .update_language_server_status(::lsp::LanguageServerName(server_name.into()), status);
1015
1016        Ok(())
1017    }
1018
1019    async fn download_file(
1020        &mut self,
1021        url: String,
1022        path: String,
1023        file_type: DownloadedFileType,
1024    ) -> wasmtime::Result<Result<(), String>> {
1025        maybe!(async {
1026            let parsed_url = Url::parse(&url)?;
1027            self.capability_granter.grant_download_file(&parsed_url)?;
1028
1029            let path = PathBuf::from(path);
1030            let extension_work_dir = self.host.work_dir.join(self.manifest.id.as_ref());
1031
1032            self.host.fs.create_dir(&extension_work_dir).await?;
1033
1034            let destination_path = self
1035                .host
1036                .writeable_path_from_extension(&self.manifest.id, &path)?;
1037
1038            let mut response = self
1039                .host
1040                .http_client
1041                .get(&url, Default::default(), true)
1042                .await
1043                .context("downloading release")?;
1044
1045            anyhow::ensure!(
1046                response.status().is_success(),
1047                "download failed with status {}",
1048                response.status().to_string()
1049            );
1050            let body = BufReader::new(response.body_mut());
1051
1052            match file_type {
1053                DownloadedFileType::Uncompressed => {
1054                    futures::pin_mut!(body);
1055                    self.host
1056                        .fs
1057                        .create_file_with(&destination_path, body)
1058                        .await?;
1059                }
1060                DownloadedFileType::Gzip => {
1061                    let body = GzipDecoder::new(body);
1062                    futures::pin_mut!(body);
1063                    self.host
1064                        .fs
1065                        .create_file_with(&destination_path, body)
1066                        .await?;
1067                }
1068                DownloadedFileType::GzipTar => {
1069                    let body = GzipDecoder::new(body);
1070                    futures::pin_mut!(body);
1071                    self.host
1072                        .fs
1073                        .extract_tar_file(&destination_path, Archive::new(body))
1074                        .await?;
1075                }
1076                DownloadedFileType::Zip => {
1077                    futures::pin_mut!(body);
1078                    extract_zip(&destination_path, body)
1079                        .await
1080                        .with_context(|| format!("unzipping {path:?} archive"))?;
1081                }
1082            }
1083
1084            Ok(())
1085        })
1086        .await
1087        .to_wasmtime_result()
1088    }
1089
1090    async fn make_file_executable(&mut self, path: String) -> wasmtime::Result<Result<(), String>> {
1091        let path = self
1092            .host
1093            .writeable_path_from_extension(&self.manifest.id, Path::new(&path))?;
1094
1095        make_file_executable(&path)
1096            .await
1097            .with_context(|| format!("setting permissions for path {path:?}"))
1098            .to_wasmtime_result()
1099    }
1100}