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 pub struct ToolContext {
23 #[serde(default, skip_serializing_if = "Option::is_none")]
25 pub proto_version: Option<Version>,
26
27 pub temp_dir: VirtualPath,
29
30 pub tool_dir: VirtualPath,
32
33 pub version: VersionSpec,
35 }
36);
37
38api_struct!(
39 pub struct ToolUnresolvedContext {
41 #[serde(default, skip_serializing_if = "Option::is_none")]
43 pub proto_version: Option<Version>,
44
45 pub temp_dir: VirtualPath,
47
48 #[serde(default, skip_serializing_if = "Option::is_none")]
50 pub version: Option<VersionSpec>,
51
52 #[doc(hidden)]
54 pub tool_dir: VirtualPath,
55 }
56);
57
58api_unit_enum!(
59 pub enum PluginType {
61 #[serde(alias = "CLI", alias = "CommandLine")] CommandLine,
63 #[default]
64 #[serde(alias = "Language")]
65 Language,
66 #[serde(alias = "PM", alias = "DependencyManager")] DependencyManager,
68 #[serde(alias = "VM", alias = "VersionManager")] VersionManager,
70 }
71);
72
73api_struct!(
74 pub struct RegisterToolInput {
76 pub id: String,
78 }
79);
80
81#[deprecated(note = "Use `RegisterToolInput` instead.")]
82pub type ToolMetadataInput = RegisterToolInput;
83
84api_struct!(
85 #[serde(default)]
87 pub struct ToolInventoryMetadata {
88 #[serde(skip_serializing_if = "Option::is_none")]
91 pub override_dir: Option<VirtualPath>,
92
93 #[serde(skip_serializing_if = "Option::is_none")]
95 pub version_suffix: Option<String>,
96 }
97);
98
99api_unit_enum!(
100 pub enum InstallStrategy {
102 #[serde(alias = "BuildFromSource")] BuildFromSource,
104 #[default]
105 #[serde(alias = "DownloadPrebuilt")] DownloadPrebuilt,
107 }
108);
109
110api_struct!(
111 pub struct RegisterToolOutput {
113 #[serde(default, skip_serializing_if = "Option::is_none")]
115 pub config_schema: Option<schematic::Schema>,
116
117 #[serde(default)]
119 pub default_install_strategy: InstallStrategy,
120
121 #[serde(default, skip_serializing_if = "Option::is_none")]
123 pub default_version: Option<UnresolvedVersionSpec>,
124
125 #[serde(default, skip_serializing_if = "Vec::is_empty")]
128 pub deprecations: Vec<String>,
129
130 #[serde(default)]
132 pub inventory: ToolInventoryMetadata,
133
134 #[serde(default, skip_serializing_if = "Option::is_none")]
136 pub minimum_proto_version: Option<Version>,
137
138 pub name: String,
140
141 #[serde(default, skip_serializing_if = "Option::is_none")]
143 pub plugin_version: Option<Version>,
144
145 #[serde(default, skip_serializing_if = "Vec::is_empty")]
147 pub requires: Vec<String>,
148
149 #[serde(default, skip_serializing_if = "Vec::is_empty")]
152 pub self_upgrade_commands: Vec<String>,
153
154 #[serde(rename = "type")]
156 pub type_of: PluginType,
157
158 #[serde(default)]
160 pub unstable: Switch,
161 }
162);
163
164#[deprecated(note = "Use `RegisterToolOutput` instead.")]
165pub type ToolMetadataOutput = RegisterToolOutput;
166
167api_struct!(
170 pub struct RegisterBackendInput {
172 pub context: ToolUnresolvedContext,
174
175 pub id: String,
177 }
178);
179
180api_struct!(
181 pub struct RegisterBackendOutput {
183 pub backend_id: String,
185
186 #[serde(default, skip_serializing_if = "Vec::is_empty")]
189 pub exes: Vec<PathBuf>,
190
191 #[serde(default, skip_serializing_if = "Option::is_none")]
193 pub source: Option<SourceLocation>,
194 }
195);
196
197api_struct!(
200 pub struct DetectVersionInput {
202 pub context: ToolUnresolvedContext,
204 }
205);
206
207api_struct!(
208 #[serde(default)]
210 pub struct DetectVersionOutput {
211 #[serde(skip_serializing_if = "Vec::is_empty")]
213 pub files: Vec<String>,
214
215 #[serde(skip_serializing_if = "Vec::is_empty")]
217 pub ignore: Vec<String>,
218 }
219);
220
221api_struct!(
222 pub struct ParseVersionFileInput {
224 pub content: String,
226
227 pub context: ToolUnresolvedContext,
229
230 pub file: String,
232
233 pub path: VirtualPath,
235 }
236);
237
238api_struct!(
239 #[serde(default)]
241 pub struct ParseVersionFileOutput {
242 #[serde(skip_serializing_if = "Option::is_none")]
245 pub version: Option<UnresolvedVersionSpec>,
246 }
247);
248
249api_struct!(
252 pub struct NativeInstallInput {
254 pub context: ToolContext,
256
257 pub install_dir: VirtualPath,
259 }
260);
261
262api_struct!(
263 pub struct NativeInstallOutput {
265 #[serde(default, skip_serializing_if = "Option::is_none")]
267 pub checksum: Option<Checksum>,
268
269 #[serde(default, skip_serializing_if = "Option::is_none")]
271 pub error: Option<String>,
272
273 pub installed: bool,
275
276 #[serde(default)]
278 pub skip_install: bool,
279 }
280);
281
282api_struct!(
283 pub struct NativeUninstallInput {
285 pub context: ToolContext,
287
288 pub uninstall_dir: VirtualPath,
290 }
291);
292
293api_struct!(
294 pub struct NativeUninstallOutput {
296 #[serde(default, skip_serializing_if = "Option::is_none")]
298 pub error: Option<String>,
299
300 pub uninstalled: bool,
302
303 #[serde(default)]
305 pub skip_uninstall: bool,
306 }
307);
308
309api_struct!(
310 pub struct DownloadPrebuiltInput {
312 pub context: ToolContext,
314
315 pub install_dir: VirtualPath,
317 }
318);
319
320api_struct!(
321 pub struct DownloadPrebuiltOutput {
323 #[serde(default, skip_serializing_if = "Option::is_none")]
326 pub archive_prefix: Option<String>,
327
328 #[serde(default, skip_serializing_if = "Option::is_none")]
330 pub checksum: Option<Checksum>,
331
332 #[serde(default, skip_serializing_if = "Option::is_none")]
335 pub checksum_name: Option<String>,
336
337 #[serde(default, skip_serializing_if = "Option::is_none")]
339 pub checksum_public_key: Option<String>,
340
341 #[serde(default, skip_serializing_if = "Option::is_none")]
344 pub checksum_url: Option<String>,
345
346 #[serde(default, skip_serializing_if = "Option::is_none")]
349 pub download_name: Option<String>,
350
351 pub download_url: String,
353 }
354);
355
356api_struct!(
357 pub struct UnpackArchiveInput {
359 pub context: ToolContext,
361
362 pub input_file: VirtualPath,
364
365 pub output_dir: VirtualPath,
367 }
368);
369
370api_struct!(
371 pub struct VerifyChecksumInput {
373 pub context: ToolContext,
375
376 pub checksum_file: VirtualPath,
378
379 #[serde(default, skip_serializing_if = "Option::is_none")]
383 pub download_checksum: Option<Checksum>,
384
385 pub download_file: VirtualPath,
387 }
388);
389
390api_struct!(
391 pub struct VerifyChecksumOutput {
393 pub verified: bool,
395 }
396);
397
398api_struct!(
401 pub struct LocateExecutablesInput {
403 pub context: ToolContext,
405
406 pub install_dir: VirtualPath,
408 }
409);
410
411api_struct!(
412 #[serde(default)]
414 pub struct ExecutableConfig {
415 #[serde(skip_serializing_if = "Option::is_none")]
418 pub exe_path: Option<PathBuf>,
419
420 #[serde(skip_serializing_if = "Option::is_none")]
423 pub exe_link_path: Option<PathBuf>,
424
425 #[serde(skip_serializing_if = "is_false")]
427 pub no_bin: bool,
428
429 #[serde(skip_serializing_if = "is_false")]
431 pub no_shim: bool,
432
433 #[serde(skip_serializing_if = "Vec::is_empty")]
436 pub parent_exe_args: Vec<String>,
437
438 #[serde(skip_serializing_if = "Option::is_none")]
440 pub parent_exe_name: Option<String>,
441
442 #[serde(skip_serializing_if = "is_false")]
444 pub primary: bool,
445
446 #[serde(skip_serializing_if = "Option::is_none")]
448 pub shim_before_args: Option<StringOrVec>,
449
450 #[serde(skip_serializing_if = "Option::is_none")]
452 pub shim_after_args: Option<StringOrVec>,
453
454 #[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 #[serde(default)]
488 pub struct LocateExecutablesOutput {
489 #[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 #[serde(skip_serializing_if = "Vec::is_empty")]
502 pub exes_dirs: Vec<PathBuf>,
503
504 #[serde(skip_serializing_if = "Vec::is_empty")]
507 pub globals_lookup_dirs: Vec<String>,
508
509 #[serde(skip_serializing_if = "Option::is_none")]
512 pub globals_prefix: Option<String>,
513 }
514);
515
516api_struct!(
519 pub struct LoadVersionsInput {
521 pub context: ToolUnresolvedContext,
523
524 pub initial: UnresolvedVersionSpec,
526 }
527);
528
529api_struct!(
530 #[serde(default)]
532 pub struct LoadVersionsOutput {
533 #[serde(skip_serializing_if = "Option::is_none")]
535 pub canary: Option<UnresolvedVersionSpec>,
536
537 #[serde(skip_serializing_if = "Option::is_none")]
539 pub latest: Option<UnresolvedVersionSpec>,
540
541 #[serde(skip_serializing_if = "FxHashMap::is_empty")]
543 pub aliases: FxHashMap<String, UnresolvedVersionSpec>,
544
545 #[serde(skip_serializing_if = "Vec::is_empty")]
547 pub versions: Vec<VersionSpec>,
548 }
549);
550
551impl LoadVersionsOutput {
552 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 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 pub struct ResolveVersionInput {
599 pub context: ToolUnresolvedContext,
601
602 pub initial: UnresolvedVersionSpec,
604 }
605);
606
607api_struct!(
608 #[serde(default)]
610 pub struct ResolveVersionOutput {
611 #[serde(skip_serializing_if = "Option::is_none")]
613 pub candidate: Option<UnresolvedVersionSpec>,
614
615 #[serde(skip_serializing_if = "Option::is_none")]
618 pub version: Option<VersionSpec>,
619 }
620);
621
622api_struct!(
625 pub struct SyncManifestInput {
627 pub context: ToolContext,
629 }
630);
631
632api_struct!(
633 #[serde(default)]
635 pub struct SyncManifestOutput {
636 #[serde(skip_serializing_if = "Option::is_none")]
639 pub versions: Option<Vec<VersionSpec>>,
640
641 pub skip_sync: bool,
643 }
644);
645
646api_struct!(
647 pub struct SyncShellProfileInput {
649 pub context: ToolContext,
651
652 pub passthrough_args: Vec<String>,
654 }
655);
656
657api_struct!(
658 pub struct SyncShellProfileOutput {
660 pub check_var: String,
663
664 #[serde(default, skip_serializing_if = "Option::is_none")]
666 pub export_vars: Option<FxHashMap<String, String>>,
667
668 #[serde(default, skip_serializing_if = "Option::is_none")]
670 pub extend_path: Option<Vec<String>>,
671
672 #[serde(default)]
674 pub skip_sync: bool,
675 }
676);