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