proto_pdk_api/api/
mod.rs

1mod build;
2mod checksum;
3mod source;
4
5use crate::shapes::*;
6use rustc_hash::FxHashMap;
7use std::path::PathBuf;
8use version_spec::{CalVer, SemVer, SpecError, UnresolvedVersionSpec, VersionSpec};
9use warpgate_api::*;
10
11pub use build::*;
12pub use checksum::*;
13pub use semver::{Version, VersionReq};
14pub use source::*;
15
16pub(crate) fn is_false(value: &bool) -> bool {
17    !(*value)
18}
19
20api_struct!(
21    /// Information about the current state of the tool.
22    pub struct ToolContext {
23        /// The version of proto (the core crate) calling plugin functions.
24        #[serde(default, skip_serializing_if = "Option::is_none")]
25        pub proto_version: Option<Version>,
26
27        /// Virtual path to the tool's temporary directory.
28        pub temp_dir: VirtualPath,
29
30        /// Virtual path to the tool's installation directory.
31        pub tool_dir: VirtualPath,
32
33        /// Current version. Will be a "latest" alias if not resolved.
34        pub version: VersionSpec,
35    }
36);
37
38api_struct!(
39    /// Information about the current state of the tool.
40    pub struct ToolUnresolvedContext {
41        /// The version of proto (the core crate) calling plugin functions.
42        #[serde(default, skip_serializing_if = "Option::is_none")]
43        pub proto_version: Option<Version>,
44
45        /// Virtual path to the tool's temporary directory.
46        pub temp_dir: VirtualPath,
47
48        /// Current version if defined.
49        #[serde(default, skip_serializing_if = "Option::is_none")]
50        pub version: Option<VersionSpec>,
51
52        // TODO: temporary compat with `ToolContext`
53        #[doc(hidden)]
54        pub tool_dir: VirtualPath,
55    }
56);
57
58api_unit_enum!(
59    /// Supported types of plugins.
60    pub enum PluginType {
61        #[serde(alias = "CLI", alias = "CommandLine")] // TEMP
62        CommandLine,
63        #[default]
64        #[serde(alias = "Language")]
65        Language,
66        #[serde(alias = "PM", alias = "DependencyManager")] // TEMP
67        DependencyManager,
68        #[serde(alias = "VM", alias = "VersionManager")] // TEMP
69        VersionManager,
70    }
71);
72
73api_struct!(
74    /// Input passed to the `register_tool` function.
75    pub struct RegisterToolInput {
76        /// ID of the tool, as it was configured.
77        pub id: String,
78    }
79);
80
81#[deprecated(note = "Use `RegisterToolInput` instead.")]
82pub type ToolMetadataInput = RegisterToolInput;
83
84api_struct!(
85    /// Controls aspects of the tool inventory.
86    #[serde(default)]
87    pub struct ToolInventoryMetadata {
88        /// Override the tool inventory directory (where all versions are installed).
89        /// This is an advanced feature and should only be used when absolutely necessary.
90        #[serde(skip_serializing_if = "Option::is_none")]
91        pub override_dir: Option<VirtualPath>,
92
93        /// Suffix to append to all versions when labeling directories.
94        #[serde(skip_serializing_if = "Option::is_none")]
95        pub version_suffix: Option<String>,
96    }
97);
98
99api_unit_enum!(
100    /// Supported strategies for installing a tool.
101    pub enum InstallStrategy {
102        #[serde(alias = "BuildFromSource")] // TEMP
103        BuildFromSource,
104        #[default]
105        #[serde(alias = "DownloadPrebuilt")] // TEMP
106        DownloadPrebuilt,
107    }
108);
109
110api_struct!(
111    /// Output returned by the `register_tool` function.
112    pub struct RegisterToolOutput {
113        /// Schema shape of the tool's configuration.
114        #[serde(default, skip_serializing_if = "Option::is_none")]
115        pub config_schema: Option<schematic::Schema>,
116
117        /// Default strategy to use when installing a tool.
118        #[serde(default)]
119        pub default_install_strategy: InstallStrategy,
120
121        /// Default alias or version to use as a fallback.
122        #[serde(default, skip_serializing_if = "Option::is_none")]
123        pub default_version: Option<UnresolvedVersionSpec>,
124
125        /// List of deprecation messages that will be displayed to users
126        /// of this plugin.
127        #[serde(default, skip_serializing_if = "Vec::is_empty")]
128        pub deprecations: Vec<String>,
129
130        /// Controls aspects of the tool inventory.
131        #[serde(default)]
132        pub inventory: ToolInventoryMetadata,
133
134        /// Minimum version of proto required to execute this plugin.
135        #[serde(default, skip_serializing_if = "Option::is_none")]
136        pub minimum_proto_version: Option<Version>,
137
138        /// Human readable name of the tool.
139        pub name: String,
140
141        /// Version of the plugin.
142        #[serde(default, skip_serializing_if = "Option::is_none")]
143        pub plugin_version: Option<Version>,
144
145        /// Other plugins that this plugin requires.
146        #[serde(default, skip_serializing_if = "Vec::is_empty")]
147        pub requires: Vec<String>,
148
149        /// Names of commands that will self-upgrade the tool,
150        /// and should be blocked from happening.
151        #[serde(default, skip_serializing_if = "Vec::is_empty")]
152        pub self_upgrade_commands: Vec<String>,
153
154        /// Type of the tool.
155        #[serde(rename = "type")]
156        pub type_of: PluginType,
157
158        /// Whether this plugin is unstable or not.
159        #[serde(default)]
160        pub unstable: Switch,
161    }
162);
163
164#[deprecated(note = "Use `RegisterToolOutput` instead.")]
165pub type ToolMetadataOutput = RegisterToolOutput;
166
167// BACKEND
168
169api_struct!(
170    /// Input passed to the `register_backend` function.
171    pub struct RegisterBackendInput {
172        /// Current tool context.
173        pub context: ToolUnresolvedContext,
174
175        /// ID of the tool, as it was configured.
176        pub id: String,
177    }
178);
179
180api_struct!(
181    /// Output returned by the `register_backend` function.
182    pub struct RegisterBackendOutput {
183        /// Unique identifier for this backend. Will be used as the folder name.
184        pub backend_id: String,
185
186        /// List of executables, relative from the backend directory,
187        /// that will be executed in the context of proto.
188        #[serde(default, skip_serializing_if = "Vec::is_empty")]
189        pub exes: Vec<PathBuf>,
190
191        /// Location in which to acquire source files for the backend.
192        #[serde(default, skip_serializing_if = "Option::is_none")]
193        pub source: Option<SourceLocation>,
194    }
195);
196
197// VERSION DETECTION
198
199api_struct!(
200    /// Input passed to the `detect_version_files` function.
201    pub struct DetectVersionInput {
202        /// Current tool context.
203        pub context: ToolUnresolvedContext,
204    }
205);
206
207api_struct!(
208    /// Output returned by the `detect_version_files` function.
209    #[serde(default)]
210    pub struct DetectVersionOutput {
211        /// List of files that should be checked for version information.
212        #[serde(skip_serializing_if = "Vec::is_empty")]
213        pub files: Vec<String>,
214
215        /// List of path patterns to ignore when traversing directories.
216        #[serde(skip_serializing_if = "Vec::is_empty")]
217        pub ignore: Vec<String>,
218    }
219);
220
221api_struct!(
222    /// Input passed to the `parse_version_file` function.
223    pub struct ParseVersionFileInput {
224        /// File contents to parse/extract a version from.
225        pub content: String,
226
227        /// Current tool context.
228        pub context: ToolUnresolvedContext,
229
230        /// Name of file that's being parsed.
231        pub file: String,
232
233        /// Virtual path to the file being parsed.
234        pub path: VirtualPath,
235    }
236);
237
238api_struct!(
239    /// Output returned by the `parse_version_file` function.
240    #[serde(default)]
241    pub struct ParseVersionFileOutput {
242        /// The version that was extracted from the file.
243        /// Can be a semantic version or a version requirement/range.
244        #[serde(skip_serializing_if = "Option::is_none")]
245        pub version: Option<UnresolvedVersionSpec>,
246    }
247);
248
249// DOWNLOAD, BUILD, INSTALL, VERIFY
250
251api_struct!(
252    /// Input passed to the `native_install` function.
253    pub struct NativeInstallInput {
254        /// Current tool context.
255        pub context: ToolContext,
256
257        /// Virtual directory to install to.
258        pub install_dir: VirtualPath,
259    }
260);
261
262api_struct!(
263    /// Output returned by the `native_install` function.
264    pub struct NativeInstallOutput {
265        /// A checksum/hash that was generated.
266        #[serde(default, skip_serializing_if = "Option::is_none")]
267        pub checksum: Option<Checksum>,
268
269        /// Error message if the install failed.
270        #[serde(default, skip_serializing_if = "Option::is_none")]
271        pub error: Option<String>,
272
273        /// Whether the install was successful.
274        pub installed: bool,
275
276        /// Whether to skip the install process or not.
277        #[serde(default)]
278        pub skip_install: bool,
279    }
280);
281
282api_struct!(
283    /// Input passed to the `native_uninstall` function.
284    pub struct NativeUninstallInput {
285        /// Current tool context.
286        pub context: ToolContext,
287
288        /// Virtual directory to uninstall from.
289        pub uninstall_dir: VirtualPath,
290    }
291);
292
293api_struct!(
294    /// Output returned by the `native_uninstall` function.
295    pub struct NativeUninstallOutput {
296        /// Error message if the uninstall failed.
297        #[serde(default, skip_serializing_if = "Option::is_none")]
298        pub error: Option<String>,
299
300        /// Whether the install was successful.
301        pub uninstalled: bool,
302
303        /// Whether to skip the uninstall process or not.
304        #[serde(default)]
305        pub skip_uninstall: bool,
306    }
307);
308
309api_struct!(
310    /// Input passed to the `download_prebuilt` function.
311    pub struct DownloadPrebuiltInput {
312        /// Current tool context.
313        pub context: ToolContext,
314
315        /// Virtual directory to install to.
316        pub install_dir: VirtualPath,
317    }
318);
319
320api_struct!(
321    /// Output returned by the `download_prebuilt` function.
322    pub struct DownloadPrebuiltOutput {
323        /// Name of the direct folder within the archive that contains the tool,
324        /// and will be removed when unpacking the archive.
325        #[serde(default, skip_serializing_if = "Option::is_none")]
326        pub archive_prefix: Option<String>,
327
328        /// The checksum hash itself.
329        #[serde(default, skip_serializing_if = "Option::is_none")]
330        pub checksum: Option<Checksum>,
331
332        /// File name of the checksum to download. If not provided,
333        /// will attempt to extract it from the URL.
334        #[serde(default, skip_serializing_if = "Option::is_none")]
335        pub checksum_name: Option<String>,
336
337        /// Public key to use for checksum verification.
338        #[serde(default, skip_serializing_if = "Option::is_none")]
339        pub checksum_public_key: Option<String>,
340
341        /// A secure URL to download the checksum file for verification.
342        /// If the tool does not support checksum verification, this setting can be omitted.
343        #[serde(default, skip_serializing_if = "Option::is_none")]
344        pub checksum_url: Option<String>,
345
346        /// File name of the archive to download. If not provided,
347        /// will attempt to extract it from the URL.
348        #[serde(default, skip_serializing_if = "Option::is_none")]
349        pub download_name: Option<String>,
350
351        /// A secure URL to download the tool/archive.
352        pub download_url: String,
353    }
354);
355
356api_struct!(
357    /// Input passed to the `unpack_archive` function.
358    pub struct UnpackArchiveInput {
359        /// Current tool context.
360        pub context: ToolContext,
361
362        /// Virtual path to the downloaded file.
363        pub input_file: VirtualPath,
364
365        /// Virtual directory to unpack the archive into, or copy the binary to.
366        pub output_dir: VirtualPath,
367    }
368);
369
370api_struct!(
371    /// Output returned by the `verify_checksum` function.
372    pub struct VerifyChecksumInput {
373        /// Current tool context.
374        pub context: ToolContext,
375
376        /// Virtual path to the checksum file.
377        pub checksum_file: VirtualPath,
378
379        /// A checksum of the downloaded file. The type of hash
380        /// is derived from the checksum file's extension, otherwise
381        /// it defaults to SHA256.
382        #[serde(default, skip_serializing_if = "Option::is_none")]
383        pub download_checksum: Option<Checksum>,
384
385        /// Virtual path to the downloaded file.
386        pub download_file: VirtualPath,
387    }
388);
389
390api_struct!(
391    /// Output returned by the `verify_checksum` function.
392    pub struct VerifyChecksumOutput {
393        /// Was the checksum correct?
394        pub verified: bool,
395    }
396);
397
398// EXECUTABLES, BINARYS, GLOBALS
399
400api_struct!(
401    /// Input passed to the `locate_executables` function.
402    pub struct LocateExecutablesInput {
403        /// Current tool context.
404        pub context: ToolContext,
405
406        /// Virtual directory the tool was installed to.
407        pub install_dir: VirtualPath,
408    }
409);
410
411api_struct!(
412    /// Configuration for generated shim and symlinked binary files.
413    #[serde(default)]
414    pub struct ExecutableConfig {
415        /// The file to execute, relative from the tool directory.
416        /// Does *not* support virtual paths.
417        #[serde(skip_serializing_if = "Option::is_none")]
418        pub exe_path: Option<PathBuf>,
419
420        /// The executable path to use for symlinking binaries instead of `exe_path`.
421        /// This should only be used when `exe_path` is a non-standard executable.
422        #[serde(skip_serializing_if = "Option::is_none")]
423        pub exe_link_path: Option<PathBuf>,
424
425        /// Do not symlink a binary in `~/.proto/bin`.
426        #[serde(skip_serializing_if = "is_false")]
427        pub no_bin: bool,
428
429        /// Do not generate a shim in `~/.proto/shims`.
430        #[serde(skip_serializing_if = "is_false")]
431        pub no_shim: bool,
432
433        /// List of arguments to append to the parent executable, but prepend before
434        /// all other arguments.
435        #[serde(skip_serializing_if = "Vec::is_empty")]
436        pub parent_exe_args: Vec<String>,
437
438        /// The parent executable name required to execute the local executable path.
439        #[serde(skip_serializing_if = "Option::is_none")]
440        pub parent_exe_name: Option<String>,
441
442        /// Whether this is the primary executable or not.
443        #[serde(skip_serializing_if = "is_false")]
444        pub primary: bool,
445
446        /// Custom args to prepend to user-provided args within the generated shim.
447        #[serde(skip_serializing_if = "Option::is_none")]
448        pub shim_before_args: Option<StringOrVec>,
449
450        /// Custom args to append to user-provided args within the generated shim.
451        #[serde(skip_serializing_if = "Option::is_none")]
452        pub shim_after_args: Option<StringOrVec>,
453
454        /// Custom environment variables to set when executing the shim.
455        #[serde(skip_serializing_if = "Option::is_none")]
456        pub shim_env_vars: Option<FxHashMap<String, String>>,
457    }
458);
459
460impl ExecutableConfig {
461    pub fn new<T: AsRef<str>>(exe_path: T) -> Self {
462        Self {
463            exe_path: Some(PathBuf::from(exe_path.as_ref())),
464            ..ExecutableConfig::default()
465        }
466    }
467
468    pub fn new_primary<T: AsRef<str>>(exe_path: T) -> Self {
469        Self {
470            exe_path: Some(PathBuf::from(exe_path.as_ref())),
471            primary: true,
472            ..ExecutableConfig::default()
473        }
474    }
475
476    pub fn with_parent<T: AsRef<str>, P: AsRef<str>>(exe_path: T, parent_exe: P) -> Self {
477        Self {
478            exe_path: Some(PathBuf::from(exe_path.as_ref())),
479            parent_exe_name: Some(parent_exe.as_ref().to_owned()),
480            ..ExecutableConfig::default()
481        }
482    }
483}
484
485api_struct!(
486    /// Output returned by the `locate_executables` function.
487    #[serde(default)]
488    pub struct LocateExecutablesOutput {
489        /// Configures executable information to be used as proto bins/shims.
490        /// The map key will be the name of the executable file.
491        #[serde(skip_serializing_if = "FxHashMap::is_empty")]
492        pub exes: FxHashMap<String, ExecutableConfig>,
493
494        #[deprecated(note = "Use `exes_dirs` instead.")]
495        #[serde(skip_serializing_if = "Option::is_none")]
496        pub exes_dir: Option<PathBuf>,
497
498        /// Relative directory path from the tool install directory in which
499        /// pre-installed executables can be located. This directory path
500        /// will be used during `proto activate`, but not for bins/shims.
501        #[serde(skip_serializing_if = "Vec::is_empty")]
502        pub exes_dirs: Vec<PathBuf>,
503
504        /// List of directory paths to find the globals installation directory.
505        /// Each path supports environment variable expansion.
506        #[serde(skip_serializing_if = "Vec::is_empty")]
507        pub globals_lookup_dirs: Vec<String>,
508
509        /// A string that all global binaries are prefixed with, and will be removed
510        /// when listing and filtering available globals.
511        #[serde(skip_serializing_if = "Option::is_none")]
512        pub globals_prefix: Option<String>,
513    }
514);
515
516// VERSION RESOLVING
517
518api_struct!(
519    /// Input passed to the `load_versions` function.
520    pub struct LoadVersionsInput {
521        /// Current tool context.
522        pub context: ToolUnresolvedContext,
523
524        /// The alias or version currently being resolved.
525        pub initial: UnresolvedVersionSpec,
526    }
527);
528
529api_struct!(
530    /// Output returned by the `load_versions` function.
531    #[serde(default)]
532    pub struct LoadVersionsOutput {
533        /// Latest canary version.
534        #[serde(skip_serializing_if = "Option::is_none")]
535        pub canary: Option<UnresolvedVersionSpec>,
536
537        /// Latest stable version.
538        #[serde(skip_serializing_if = "Option::is_none")]
539        pub latest: Option<UnresolvedVersionSpec>,
540
541        /// Mapping of aliases (channels, etc) to a version.
542        #[serde(skip_serializing_if = "FxHashMap::is_empty")]
543        pub aliases: FxHashMap<String, UnresolvedVersionSpec>,
544
545        /// List of available production versions to install.
546        #[serde(skip_serializing_if = "Vec::is_empty")]
547        pub versions: Vec<VersionSpec>,
548    }
549);
550
551impl LoadVersionsOutput {
552    /// Create the output from a list of strings that'll be parsed as versions.
553    /// The latest version will be the highest version number.
554    pub fn from(values: Vec<String>) -> Result<Self, SpecError> {
555        let mut versions = vec![];
556
557        for value in values {
558            versions.push(VersionSpec::parse(&value)?);
559        }
560
561        Ok(Self::from_versions(versions))
562    }
563
564    /// Create the output from a list of version specifications.
565    /// The latest version will be the highest version number.
566    pub fn from_versions(versions: Vec<VersionSpec>) -> Self {
567        let mut output = LoadVersionsOutput::default();
568        let mut latest = Version::new(0, 0, 0);
569        let mut calver = false;
570
571        for version in versions {
572            if let Some(inner) = version.as_version() {
573                if inner.pre.is_empty() && inner.build.is_empty() && inner > &latest {
574                    inner.clone_into(&mut latest);
575                    calver = matches!(version, VersionSpec::Calendar(_));
576                }
577            }
578
579            output.versions.push(version);
580        }
581
582        output.latest = Some(if calver {
583            UnresolvedVersionSpec::Calendar(CalVer(latest))
584        } else {
585            UnresolvedVersionSpec::Semantic(SemVer(latest))
586        });
587
588        output
589            .aliases
590            .insert("latest".into(), output.latest.clone().unwrap());
591
592        output
593    }
594}
595
596api_struct!(
597    /// Input passed to the `resolve_version` function.
598    pub struct ResolveVersionInput {
599        /// Current tool context.
600        pub context: ToolUnresolvedContext,
601
602        /// The alias or version currently being resolved.
603        pub initial: UnresolvedVersionSpec,
604    }
605);
606
607api_struct!(
608    /// Output returned by the `resolve_version` function.
609    #[serde(default)]
610    pub struct ResolveVersionOutput {
611        /// New alias or version candidate to resolve.
612        #[serde(skip_serializing_if = "Option::is_none")]
613        pub candidate: Option<UnresolvedVersionSpec>,
614
615        /// An explicitly resolved version to be used as-is.
616        /// Note: Only use this field if you know what you're doing!
617        #[serde(skip_serializing_if = "Option::is_none")]
618        pub version: Option<VersionSpec>,
619    }
620);
621
622// MISCELLANEOUS
623
624api_struct!(
625    /// Input passed to the `sync_manifest` function.
626    pub struct SyncManifestInput {
627        /// Current tool context.
628        pub context: ToolContext,
629    }
630);
631
632api_struct!(
633    /// Output returned by the `sync_manifest` function.
634    #[serde(default)]
635    pub struct SyncManifestOutput {
636        /// List of versions that are currently installed. Will replace
637        /// what is currently in the manifest.
638        #[serde(skip_serializing_if = "Option::is_none")]
639        pub versions: Option<Vec<VersionSpec>>,
640
641        /// Whether to skip the syncing process or not.
642        pub skip_sync: bool,
643    }
644);
645
646api_struct!(
647    /// Input passed to the `sync_shell_profile` function.
648    pub struct SyncShellProfileInput {
649        /// Current tool context.
650        pub context: ToolContext,
651
652        /// Arguments passed after `--` that was directly passed to the tool's binary.
653        pub passthrough_args: Vec<String>,
654    }
655);
656
657api_struct!(
658    /// Output returned by the `sync_shell_profile` function.
659    pub struct SyncShellProfileOutput {
660        /// An environment variable to check for in the shell profile.
661        /// If the variable exists, injecting path and exports will be avoided.
662        pub check_var: String,
663
664        /// A mapping of environment variables that will be injected as exports.
665        #[serde(default, skip_serializing_if = "Option::is_none")]
666        pub export_vars: Option<FxHashMap<String, String>>,
667
668        /// A list of paths to prepend to the `PATH` environment variable.
669        #[serde(default, skip_serializing_if = "Option::is_none")]
670        pub extend_path: Option<Vec<String>>,
671
672        /// Whether to skip the syncing process or not.
673        #[serde(default)]
674        pub skip_sync: bool,
675    }
676);