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