proto_pdk_api/api/
mod.rs

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