proto_pdk_api/api/
mod.rs

1mod build;
2mod checksum;
3mod source;
4
5use crate::shapes::*;
6use derive_setters::Setters;
7use rustc_hash::FxHashMap;
8use std::path::PathBuf;
9use version_spec::{CalVer, SemVer, SpecError, UnresolvedVersionSpec, VersionSpec};
10use warpgate_api::*;
11
12pub use build::*;
13pub use checksum::*;
14pub use semver::{Version, VersionReq};
15pub use source::*;
16
17/// Enumeration of all available plugin functions that can be implemented by plugins.
18///
19/// This enum provides type-safe access to plugin function names and eliminates
20/// the risk of typos when calling plugin functions. Each variant corresponds to
21/// a specific plugin function with its associated input/output types.
22#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
23pub enum PluginFunction {
24    /// Register and configure a tool with proto.
25    ///
26    /// Called when proto first loads a plugin to get basic metadata about the tool
27    /// including its name, type, and configuration schema.
28    ///
29    /// **Input:** [`RegisterToolInput`] | **Output:** [`RegisterToolOutput`]
30    RegisterTool,
31
32    /// Register a backend with proto.
33    ///
34    /// Allows plugins to define custom backends for sourcing tools from locations
35    /// other than the default registry.
36    ///
37    /// **Input:** [`RegisterBackendInput`] | **Output:** [`RegisterBackendOutput`]
38    RegisterBackend,
39
40    /// Detect version files in a project.
41    ///
42    /// Returns a list of file patterns that should be checked for version information
43    /// when auto-detecting tool versions.
44    ///
45    /// **Input:** [`DetectVersionInput`] | **Output:** [`DetectVersionOutput`]
46    DetectVersionFiles,
47
48    /// Parse version information from files.
49    ///
50    /// Extracts version specifications from configuration files like `.nvmrc`,
51    /// `package.json`, `pyproject.toml`, etc.
52    ///
53    /// **Input:** [`ParseVersionFileInput`] | **Output:** [`ParseVersionFileOutput`]
54    ParseVersionFile,
55
56    /// Load available versions for a tool.
57    ///
58    /// Fetches the list of available versions that can be installed, including
59    /// version aliases like "latest" or "lts".
60    ///
61    /// **Input:** [`LoadVersionsInput`] | **Output:** [`LoadVersionsOutput`]
62    LoadVersions,
63
64    /// Resolve version specifications to concrete versions.
65    ///
66    /// Takes version requirements or aliases and resolves them to specific
67    /// installable versions.
68    ///
69    /// **Input:** [`ResolveVersionInput`] | **Output:** [`ResolveVersionOutput`]
70    ResolveVersion,
71
72    /// Download prebuilt tool archives.
73    ///
74    /// Provides URLs and metadata for downloading pre-compiled tool binaries
75    /// instead of building from source.
76    ///
77    /// **Input:** [`DownloadPrebuiltInput`] | **Output:** [`DownloadPrebuiltOutput`]
78    DownloadPrebuilt,
79
80    /// Provide build instructions for tools.
81    ///
82    /// Returns the steps needed to build a tool from source, including dependencies,
83    /// build commands, and environment requirements.
84    ///
85    /// **Input:** [`BuildInstructionsInput`] | **Output:** [`BuildInstructionsOutput`]
86    BuildInstructions,
87
88    /// Unpack downloaded archives.
89    ///
90    /// Handles custom unpacking logic for tool archives when the default extraction
91    /// methods are insufficient.
92    ///
93    /// **Input:** [`UnpackArchiveInput`] | **Output:** None
94    UnpackArchive,
95
96    /// Verify download checksums.
97    ///
98    /// Provides custom checksum verification logic for downloaded tool archives
99    /// to ensure integrity.
100    ///
101    /// **Input:** [`VerifyChecksumInput`] | **Output:** [`VerifyChecksumOutput`]
102    VerifyChecksum,
103
104    /// Native tool installation.
105    ///
106    /// Handles tool installation using the tool's own installation methods rather
107    /// than proto's standard process.
108    ///
109    /// **Input:** [`NativeInstallInput`] | **Output:** [`NativeInstallOutput`]
110    NativeInstall,
111
112    /// Native tool uninstallation.
113    ///
114    /// Handles tool removal using the tool's own uninstallation methods rather
115    /// than simple directory deletion.
116    ///
117    /// **Input:** [`NativeUninstallInput`] | **Output:** [`NativeUninstallOutput`]
118    NativeUninstall,
119
120    /// Locate tool executables.
121    ///
122    /// Identifies where executables are located within an installed tool and
123    /// configures them for proto's shim system.
124    ///
125    /// **Input:** [`LocateExecutablesInput`] | **Output:** [`LocateExecutablesOutput`]
126    LocateExecutables,
127
128    /// Sync the tool manifest.
129    ///
130    /// Allows plugins to update proto's inventory of installed versions with
131    /// external changes.
132    ///
133    /// **Input:** [`SyncManifestInput`] | **Output:** [`SyncManifestOutput`]
134    SyncManifest,
135
136    /// Sync shell profile configuration.
137    ///
138    /// Configures shell environment variables and PATH modifications needed for
139    /// the tool to work properly.
140    ///
141    /// **Input:** [`SyncShellProfileInput`] | **Output:** [`SyncShellProfileOutput`]
142    SyncShellProfile,
143}
144
145impl PluginFunction {
146    /// Get the string representation of the plugin function name.
147    ///
148    /// This returns the actual function name that should be used when calling
149    /// the plugin function via WASM.
150    pub fn as_str(&self) -> &'static str {
151        match self {
152            Self::RegisterTool => "register_tool",
153            Self::RegisterBackend => "register_backend",
154            Self::DetectVersionFiles => "detect_version_files",
155            Self::ParseVersionFile => "parse_version_file",
156            Self::LoadVersions => "load_versions",
157            Self::ResolveVersion => "resolve_version",
158            Self::DownloadPrebuilt => "download_prebuilt",
159            Self::BuildInstructions => "build_instructions",
160            Self::UnpackArchive => "unpack_archive",
161            Self::VerifyChecksum => "verify_checksum",
162            Self::NativeInstall => "native_install",
163            Self::NativeUninstall => "native_uninstall",
164            Self::LocateExecutables => "locate_executables",
165            Self::SyncManifest => "sync_manifest",
166            Self::SyncShellProfile => "sync_shell_profile",
167        }
168    }
169}
170
171impl AsRef<str> for PluginFunction {
172    fn as_ref(&self) -> &str {
173        self.as_str()
174    }
175}
176
177pub(crate) fn is_false(value: &bool) -> bool {
178    !(*value)
179}
180
181pub(crate) fn is_default<T: Default + PartialEq>(value: &T) -> bool {
182    value == &T::default()
183}
184
185api_struct!(
186    /// Information about the current state of the plugin,
187    /// after a version has been resolved.
188    pub struct PluginContext {
189        /// The version of proto (the core crate) calling plugin functions.
190        #[serde(default, skip_serializing_if = "Option::is_none")]
191        pub proto_version: Option<Version>,
192
193        /// Virtual path to the tool's temporary directory.
194        pub temp_dir: VirtualPath,
195
196        /// Virtual path to the tool's installation directory.
197        pub tool_dir: VirtualPath,
198
199        /// Current version. Will be a "latest" alias if not resolved.
200        pub version: VersionSpec,
201    }
202);
203
204api_struct!(
205    /// Information about the current state of the plugin,
206    /// before a version has been resolved.
207    pub struct PluginUnresolvedContext {
208        /// The version of proto (the core crate) calling plugin functions.
209        #[serde(default, skip_serializing_if = "Option::is_none")]
210        pub proto_version: Option<Version>,
211
212        /// Virtual path to the tool's temporary directory.
213        pub temp_dir: VirtualPath,
214
215        /// Current version if defined.
216        #[serde(default, skip_serializing_if = "Option::is_none")]
217        pub version: Option<VersionSpec>,
218
219        // TODO: temporary compat with `ToolContext`
220        #[doc(hidden)]
221        pub tool_dir: VirtualPath,
222    }
223);
224
225api_unit_enum!(
226    /// Supported types of plugins.
227    pub enum PluginType {
228        #[serde(alias = "CLI", alias = "CommandLine")] // TEMP
229        CommandLine,
230        #[default]
231        #[serde(alias = "Language")]
232        Language,
233        #[serde(alias = "PM", alias = "DependencyManager")] // TEMP
234        DependencyManager,
235        #[serde(alias = "VM", alias = "VersionManager")] // TEMP
236        VersionManager,
237    }
238);
239
240api_struct!(
241    /// Input passed to the `register_tool` function.
242    pub struct RegisterToolInput {
243        /// ID of the tool, as it was configured.
244        pub id: String,
245    }
246);
247
248#[deprecated(note = "Use `RegisterToolInput` instead.")]
249pub type ToolMetadataInput = RegisterToolInput;
250
251api_struct!(
252    /// Controls aspects of the tool inventory.
253    #[serde(default)]
254    pub struct ToolInventoryOptions {
255        /// Override the tool inventory directory (where all versions are installed).
256        /// This is an advanced feature and should only be used when absolutely necessary.
257        #[serde(skip_serializing_if = "Option::is_none")]
258        pub override_dir: Option<VirtualPath>,
259
260        /// Suffix to append to all versions when labeling directories.
261        #[serde(skip_serializing_if = "Option::is_none")]
262        pub version_suffix: Option<String>,
263    }
264);
265
266api_unit_enum!(
267    /// Supported strategies for installing a tool.
268    pub enum InstallStrategy {
269        #[serde(alias = "BuildFromSource")] // TEMP
270        BuildFromSource,
271        #[default]
272        #[serde(alias = "DownloadPrebuilt")] // TEMP
273        DownloadPrebuilt,
274    }
275);
276
277api_struct!(
278    /// Options related to lockfile integration.
279    #[serde(default)]
280    pub struct ToolLockOptions {
281        /// Ignore operating system and architecture values
282        /// when matching against records in the lockfile.
283        #[serde(skip_serializing_if = "is_false")]
284        pub ignore_os_arch: bool,
285
286        /// Do not record the install in the lockfile.
287        #[serde(skip_serializing_if = "is_false")]
288        pub no_record: bool,
289    }
290);
291
292api_struct!(
293    /// Output returned by the `register_tool` function.
294    pub struct RegisterToolOutput {
295        /// Schema shape of the tool's configuration.
296        #[serde(default, skip_serializing_if = "Option::is_none")]
297        pub config_schema: Option<schematic::Schema>,
298
299        /// Default strategy to use when installing a tool.
300        #[serde(default)]
301        pub default_install_strategy: InstallStrategy,
302
303        /// Default alias or version to use as a fallback.
304        #[serde(default, skip_serializing_if = "Option::is_none")]
305        pub default_version: Option<UnresolvedVersionSpec>,
306
307        /// List of deprecation messages that will be displayed to users
308        /// of this plugin.
309        #[serde(default, skip_serializing_if = "Vec::is_empty")]
310        pub deprecations: Vec<String>,
311
312        /// Controls aspects of the tool inventory.
313        #[serde(default, skip_serializing_if = "is_default", alias = "inventory")]
314        pub inventory_options: ToolInventoryOptions,
315
316        /// Options for integrating with a lockfile.
317        #[serde(default, skip_serializing_if = "is_default")]
318        pub lock_options: ToolLockOptions,
319
320        /// Minimum version of proto required to execute this plugin.
321        #[serde(default, skip_serializing_if = "Option::is_none")]
322        pub minimum_proto_version: Option<Version>,
323
324        /// Human readable name of the tool.
325        pub name: String,
326
327        /// Version of the plugin.
328        #[serde(default, skip_serializing_if = "Option::is_none")]
329        pub plugin_version: Option<Version>,
330
331        /// Other plugins that this plugin requires.
332        #[serde(default, skip_serializing_if = "Vec::is_empty")]
333        pub requires: Vec<String>,
334
335        /// Names of commands that will self-upgrade the tool,
336        /// and should be blocked from happening.
337        #[serde(default, skip_serializing_if = "Vec::is_empty")]
338        pub self_upgrade_commands: Vec<String>,
339
340        /// Type of the tool.
341        #[serde(rename = "type")]
342        pub type_of: PluginType,
343
344        /// Whether this plugin is unstable or not.
345        #[serde(default)]
346        pub unstable: Switch,
347    }
348);
349
350#[deprecated(note = "Use `RegisterToolOutput` instead.")]
351pub type ToolMetadataOutput = RegisterToolOutput;
352
353// BACKEND
354
355api_struct!(
356    /// Input passed to the `register_backend` function.
357    pub struct RegisterBackendInput {
358        /// Current tool context.
359        pub context: PluginUnresolvedContext,
360
361        /// ID of the tool, as it was configured.
362        pub id: String,
363    }
364);
365
366api_struct!(
367    /// Output returned by the `register_backend` function.
368    pub struct RegisterBackendOutput {
369        /// Unique identifier for this backend. Will be used as the folder name.
370        pub backend_id: String,
371
372        /// List of executables, relative from the backend directory,
373        /// that will be executed in the context of proto.
374        #[serde(default, skip_serializing_if = "Vec::is_empty")]
375        pub exes: Vec<PathBuf>,
376
377        /// Location in which to acquire source files for the backend.
378        #[serde(default, skip_serializing_if = "Option::is_none")]
379        pub source: Option<SourceLocation>,
380    }
381);
382
383// VERSION DETECTION
384
385api_struct!(
386    /// Input passed to the `detect_version_files` function.
387    pub struct DetectVersionInput {
388        /// Current tool context.
389        pub context: PluginUnresolvedContext,
390    }
391);
392
393api_struct!(
394    /// Output returned by the `detect_version_files` function.
395    #[serde(default)]
396    pub struct DetectVersionOutput {
397        /// List of files that should be checked for version information.
398        #[serde(skip_serializing_if = "Vec::is_empty")]
399        pub files: Vec<String>,
400
401        /// List of path patterns to ignore when traversing directories.
402        #[serde(skip_serializing_if = "Vec::is_empty")]
403        pub ignore: Vec<String>,
404    }
405);
406
407api_struct!(
408    /// Input passed to the `parse_version_file` function.
409    pub struct ParseVersionFileInput {
410        /// File contents to parse/extract a version from.
411        pub content: String,
412
413        /// Current tool context.
414        pub context: PluginUnresolvedContext,
415
416        /// Name of file that's being parsed.
417        pub file: String,
418
419        /// Virtual path to the file being parsed.
420        pub path: VirtualPath,
421    }
422);
423
424api_struct!(
425    /// Output returned by the `parse_version_file` function.
426    #[serde(default)]
427    pub struct ParseVersionFileOutput {
428        /// The version that was extracted from the file.
429        /// Can be a semantic version or a version requirement/range.
430        #[serde(skip_serializing_if = "Option::is_none")]
431        pub version: Option<UnresolvedVersionSpec>,
432    }
433);
434
435// DOWNLOAD, BUILD, INSTALL, VERIFY
436
437api_struct!(
438    /// Input passed to the `native_install` function.
439    pub struct NativeInstallInput {
440        /// Current tool context.
441        pub context: PluginContext,
442
443        /// Virtual directory to install to.
444        pub install_dir: VirtualPath,
445    }
446);
447
448api_struct!(
449    /// Output returned by the `native_install` function.
450    pub struct NativeInstallOutput {
451        /// A checksum/hash that was generated.
452        #[serde(default, skip_serializing_if = "Option::is_none")]
453        pub checksum: Option<Checksum>,
454
455        /// Error message if the install failed.
456        #[serde(default, skip_serializing_if = "Option::is_none")]
457        pub error: Option<String>,
458
459        /// Whether the install was successful.
460        pub installed: bool,
461
462        /// Whether to skip the install process or not.
463        #[serde(default)]
464        pub skip_install: bool,
465    }
466);
467
468api_struct!(
469    /// Input passed to the `native_uninstall` function.
470    pub struct NativeUninstallInput {
471        /// Current tool context.
472        pub context: PluginContext,
473
474        /// Virtual directory to uninstall from.
475        pub uninstall_dir: VirtualPath,
476    }
477);
478
479api_struct!(
480    /// Output returned by the `native_uninstall` function.
481    pub struct NativeUninstallOutput {
482        /// Error message if the uninstall failed.
483        #[serde(default, skip_serializing_if = "Option::is_none")]
484        pub error: Option<String>,
485
486        /// Whether the install was successful.
487        pub uninstalled: bool,
488
489        /// Whether to skip the uninstall process or not.
490        #[serde(default)]
491        pub skip_uninstall: bool,
492    }
493);
494
495api_struct!(
496    /// Input passed to the `download_prebuilt` function.
497    pub struct DownloadPrebuiltInput {
498        /// Current tool context.
499        pub context: PluginContext,
500
501        /// Virtual directory to install to.
502        pub install_dir: VirtualPath,
503    }
504);
505
506api_struct!(
507    /// Output returned by the `download_prebuilt` function.
508    pub struct DownloadPrebuiltOutput {
509        /// Name of the direct folder within the archive that contains the tool,
510        /// and will be removed when unpacking the archive.
511        #[serde(default, skip_serializing_if = "Option::is_none")]
512        pub archive_prefix: Option<String>,
513
514        /// The checksum hash itself.
515        #[serde(default, skip_serializing_if = "Option::is_none")]
516        pub checksum: Option<Checksum>,
517
518        /// File name of the checksum to download. If not provided,
519        /// will attempt to extract it from the URL.
520        #[serde(default, skip_serializing_if = "Option::is_none")]
521        pub checksum_name: Option<String>,
522
523        /// Public key to use for checksum verification.
524        #[serde(default, skip_serializing_if = "Option::is_none")]
525        pub checksum_public_key: Option<String>,
526
527        /// A secure URL to download the checksum file for verification.
528        /// If the tool does not support checksum verification, this setting can be omitted.
529        #[serde(default, skip_serializing_if = "Option::is_none")]
530        pub checksum_url: Option<String>,
531
532        /// File name of the archive to download. If not provided,
533        /// will attempt to extract it from the URL.
534        #[serde(default, skip_serializing_if = "Option::is_none")]
535        pub download_name: Option<String>,
536
537        /// A secure URL to download the tool/archive.
538        pub download_url: String,
539    }
540);
541
542api_struct!(
543    /// Input passed to the `unpack_archive` function.
544    pub struct UnpackArchiveInput {
545        /// Current tool context.
546        pub context: PluginContext,
547
548        /// Virtual path to the downloaded file.
549        pub input_file: VirtualPath,
550
551        /// Virtual directory to unpack the archive into, or copy the executable to.
552        pub output_dir: VirtualPath,
553    }
554);
555
556api_struct!(
557    /// Output returned by the `verify_checksum` function.
558    pub struct VerifyChecksumInput {
559        /// Current tool context.
560        pub context: PluginContext,
561
562        /// Virtual path to the checksum file.
563        pub checksum_file: VirtualPath,
564
565        /// A checksum of the downloaded file. The type of hash
566        /// is derived from the checksum file's extension, otherwise
567        /// it defaults to SHA256.
568        #[serde(default, skip_serializing_if = "Option::is_none")]
569        pub download_checksum: Option<Checksum>,
570
571        /// Virtual path to the downloaded file.
572        pub download_file: VirtualPath,
573    }
574);
575
576api_struct!(
577    /// Output returned by the `verify_checksum` function.
578    pub struct VerifyChecksumOutput {
579        /// Was the checksum correct?
580        pub verified: bool,
581    }
582);
583
584// EXECUTABLES, BINARYS, GLOBALS
585
586api_struct!(
587    /// Input passed to the `locate_executables` function.
588    pub struct LocateExecutablesInput {
589        /// Current tool context.
590        pub context: PluginContext,
591
592        /// Virtual directory the tool was installed to.
593        pub install_dir: VirtualPath,
594    }
595);
596
597api_struct!(
598    /// Configuration for generated shim and symlinked executable files.
599    #[derive(Setters)]
600    #[serde(default)]
601    pub struct ExecutableConfig {
602        /// The file to execute, relative from the tool directory.
603        /// Does *not* support virtual paths.
604        #[setters(strip_option)]
605        #[serde(skip_serializing_if = "Option::is_none")]
606        pub exe_path: Option<PathBuf>,
607
608        /// The executable path to use for symlinking instead of `exe_path`.
609        /// This should only be used when `exe_path` is a non-standard executable.
610        #[setters(strip_option)]
611        #[serde(skip_serializing_if = "Option::is_none")]
612        pub exe_link_path: Option<PathBuf>,
613
614        /// Do not symlink a binary in `~/.proto/bin`.
615        #[serde(skip_serializing_if = "is_false")]
616        pub no_bin: bool,
617
618        /// Do not generate a shim in `~/.proto/shims`.
619        #[serde(skip_serializing_if = "is_false")]
620        pub no_shim: bool,
621
622        /// List of arguments to append to the parent executable, but prepend before
623        /// all other arguments.
624        #[serde(skip_serializing_if = "Vec::is_empty")]
625        pub parent_exe_args: Vec<String>,
626
627        /// The parent executable name required to execute the local executable path.
628        #[setters(into, strip_option)]
629        #[serde(skip_serializing_if = "Option::is_none")]
630        pub parent_exe_name: Option<String>,
631
632        /// Whether this is the primary executable or not.
633        #[serde(skip_serializing_if = "is_false")]
634        pub primary: bool,
635
636        /// Custom args to prepend to user-provided args within the generated shim.
637        #[setters(strip_option)]
638        #[serde(skip_serializing_if = "Option::is_none")]
639        pub shim_before_args: Option<StringOrVec>,
640
641        /// Custom args to append to user-provided args within the generated shim.
642        #[setters(strip_option)]
643        #[serde(skip_serializing_if = "Option::is_none")]
644        pub shim_after_args: Option<StringOrVec>,
645
646        /// Custom environment variables to set when executing the shim.
647        #[setters(strip_option)]
648        #[serde(skip_serializing_if = "Option::is_none")]
649        pub shim_env_vars: Option<FxHashMap<String, String>>,
650    }
651);
652
653impl ExecutableConfig {
654    pub fn new<T: AsRef<str>>(exe_path: T) -> Self {
655        Self {
656            exe_path: Some(PathBuf::from(exe_path.as_ref())),
657            ..ExecutableConfig::default()
658        }
659    }
660
661    pub fn new_primary<T: AsRef<str>>(exe_path: T) -> Self {
662        Self {
663            exe_path: Some(PathBuf::from(exe_path.as_ref())),
664            primary: true,
665            ..ExecutableConfig::default()
666        }
667    }
668
669    pub fn with_parent<T: AsRef<str>, P: AsRef<str>>(exe_path: T, parent_exe: P) -> Self {
670        Self {
671            exe_path: Some(PathBuf::from(exe_path.as_ref())),
672            parent_exe_name: Some(parent_exe.as_ref().to_owned()),
673            ..ExecutableConfig::default()
674        }
675    }
676}
677
678api_struct!(
679    /// Output returned by the `locate_executables` function.
680    #[serde(default)]
681    pub struct LocateExecutablesOutput {
682        /// Configures executable information to be used as proto bins/shims.
683        /// The map key will be the name of the executable file.
684        #[serde(skip_serializing_if = "FxHashMap::is_empty")]
685        pub exes: FxHashMap<String, ExecutableConfig>,
686
687        #[deprecated(note = "Use `exes_dirs` instead.")]
688        #[serde(skip_serializing_if = "Option::is_none")]
689        pub exes_dir: Option<PathBuf>,
690
691        /// Relative directory path from the tool install directory in which
692        /// pre-installed executables can be located. This directory path
693        /// will be used during `proto activate`, but not for bins/shims.
694        #[serde(skip_serializing_if = "Vec::is_empty")]
695        pub exes_dirs: Vec<PathBuf>,
696
697        /// List of directory paths to find the globals installation directory.
698        /// Each path supports environment variable expansion.
699        #[serde(skip_serializing_if = "Vec::is_empty")]
700        pub globals_lookup_dirs: Vec<String>,
701
702        /// A string that all global executables are prefixed with, and will be removed
703        /// when listing and filtering available globals.
704        #[serde(skip_serializing_if = "Option::is_none")]
705        pub globals_prefix: Option<String>,
706    }
707);
708
709// VERSION RESOLVING
710
711api_struct!(
712    /// Input passed to the `load_versions` function.
713    pub struct LoadVersionsInput {
714        /// Current tool context.
715        pub context: PluginUnresolvedContext,
716
717        /// The alias or version currently being resolved.
718        pub initial: UnresolvedVersionSpec,
719    }
720);
721
722api_struct!(
723    /// Output returned by the `load_versions` function.
724    #[serde(default)]
725    pub struct LoadVersionsOutput {
726        /// Latest canary version.
727        #[serde(skip_serializing_if = "Option::is_none")]
728        pub canary: Option<UnresolvedVersionSpec>,
729
730        /// Latest stable version.
731        #[serde(skip_serializing_if = "Option::is_none")]
732        pub latest: Option<UnresolvedVersionSpec>,
733
734        /// Mapping of aliases (channels, etc) to a version.
735        #[serde(skip_serializing_if = "FxHashMap::is_empty")]
736        pub aliases: FxHashMap<String, UnresolvedVersionSpec>,
737
738        /// List of available production versions to install.
739        #[serde(skip_serializing_if = "Vec::is_empty")]
740        pub versions: Vec<VersionSpec>,
741    }
742);
743
744impl LoadVersionsOutput {
745    /// Create the output from a list of strings that'll be parsed as versions.
746    /// The latest version will be the highest version number.
747    pub fn from(values: Vec<String>) -> Result<Self, SpecError> {
748        let mut versions = vec![];
749
750        for value in values {
751            versions.push(VersionSpec::parse(&value)?);
752        }
753
754        Ok(Self::from_versions(versions))
755    }
756
757    /// Create the output from a list of version specifications.
758    /// The latest version will be the highest version number.
759    pub fn from_versions(versions: Vec<VersionSpec>) -> Self {
760        let mut output = LoadVersionsOutput::default();
761        let mut latest = Version::new(0, 0, 0);
762        let mut calver = false;
763
764        for version in versions {
765            if let Some(inner) = version.as_version() {
766                if inner.pre.is_empty() && inner.build.is_empty() && inner > &latest {
767                    inner.clone_into(&mut latest);
768                    calver = matches!(version, VersionSpec::Calendar(_));
769                }
770            }
771
772            output.versions.push(version);
773        }
774
775        output.latest = Some(if calver {
776            UnresolvedVersionSpec::Calendar(CalVer(latest))
777        } else {
778            UnresolvedVersionSpec::Semantic(SemVer(latest))
779        });
780
781        output
782            .aliases
783            .insert("latest".into(), output.latest.clone().unwrap());
784
785        output
786    }
787}
788
789api_struct!(
790    /// Input passed to the `resolve_version` function.
791    pub struct ResolveVersionInput {
792        /// Current tool context.
793        pub context: PluginUnresolvedContext,
794
795        /// The alias or version currently being resolved.
796        pub initial: UnresolvedVersionSpec,
797    }
798);
799
800api_struct!(
801    /// Output returned by the `resolve_version` function.
802    #[serde(default)]
803    pub struct ResolveVersionOutput {
804        /// New alias or version candidate to resolve.
805        #[serde(skip_serializing_if = "Option::is_none")]
806        pub candidate: Option<UnresolvedVersionSpec>,
807
808        /// An explicitly resolved version to be used as-is.
809        /// Note: Only use this field if you know what you're doing!
810        #[serde(skip_serializing_if = "Option::is_none")]
811        pub version: Option<VersionSpec>,
812    }
813);
814
815// MISCELLANEOUS
816
817api_struct!(
818    /// Input passed to the `sync_manifest` function.
819    pub struct SyncManifestInput {
820        /// Current tool context.
821        pub context: PluginContext,
822    }
823);
824
825api_struct!(
826    /// Output returned by the `sync_manifest` function.
827    #[serde(default)]
828    pub struct SyncManifestOutput {
829        /// List of versions that are currently installed. Will replace
830        /// what is currently in the manifest.
831        #[serde(skip_serializing_if = "Option::is_none")]
832        pub versions: Option<Vec<VersionSpec>>,
833
834        /// Whether to skip the syncing process or not.
835        pub skip_sync: bool,
836    }
837);
838
839api_struct!(
840    /// Input passed to the `sync_shell_profile` function.
841    pub struct SyncShellProfileInput {
842        /// Current tool context.
843        pub context: PluginContext,
844
845        /// Arguments passed after `--` that was directly passed to the tool's executable.
846        pub passthrough_args: Vec<String>,
847    }
848);
849
850api_struct!(
851    /// Output returned by the `sync_shell_profile` function.
852    pub struct SyncShellProfileOutput {
853        /// An environment variable to check for in the shell profile.
854        /// If the variable exists, injecting path and exports will be avoided.
855        pub check_var: String,
856
857        /// A mapping of environment variables that will be injected as exports.
858        #[serde(default, skip_serializing_if = "Option::is_none")]
859        pub export_vars: Option<FxHashMap<String, String>>,
860
861        /// A list of paths to prepend to the `PATH` environment variable.
862        #[serde(default, skip_serializing_if = "Option::is_none")]
863        pub extend_path: Option<Vec<String>>,
864
865        /// Whether to skip the syncing process or not.
866        #[serde(default)]
867        pub skip_sync: bool,
868    }
869);