Skip to main content

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