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