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 pub struct ToolContext {
21 #[serde(default)]
23 pub proto_version: Option<Version>,
24
25 pub temp_dir: VirtualPath,
27
28 pub tool_dir: VirtualPath,
30
31 pub version: VersionSpec,
33 }
34);
35
36api_unit_enum!(
37 pub enum PluginType {
39 #[serde(alias = "CLI", alias = "CommandLine")] CommandLine,
41 #[default]
42 #[serde(alias = "Language")]
43 Language,
44 #[serde(alias = "PM", alias = "DependencyManager")] DependencyManager,
46 #[serde(alias = "VM", alias = "VersionManager")] VersionManager,
48 }
49);
50
51api_struct!(
52 pub struct RegisterToolInput {
54 pub id: String,
56 }
57);
58
59#[deprecated(note = "Use `RegisterToolInput` instead.")]
60pub type ToolMetadataInput = RegisterToolInput;
61
62api_struct!(
63 #[serde(default)]
65 pub struct ToolInventoryMetadata {
66 #[serde(skip_serializing_if = "Option::is_none")]
69 pub override_dir: Option<VirtualPath>,
70
71 #[serde(skip_serializing_if = "Option::is_none")]
73 pub version_suffix: Option<String>,
74 }
75);
76
77api_unit_enum!(
78 pub enum InstallStrategy {
80 #[serde(alias = "BuildFromSource")] BuildFromSource,
82 #[default]
83 #[serde(alias = "DownloadPrebuilt")] DownloadPrebuilt,
85 }
86);
87
88api_struct!(
89 pub struct RegisterToolOutput {
91 #[serde(default, skip_serializing_if = "Option::is_none")]
93 pub config_schema: Option<schematic::Schema>,
94
95 #[serde(default)]
97 pub default_install_strategy: InstallStrategy,
98
99 #[serde(default, skip_serializing_if = "Option::is_none")]
101 pub default_version: Option<UnresolvedVersionSpec>,
102
103 #[serde(default, skip_serializing_if = "Vec::is_empty")]
106 pub deprecations: Vec<String>,
107
108 #[serde(default)]
110 pub inventory: ToolInventoryMetadata,
111
112 #[serde(default, skip_serializing_if = "Option::is_none")]
114 pub minimum_proto_version: Option<Version>,
115
116 pub name: String,
118
119 #[serde(default, skip_serializing_if = "Option::is_none")]
121 pub plugin_version: Option<Version>,
122
123 #[serde(default, skip_serializing_if = "Vec::is_empty")]
125 pub requires: Vec<String>,
126
127 #[serde(default, skip_serializing_if = "Vec::is_empty")]
130 pub self_upgrade_commands: Vec<String>,
131
132 #[serde(rename = "type")]
134 pub type_of: PluginType,
135
136 #[serde(default)]
138 pub unstable: Switch,
139 }
140);
141
142#[deprecated(note = "Use `RegisterToolOutput` instead.")]
143pub type ToolMetadataOutput = RegisterToolOutput;
144
145api_struct!(
148 pub struct RegisterBackendInput {
150 pub context: ToolContext,
152
153 pub id: String,
155 }
156);
157
158api_struct!(
159 pub struct RegisterBackendOutput {
161 pub backend_id: String,
163
164 #[serde(default, skip_serializing_if = "Vec::is_empty")]
167 pub exes: Vec<PathBuf>,
168
169 #[serde(default, skip_serializing_if = "Option::is_none")]
171 pub source: Option<SourceLocation>,
172 }
173);
174
175api_struct!(
178 #[serde(default)]
180 pub struct DetectVersionOutput {
181 #[serde(skip_serializing_if = "Vec::is_empty")]
183 pub files: Vec<String>,
184
185 #[serde(skip_serializing_if = "Vec::is_empty")]
187 pub ignore: Vec<String>,
188 }
189);
190
191api_struct!(
192 pub struct ParseVersionFileInput {
194 pub content: String,
196
197 pub file: String,
199
200 pub path: VirtualPath,
202 }
203);
204
205api_struct!(
206 #[serde(default)]
208 pub struct ParseVersionFileOutput {
209 #[serde(skip_serializing_if = "Option::is_none")]
212 pub version: Option<UnresolvedVersionSpec>,
213 }
214);
215
216api_struct!(
219 pub struct NativeInstallInput {
221 pub context: ToolContext,
223
224 pub install_dir: VirtualPath,
226 }
227);
228
229api_struct!(
230 pub struct NativeInstallOutput {
232 #[serde(default, skip_serializing_if = "Option::is_none")]
234 pub error: Option<String>,
235
236 pub installed: bool,
238
239 #[serde(default)]
241 pub skip_install: bool,
242 }
243);
244
245api_struct!(
246 pub struct NativeUninstallInput {
248 pub context: ToolContext,
250 }
251);
252
253api_struct!(
254 pub struct NativeUninstallOutput {
256 #[serde(default, skip_serializing_if = "Option::is_none")]
258 pub error: Option<String>,
259
260 pub uninstalled: bool,
262
263 #[serde(default)]
265 pub skip_uninstall: bool,
266 }
267);
268
269api_struct!(
270 pub struct DownloadPrebuiltInput {
272 pub context: ToolContext,
274
275 pub install_dir: VirtualPath,
277 }
278);
279
280api_struct!(
281 pub struct DownloadPrebuiltOutput {
283 #[serde(default, skip_serializing_if = "Option::is_none")]
286 pub archive_prefix: Option<String>,
287
288 #[serde(default, skip_serializing_if = "Option::is_none")]
290 pub checksum: Option<String>,
291
292 #[serde(default, skip_serializing_if = "Option::is_none")]
295 pub checksum_name: Option<String>,
296
297 #[serde(default, skip_serializing_if = "Option::is_none")]
299 pub checksum_public_key: Option<String>,
300
301 #[serde(default, skip_serializing_if = "Option::is_none")]
304 pub checksum_url: Option<String>,
305
306 #[serde(default, skip_serializing_if = "Option::is_none")]
309 pub download_name: Option<String>,
310
311 pub download_url: String,
313 }
314);
315
316api_struct!(
317 pub struct UnpackArchiveInput {
319 pub context: ToolContext,
321
322 pub input_file: VirtualPath,
324
325 pub output_dir: VirtualPath,
327 }
328);
329
330api_struct!(
331 pub struct VerifyChecksumInput {
333 pub context: ToolContext,
335
336 pub checksum_file: VirtualPath,
338
339 pub download_file: VirtualPath,
341 }
342);
343
344api_struct!(
345 pub struct VerifyChecksumOutput {
347 pub verified: bool,
348 }
349);
350
351api_struct!(
354 pub struct LocateExecutablesInput {
356 pub context: ToolContext,
358 }
359);
360
361api_struct!(
362 #[serde(default)]
364 pub struct ExecutableConfig {
365 #[serde(skip_serializing_if = "Option::is_none")]
368 pub exe_path: Option<PathBuf>,
369
370 #[serde(skip_serializing_if = "Option::is_none")]
373 pub exe_link_path: Option<PathBuf>,
374
375 #[serde(skip_serializing_if = "is_false")]
377 pub no_bin: bool,
378
379 #[serde(skip_serializing_if = "is_false")]
381 pub no_shim: bool,
382
383 #[serde(skip_serializing_if = "Option::is_none")]
385 pub parent_exe_name: Option<String>,
386
387 #[serde(skip_serializing_if = "is_false")]
389 pub primary: bool,
390
391 #[serde(skip_serializing_if = "Option::is_none")]
393 pub shim_before_args: Option<StringOrVec>,
394
395 #[serde(skip_serializing_if = "Option::is_none")]
397 pub shim_after_args: Option<StringOrVec>,
398
399 #[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 #[serde(default)]
433 pub struct LocateExecutablesOutput {
434 #[serde(skip_serializing_if = "FxHashMap::is_empty")]
437 pub exes: FxHashMap<String, ExecutableConfig>,
438
439 #[serde(skip_serializing_if = "Option::is_none")]
443 pub exes_dir: Option<PathBuf>,
444
445 #[serde(skip_serializing_if = "Vec::is_empty")]
448 pub globals_lookup_dirs: Vec<String>,
449
450 #[serde(skip_serializing_if = "Option::is_none")]
453 pub globals_prefix: Option<String>,
454
455 #[deprecated(note = "Use `exes` instead.")]
458 #[serde(skip_serializing_if = "Option::is_none")]
459 pub primary: Option<ExecutableConfig>,
460
461 #[deprecated(note = "Use `exes` instead.")]
464 #[serde(skip_serializing_if = "FxHashMap::is_empty")]
465 pub secondary: FxHashMap<String, ExecutableConfig>,
466 }
467);
468
469api_struct!(
472 pub struct LoadVersionsInput {
474 pub context: ToolContext,
476
477 pub initial: UnresolvedVersionSpec,
479 }
480);
481
482api_struct!(
483 #[serde(default)]
485 pub struct LoadVersionsOutput {
486 #[serde(skip_serializing_if = "Option::is_none")]
488 pub canary: Option<UnresolvedVersionSpec>,
489
490 #[serde(skip_serializing_if = "Option::is_none")]
492 pub latest: Option<UnresolvedVersionSpec>,
493
494 #[serde(skip_serializing_if = "FxHashMap::is_empty")]
496 pub aliases: FxHashMap<String, UnresolvedVersionSpec>,
497
498 #[serde(skip_serializing_if = "Vec::is_empty")]
500 pub versions: Vec<VersionSpec>,
501 }
502);
503
504impl LoadVersionsOutput {
505 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 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 pub struct ResolveVersionInput {
552 pub context: ToolContext,
554
555 pub initial: UnresolvedVersionSpec,
557 }
558);
559
560api_struct!(
561 #[serde(default)]
563 pub struct ResolveVersionOutput {
564 #[serde(skip_serializing_if = "Option::is_none")]
566 pub candidate: Option<UnresolvedVersionSpec>,
567
568 #[serde(skip_serializing_if = "Option::is_none")]
571 pub version: Option<VersionSpec>,
572 }
573);
574
575api_struct!(
578 pub struct SyncManifestInput {
580 pub context: ToolContext,
582 }
583);
584
585api_struct!(
586 #[serde(default)]
588 pub struct SyncManifestOutput {
589 #[serde(skip_serializing_if = "Option::is_none")]
592 pub versions: Option<Vec<VersionSpec>>,
593
594 pub skip_sync: bool,
596 }
597);
598
599api_struct!(
600 pub struct SyncShellProfileInput {
602 pub context: ToolContext,
604
605 pub passthrough_args: Vec<String>,
607 }
608);
609
610api_struct!(
611 pub struct SyncShellProfileOutput {
613 pub check_var: String,
616
617 #[serde(default, skip_serializing_if = "Option::is_none")]
619 pub export_vars: Option<FxHashMap<String, String>>,
620
621 #[serde(default, skip_serializing_if = "Option::is_none")]
623 pub extend_path: Option<Vec<String>>,
624
625 #[serde(default)]
627 pub skip_sync: bool,
628 }
629);