wit.rs

  1mod since_v0_0_1;
  2mod since_v0_0_4;
  3mod since_v0_0_6;
  4mod since_v0_1_0;
  5mod since_v0_2_0;
  6mod since_v0_3_0;
  7mod since_v0_4_0;
  8mod since_v0_5_0;
  9use extension::{KeyValueStoreDelegate, WorktreeDelegate};
 10use language::LanguageName;
 11use lsp::LanguageServerName;
 12use release_channel::ReleaseChannel;
 13use since_v0_5_0 as latest;
 14
 15use super::{WasmState, wasm_engine};
 16use anyhow::{Context as _, Result, anyhow};
 17use semantic_version::SemanticVersion;
 18use std::{ops::RangeInclusive, sync::Arc};
 19use wasmtime::{
 20    Store,
 21    component::{Component, Linker, Resource},
 22};
 23
 24#[cfg(test)]
 25pub use latest::CodeLabelSpanLiteral;
 26pub use latest::{
 27    CodeLabel, CodeLabelSpan, Command, ExtensionProject, Range, SlashCommand,
 28    zed::extension::lsp::{
 29        Completion, CompletionKind, CompletionLabelDetails, InsertTextFormat, Symbol, SymbolKind,
 30    },
 31    zed::extension::slash_command::{SlashCommandArgumentCompletion, SlashCommandOutput},
 32};
 33pub use since_v0_0_4::LanguageServerConfig;
 34
 35pub fn new_linker(
 36    f: impl Fn(&mut Linker<WasmState>, fn(&mut WasmState) -> &mut WasmState) -> Result<()>,
 37) -> Linker<WasmState> {
 38    let mut linker = Linker::new(&wasm_engine());
 39    wasmtime_wasi::add_to_linker_async(&mut linker).unwrap();
 40    f(&mut linker, wasi_view).unwrap();
 41    linker
 42}
 43
 44fn wasi_view(state: &mut WasmState) -> &mut WasmState {
 45    state
 46}
 47
 48/// Returns whether the given Wasm API version is supported by the Wasm host.
 49pub fn is_supported_wasm_api_version(
 50    release_channel: ReleaseChannel,
 51    version: SemanticVersion,
 52) -> bool {
 53    wasm_api_version_range(release_channel).contains(&version)
 54}
 55
 56/// Returns the Wasm API version range that is supported by the Wasm host.
 57#[inline(always)]
 58pub fn wasm_api_version_range(release_channel: ReleaseChannel) -> RangeInclusive<SemanticVersion> {
 59    // Note: The release channel can be used to stage a new version of the extension API.
 60    let _ = release_channel;
 61
 62    let max_version = match release_channel {
 63        ReleaseChannel::Dev | ReleaseChannel::Nightly => latest::MAX_VERSION,
 64        ReleaseChannel::Stable | ReleaseChannel::Preview => since_v0_4_0::MAX_VERSION,
 65    };
 66
 67    since_v0_0_1::MIN_VERSION..=max_version
 68}
 69
 70/// Authorizes access to use unreleased versions of the Wasm API, based on the provided [`ReleaseChannel`].
 71///
 72/// Note: If there isn't currently an unreleased Wasm API version this function may be unused. Don't delete it!
 73pub fn authorize_access_to_unreleased_wasm_api_version(
 74    release_channel: ReleaseChannel,
 75) -> Result<()> {
 76    let allow_unreleased_version = match release_channel {
 77        ReleaseChannel::Dev | ReleaseChannel::Nightly => true,
 78        ReleaseChannel::Stable | ReleaseChannel::Preview => {
 79            // We always allow the latest in tests so that the extension tests pass on release branches.
 80            cfg!(any(test, feature = "test-support"))
 81        }
 82    };
 83
 84    if !allow_unreleased_version {
 85        Err(anyhow!(
 86            "unreleased versions of the extension API can only be used on development builds of Zed"
 87        ))?;
 88    }
 89
 90    Ok(())
 91}
 92
 93pub enum Extension {
 94    V0_5_0(since_v0_5_0::Extension),
 95    V0_4_0(since_v0_4_0::Extension),
 96    V0_3_0(since_v0_3_0::Extension),
 97    V0_2_0(since_v0_2_0::Extension),
 98    V0_1_0(since_v0_1_0::Extension),
 99    V0_0_6(since_v0_0_6::Extension),
100    V0_0_4(since_v0_0_4::Extension),
101    V0_0_1(since_v0_0_1::Extension),
102}
103
104impl Extension {
105    pub async fn instantiate_async(
106        store: &mut Store<WasmState>,
107        release_channel: ReleaseChannel,
108        version: SemanticVersion,
109        component: &Component,
110    ) -> Result<Self> {
111        // Note: The release channel can be used to stage a new version of the extension API.
112        let _ = release_channel;
113
114        if version >= latest::MIN_VERSION {
115            authorize_access_to_unreleased_wasm_api_version(release_channel)?;
116
117            let extension =
118                latest::Extension::instantiate_async(store, component, latest::linker())
119                    .await
120                    .context("failed to instantiate wasm extension")?;
121            Ok(Self::V0_5_0(extension))
122        } else if version >= since_v0_4_0::MIN_VERSION {
123            let extension = since_v0_4_0::Extension::instantiate_async(
124                store,
125                component,
126                since_v0_4_0::linker(),
127            )
128            .await
129            .context("failed to instantiate wasm extension")?;
130            Ok(Self::V0_4_0(extension))
131        } else if version >= since_v0_3_0::MIN_VERSION {
132            let extension = since_v0_3_0::Extension::instantiate_async(
133                store,
134                component,
135                since_v0_3_0::linker(),
136            )
137            .await
138            .context("failed to instantiate wasm extension")?;
139            Ok(Self::V0_3_0(extension))
140        } else if version >= since_v0_2_0::MIN_VERSION {
141            let extension = since_v0_2_0::Extension::instantiate_async(
142                store,
143                component,
144                since_v0_2_0::linker(),
145            )
146            .await
147            .context("failed to instantiate wasm extension")?;
148            Ok(Self::V0_2_0(extension))
149        } else if version >= since_v0_1_0::MIN_VERSION {
150            let extension = since_v0_1_0::Extension::instantiate_async(
151                store,
152                component,
153                since_v0_1_0::linker(),
154            )
155            .await
156            .context("failed to instantiate wasm extension")?;
157            Ok(Self::V0_1_0(extension))
158        } else if version >= since_v0_0_6::MIN_VERSION {
159            let extension = since_v0_0_6::Extension::instantiate_async(
160                store,
161                component,
162                since_v0_0_6::linker(),
163            )
164            .await
165            .context("failed to instantiate wasm extension")?;
166            Ok(Self::V0_0_6(extension))
167        } else if version >= since_v0_0_4::MIN_VERSION {
168            let extension = since_v0_0_4::Extension::instantiate_async(
169                store,
170                component,
171                since_v0_0_4::linker(),
172            )
173            .await
174            .context("failed to instantiate wasm extension")?;
175            Ok(Self::V0_0_4(extension))
176        } else {
177            let extension = since_v0_0_1::Extension::instantiate_async(
178                store,
179                component,
180                since_v0_0_1::linker(),
181            )
182            .await
183            .context("failed to instantiate wasm extension")?;
184            Ok(Self::V0_0_1(extension))
185        }
186    }
187
188    pub async fn call_init_extension(&self, store: &mut Store<WasmState>) -> Result<()> {
189        match self {
190            Extension::V0_5_0(ext) => ext.call_init_extension(store).await,
191            Extension::V0_4_0(ext) => ext.call_init_extension(store).await,
192            Extension::V0_3_0(ext) => ext.call_init_extension(store).await,
193            Extension::V0_2_0(ext) => ext.call_init_extension(store).await,
194            Extension::V0_1_0(ext) => ext.call_init_extension(store).await,
195            Extension::V0_0_6(ext) => ext.call_init_extension(store).await,
196            Extension::V0_0_4(ext) => ext.call_init_extension(store).await,
197            Extension::V0_0_1(ext) => ext.call_init_extension(store).await,
198        }
199    }
200
201    pub async fn call_language_server_command(
202        &self,
203        store: &mut Store<WasmState>,
204        language_server_id: &LanguageServerName,
205        language_name: &LanguageName,
206        resource: Resource<Arc<dyn WorktreeDelegate>>,
207    ) -> Result<Result<Command, String>> {
208        match self {
209            Extension::V0_5_0(ext) => {
210                ext.call_language_server_command(store, &language_server_id.0, resource)
211                    .await
212            }
213            Extension::V0_4_0(ext) => {
214                ext.call_language_server_command(store, &language_server_id.0, resource)
215                    .await
216            }
217            Extension::V0_3_0(ext) => {
218                ext.call_language_server_command(store, &language_server_id.0, resource)
219                    .await
220            }
221            Extension::V0_2_0(ext) => Ok(ext
222                .call_language_server_command(store, &language_server_id.0, resource)
223                .await?
224                .map(|command| command.into())),
225            Extension::V0_1_0(ext) => Ok(ext
226                .call_language_server_command(store, &language_server_id.0, resource)
227                .await?
228                .map(|command| command.into())),
229            Extension::V0_0_6(ext) => Ok(ext
230                .call_language_server_command(store, &language_server_id.0, resource)
231                .await?
232                .map(|command| command.into())),
233            Extension::V0_0_4(ext) => Ok(ext
234                .call_language_server_command(
235                    store,
236                    &LanguageServerConfig {
237                        name: language_server_id.0.to_string(),
238                        language_name: language_name.to_string(),
239                    },
240                    resource,
241                )
242                .await?
243                .map(|command| command.into())),
244            Extension::V0_0_1(ext) => Ok(ext
245                .call_language_server_command(
246                    store,
247                    &LanguageServerConfig {
248                        name: language_server_id.0.to_string(),
249                        language_name: language_name.to_string(),
250                    }
251                    .into(),
252                    resource,
253                )
254                .await?
255                .map(|command| command.into())),
256        }
257    }
258
259    pub async fn call_language_server_initialization_options(
260        &self,
261        store: &mut Store<WasmState>,
262        language_server_id: &LanguageServerName,
263        language_name: &LanguageName,
264        resource: Resource<Arc<dyn WorktreeDelegate>>,
265    ) -> Result<Result<Option<String>, String>> {
266        match self {
267            Extension::V0_5_0(ext) => {
268                ext.call_language_server_initialization_options(
269                    store,
270                    &language_server_id.0,
271                    resource,
272                )
273                .await
274            }
275            Extension::V0_4_0(ext) => {
276                ext.call_language_server_initialization_options(
277                    store,
278                    &language_server_id.0,
279                    resource,
280                )
281                .await
282            }
283            Extension::V0_3_0(ext) => {
284                ext.call_language_server_initialization_options(
285                    store,
286                    &language_server_id.0,
287                    resource,
288                )
289                .await
290            }
291            Extension::V0_2_0(ext) => {
292                ext.call_language_server_initialization_options(
293                    store,
294                    &language_server_id.0,
295                    resource,
296                )
297                .await
298            }
299            Extension::V0_1_0(ext) => {
300                ext.call_language_server_initialization_options(
301                    store,
302                    &language_server_id.0,
303                    resource,
304                )
305                .await
306            }
307            Extension::V0_0_6(ext) => {
308                ext.call_language_server_initialization_options(
309                    store,
310                    &language_server_id.0,
311                    resource,
312                )
313                .await
314            }
315            Extension::V0_0_4(ext) => {
316                ext.call_language_server_initialization_options(
317                    store,
318                    &LanguageServerConfig {
319                        name: language_server_id.0.to_string(),
320                        language_name: language_name.to_string(),
321                    },
322                    resource,
323                )
324                .await
325            }
326            Extension::V0_0_1(ext) => {
327                ext.call_language_server_initialization_options(
328                    store,
329                    &LanguageServerConfig {
330                        name: language_server_id.0.to_string(),
331                        language_name: language_name.to_string(),
332                    }
333                    .into(),
334                    resource,
335                )
336                .await
337            }
338        }
339    }
340
341    pub async fn call_language_server_workspace_configuration(
342        &self,
343        store: &mut Store<WasmState>,
344        language_server_id: &LanguageServerName,
345        resource: Resource<Arc<dyn WorktreeDelegate>>,
346    ) -> Result<Result<Option<String>, String>> {
347        match self {
348            Extension::V0_5_0(ext) => {
349                ext.call_language_server_workspace_configuration(
350                    store,
351                    &language_server_id.0,
352                    resource,
353                )
354                .await
355            }
356            Extension::V0_4_0(ext) => {
357                ext.call_language_server_workspace_configuration(
358                    store,
359                    &language_server_id.0,
360                    resource,
361                )
362                .await
363            }
364            Extension::V0_3_0(ext) => {
365                ext.call_language_server_workspace_configuration(
366                    store,
367                    &language_server_id.0,
368                    resource,
369                )
370                .await
371            }
372            Extension::V0_2_0(ext) => {
373                ext.call_language_server_workspace_configuration(
374                    store,
375                    &language_server_id.0,
376                    resource,
377                )
378                .await
379            }
380            Extension::V0_1_0(ext) => {
381                ext.call_language_server_workspace_configuration(
382                    store,
383                    &language_server_id.0,
384                    resource,
385                )
386                .await
387            }
388            Extension::V0_0_6(ext) => {
389                ext.call_language_server_workspace_configuration(
390                    store,
391                    &language_server_id.0,
392                    resource,
393                )
394                .await
395            }
396            Extension::V0_0_4(_) | Extension::V0_0_1(_) => Ok(Ok(None)),
397        }
398    }
399
400    pub async fn call_language_server_additional_initialization_options(
401        &self,
402        store: &mut Store<WasmState>,
403        language_server_id: &LanguageServerName,
404        target_language_server_id: &LanguageServerName,
405        resource: Resource<Arc<dyn WorktreeDelegate>>,
406    ) -> Result<Result<Option<String>, String>> {
407        match self {
408            Extension::V0_5_0(ext) => {
409                ext.call_language_server_additional_initialization_options(
410                    store,
411                    &language_server_id.0,
412                    &target_language_server_id.0,
413                    resource,
414                )
415                .await
416            }
417            Extension::V0_4_0(ext) => {
418                ext.call_language_server_additional_initialization_options(
419                    store,
420                    &language_server_id.0,
421                    &target_language_server_id.0,
422                    resource,
423                )
424                .await
425            }
426            Extension::V0_3_0(_)
427            | Extension::V0_2_0(_)
428            | Extension::V0_1_0(_)
429            | Extension::V0_0_6(_)
430            | Extension::V0_0_4(_)
431            | Extension::V0_0_1(_) => Ok(Ok(None)),
432        }
433    }
434
435    pub async fn call_language_server_additional_workspace_configuration(
436        &self,
437        store: &mut Store<WasmState>,
438        language_server_id: &LanguageServerName,
439        target_language_server_id: &LanguageServerName,
440        resource: Resource<Arc<dyn WorktreeDelegate>>,
441    ) -> Result<Result<Option<String>, String>> {
442        match self {
443            Extension::V0_5_0(ext) => {
444                ext.call_language_server_additional_workspace_configuration(
445                    store,
446                    &language_server_id.0,
447                    &target_language_server_id.0,
448                    resource,
449                )
450                .await
451            }
452            Extension::V0_4_0(ext) => {
453                ext.call_language_server_additional_workspace_configuration(
454                    store,
455                    &language_server_id.0,
456                    &target_language_server_id.0,
457                    resource,
458                )
459                .await
460            }
461            Extension::V0_3_0(_)
462            | Extension::V0_2_0(_)
463            | Extension::V0_1_0(_)
464            | Extension::V0_0_6(_)
465            | Extension::V0_0_4(_)
466            | Extension::V0_0_1(_) => Ok(Ok(None)),
467        }
468    }
469
470    pub async fn call_labels_for_completions(
471        &self,
472        store: &mut Store<WasmState>,
473        language_server_id: &LanguageServerName,
474        completions: Vec<latest::Completion>,
475    ) -> Result<Result<Vec<Option<CodeLabel>>, String>> {
476        match self {
477            Extension::V0_5_0(ext) => {
478                ext.call_labels_for_completions(store, &language_server_id.0, &completions)
479                    .await
480            }
481            Extension::V0_4_0(ext) => Ok(ext
482                .call_labels_for_completions(
483                    store,
484                    &language_server_id.0,
485                    &completions.into_iter().collect::<Vec<_>>(),
486                )
487                .await?
488                .map(|labels| {
489                    labels
490                        .into_iter()
491                        .map(|label| label.map(Into::into))
492                        .collect()
493                })),
494            Extension::V0_3_0(ext) => Ok(ext
495                .call_labels_for_completions(
496                    store,
497                    &language_server_id.0,
498                    &completions.into_iter().collect::<Vec<_>>(),
499                )
500                .await?
501                .map(|labels| {
502                    labels
503                        .into_iter()
504                        .map(|label| label.map(Into::into))
505                        .collect()
506                })),
507            Extension::V0_2_0(ext) => Ok(ext
508                .call_labels_for_completions(
509                    store,
510                    &language_server_id.0,
511                    &completions.into_iter().collect::<Vec<_>>(),
512                )
513                .await?
514                .map(|labels| {
515                    labels
516                        .into_iter()
517                        .map(|label| label.map(Into::into))
518                        .collect()
519                })),
520            Extension::V0_1_0(ext) => Ok(ext
521                .call_labels_for_completions(
522                    store,
523                    &language_server_id.0,
524                    &completions.into_iter().map(Into::into).collect::<Vec<_>>(),
525                )
526                .await?
527                .map(|labels| {
528                    labels
529                        .into_iter()
530                        .map(|label| label.map(Into::into))
531                        .collect()
532                })),
533            Extension::V0_0_6(ext) => Ok(ext
534                .call_labels_for_completions(
535                    store,
536                    &language_server_id.0,
537                    &completions.into_iter().map(Into::into).collect::<Vec<_>>(),
538                )
539                .await?
540                .map(|labels| {
541                    labels
542                        .into_iter()
543                        .map(|label| label.map(Into::into))
544                        .collect()
545                })),
546            Extension::V0_0_1(_) | Extension::V0_0_4(_) => Ok(Ok(Vec::new())),
547        }
548    }
549
550    pub async fn call_labels_for_symbols(
551        &self,
552        store: &mut Store<WasmState>,
553        language_server_id: &LanguageServerName,
554        symbols: Vec<latest::Symbol>,
555    ) -> Result<Result<Vec<Option<CodeLabel>>, String>> {
556        match self {
557            Extension::V0_5_0(ext) => {
558                ext.call_labels_for_symbols(store, &language_server_id.0, &symbols)
559                    .await
560            }
561            Extension::V0_4_0(ext) => Ok(ext
562                .call_labels_for_symbols(
563                    store,
564                    &language_server_id.0,
565                    &symbols.into_iter().collect::<Vec<_>>(),
566                )
567                .await?
568                .map(|labels| {
569                    labels
570                        .into_iter()
571                        .map(|label| label.map(Into::into))
572                        .collect()
573                })),
574            Extension::V0_3_0(ext) => Ok(ext
575                .call_labels_for_symbols(
576                    store,
577                    &language_server_id.0,
578                    &symbols.into_iter().collect::<Vec<_>>(),
579                )
580                .await?
581                .map(|labels| {
582                    labels
583                        .into_iter()
584                        .map(|label| label.map(Into::into))
585                        .collect()
586                })),
587            Extension::V0_2_0(ext) => Ok(ext
588                .call_labels_for_symbols(
589                    store,
590                    &language_server_id.0,
591                    &symbols.into_iter().collect::<Vec<_>>(),
592                )
593                .await?
594                .map(|labels| {
595                    labels
596                        .into_iter()
597                        .map(|label| label.map(Into::into))
598                        .collect()
599                })),
600            Extension::V0_1_0(ext) => Ok(ext
601                .call_labels_for_symbols(
602                    store,
603                    &language_server_id.0,
604                    &symbols.into_iter().map(Into::into).collect::<Vec<_>>(),
605                )
606                .await?
607                .map(|labels| {
608                    labels
609                        .into_iter()
610                        .map(|label| label.map(Into::into))
611                        .collect()
612                })),
613            Extension::V0_0_6(ext) => Ok(ext
614                .call_labels_for_symbols(
615                    store,
616                    &language_server_id.0,
617                    &symbols.into_iter().map(Into::into).collect::<Vec<_>>(),
618                )
619                .await?
620                .map(|labels| {
621                    labels
622                        .into_iter()
623                        .map(|label| label.map(Into::into))
624                        .collect()
625                })),
626            Extension::V0_0_1(_) | Extension::V0_0_4(_) => Ok(Ok(Vec::new())),
627        }
628    }
629
630    pub async fn call_complete_slash_command_argument(
631        &self,
632        store: &mut Store<WasmState>,
633        command: &SlashCommand,
634        arguments: &[String],
635    ) -> Result<Result<Vec<SlashCommandArgumentCompletion>, String>> {
636        match self {
637            Extension::V0_5_0(ext) => {
638                ext.call_complete_slash_command_argument(store, command, arguments)
639                    .await
640            }
641            Extension::V0_4_0(ext) => {
642                ext.call_complete_slash_command_argument(store, command, arguments)
643                    .await
644            }
645            Extension::V0_3_0(ext) => {
646                ext.call_complete_slash_command_argument(store, command, arguments)
647                    .await
648            }
649            Extension::V0_2_0(ext) => {
650                ext.call_complete_slash_command_argument(store, command, arguments)
651                    .await
652            }
653            Extension::V0_1_0(ext) => {
654                ext.call_complete_slash_command_argument(store, command, arguments)
655                    .await
656            }
657            Extension::V0_0_1(_) | Extension::V0_0_4(_) | Extension::V0_0_6(_) => {
658                Ok(Ok(Vec::new()))
659            }
660        }
661    }
662
663    pub async fn call_run_slash_command(
664        &self,
665        store: &mut Store<WasmState>,
666        command: &SlashCommand,
667        arguments: &[String],
668        resource: Option<Resource<Arc<dyn WorktreeDelegate>>>,
669    ) -> Result<Result<SlashCommandOutput, String>> {
670        match self {
671            Extension::V0_5_0(ext) => {
672                ext.call_run_slash_command(store, command, arguments, resource)
673                    .await
674            }
675            Extension::V0_4_0(ext) => {
676                ext.call_run_slash_command(store, command, arguments, resource)
677                    .await
678            }
679            Extension::V0_3_0(ext) => {
680                ext.call_run_slash_command(store, command, arguments, resource)
681                    .await
682            }
683            Extension::V0_2_0(ext) => {
684                ext.call_run_slash_command(store, command, arguments, resource)
685                    .await
686            }
687            Extension::V0_1_0(ext) => {
688                ext.call_run_slash_command(store, command, arguments, resource)
689                    .await
690            }
691            Extension::V0_0_1(_) | Extension::V0_0_4(_) | Extension::V0_0_6(_) => {
692                Err(anyhow!("`run_slash_command` not available prior to v0.1.0"))
693            }
694        }
695    }
696
697    pub async fn call_context_server_command(
698        &self,
699        store: &mut Store<WasmState>,
700        context_server_id: Arc<str>,
701        project: Resource<ExtensionProject>,
702    ) -> Result<Result<Command, String>> {
703        match self {
704            Extension::V0_5_0(ext) => {
705                ext.call_context_server_command(store, &context_server_id, project)
706                    .await
707            }
708            Extension::V0_4_0(ext) => {
709                ext.call_context_server_command(store, &context_server_id, project)
710                    .await
711            }
712            Extension::V0_3_0(ext) => {
713                ext.call_context_server_command(store, &context_server_id, project)
714                    .await
715            }
716            Extension::V0_2_0(ext) => Ok(ext
717                .call_context_server_command(store, &context_server_id, project)
718                .await?
719                .map(Into::into)),
720            Extension::V0_0_1(_)
721            | Extension::V0_0_4(_)
722            | Extension::V0_0_6(_)
723            | Extension::V0_1_0(_) => Err(anyhow!(
724                "`context_server_command` not available prior to v0.2.0"
725            )),
726        }
727    }
728
729    pub async fn call_suggest_docs_packages(
730        &self,
731        store: &mut Store<WasmState>,
732        provider: &str,
733    ) -> Result<Result<Vec<String>, String>> {
734        match self {
735            Extension::V0_5_0(ext) => ext.call_suggest_docs_packages(store, provider).await,
736            Extension::V0_4_0(ext) => ext.call_suggest_docs_packages(store, provider).await,
737            Extension::V0_3_0(ext) => ext.call_suggest_docs_packages(store, provider).await,
738            Extension::V0_2_0(ext) => ext.call_suggest_docs_packages(store, provider).await,
739            Extension::V0_1_0(ext) => ext.call_suggest_docs_packages(store, provider).await,
740            Extension::V0_0_1(_) | Extension::V0_0_4(_) | Extension::V0_0_6(_) => Err(anyhow!(
741                "`suggest_docs_packages` not available prior to v0.1.0"
742            )),
743        }
744    }
745
746    pub async fn call_index_docs(
747        &self,
748        store: &mut Store<WasmState>,
749        provider: &str,
750        package_name: &str,
751        kv_store: Resource<Arc<dyn KeyValueStoreDelegate>>,
752    ) -> Result<Result<(), String>> {
753        match self {
754            Extension::V0_5_0(ext) => {
755                ext.call_index_docs(store, provider, package_name, kv_store)
756                    .await
757            }
758            Extension::V0_4_0(ext) => {
759                ext.call_index_docs(store, provider, package_name, kv_store)
760                    .await
761            }
762            Extension::V0_3_0(ext) => {
763                ext.call_index_docs(store, provider, package_name, kv_store)
764                    .await
765            }
766            Extension::V0_2_0(ext) => {
767                ext.call_index_docs(store, provider, package_name, kv_store)
768                    .await
769            }
770            Extension::V0_1_0(ext) => {
771                ext.call_index_docs(store, provider, package_name, kv_store)
772                    .await
773            }
774            Extension::V0_0_1(_) | Extension::V0_0_4(_) | Extension::V0_0_6(_) => {
775                Err(anyhow!("`index_docs` not available prior to v0.1.0"))
776            }
777        }
778    }
779}
780
781trait ToWasmtimeResult<T> {
782    fn to_wasmtime_result(self) -> wasmtime::Result<Result<T, String>>;
783}
784
785impl<T> ToWasmtimeResult<T> for Result<T> {
786    fn to_wasmtime_result(self) -> wasmtime::Result<Result<T, String>> {
787        Ok(self.map_err(|error| error.to_string()))
788    }
789}