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