1#![forbid(unsafe_code)]
13
14pub mod wasm;
15pub use wasm::Sandbox;
16
17use serde::{Deserialize, Serialize};
18use std::collections::BTreeMap;
19use vanta_core::{Artifact, Checksum, Platform};
20
21#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
23pub struct ProviderDef {
24 pub id: String,
26 pub tool: String,
28 pub url_template: String,
30 pub archive: String,
32 #[serde(default)]
37 pub archive_map: BTreeMap<String, String>,
38 #[serde(default)]
40 pub strip: u32,
41 #[serde(default)]
43 pub bin: Vec<String>,
44 #[serde(default)]
46 pub os_map: BTreeMap<String, String>,
47 #[serde(default)]
49 pub arch_map: BTreeMap<String, String>,
50}
51
52impl ProviderDef {
53 pub fn render_artifact(
56 &self,
57 version: &str,
58 platform: &Platform,
59 checksum: Checksum,
60 size: Option<u64>,
61 ) -> Artifact {
62 let os = self.map_os(platform);
63 let arch = self.map_arch(platform);
64 let archive = self.archive_for(platform);
65 let url = self
66 .url_template
67 .replace("{version}", version)
68 .replace("{os}", &os)
69 .replace("{arch}", &arch)
70 .replace("{ext}", ext_for(&archive));
71 Artifact {
72 url,
73 mirrors: Vec::new(),
74 archive,
75 size,
76 checksum,
77 signature: None,
78 signature_key: None,
79 bin: self.bin.clone(),
80 strip: self.strip,
81 store_key: None,
82 }
83 }
84
85 pub fn archive_for(&self, platform: &Platform) -> String {
88 self.archive_map
89 .get(platform.os.as_str())
90 .cloned()
91 .unwrap_or_else(|| self.archive.clone())
92 }
93
94 fn map_os(&self, platform: &Platform) -> String {
95 let key = platform.os.as_str();
96 self.os_map
97 .get(key)
98 .cloned()
99 .unwrap_or_else(|| key.to_string())
100 }
101
102 fn map_arch(&self, platform: &Platform) -> String {
103 let key = platform.arch.as_str();
104 self.arch_map
105 .get(key)
106 .cloned()
107 .unwrap_or_else(|| key.to_string())
108 }
109}
110
111pub fn ext_for(archive: &str) -> &'static str {
113 match archive {
114 "tar.gz" | "tgz" => "tar.gz",
115 "tar.xz" => "tar.xz",
116 "zip" => "zip",
117 _ => "",
118 }
119}
120
121#[cfg(test)]
122mod tests {
123 use super::*;
124 use vanta_core::{Arch, Libc, Os};
125
126 fn node_provider() -> ProviderDef {
127 let mut os_map = BTreeMap::new();
128 os_map.insert("macos".into(), "darwin".into());
129 let mut arch_map = BTreeMap::new();
130 arch_map.insert("aarch64".into(), "arm64".into());
131 ProviderDef {
132 id: "official/node".into(),
133 tool: "node".into(),
134 url_template: "https://nodejs.org/dist/v{version}/node-v{version}-{os}-{arch}.{ext}"
135 .into(),
136 archive: "tar.gz".into(),
137 archive_map: BTreeMap::new(),
138 strip: 1,
139 bin: vec!["bin/node".into()],
140 os_map,
141 arch_map,
142 }
143 }
144
145 #[test]
146 fn archive_map_overrides_kind_and_ext_per_os() {
147 let mut p = node_provider();
148 p.archive_map.insert("macos".into(), "zip".into());
149 let mac = Platform {
150 os: Os::Macos,
151 arch: Arch::Aarch64,
152 libc: Libc::None,
153 };
154 let linux = Platform {
155 os: Os::Linux,
156 arch: Arch::X86_64,
157 libc: Libc::Gnu,
158 };
159 fn cs() -> Checksum {
160 Checksum {
161 algo: "sha256".into(),
162 value: "00".into(),
163 }
164 }
165 let mac_art = p.render_artifact("1.0.0", &mac, cs(), None);
166 let linux_art = p.render_artifact("1.0.0", &linux, cs(), None);
167 assert_eq!(mac_art.archive, "zip");
168 assert!(mac_art.url.ends_with(".zip"));
169 assert_eq!(linux_art.archive, "tar.gz");
170 assert!(linux_art.url.ends_with(".tar.gz"));
171 }
172
173 #[test]
174 fn renders_url_with_token_maps() {
175 let p = node_provider();
176 let plat = Platform {
177 os: Os::Macos,
178 arch: Arch::Aarch64,
179 libc: Libc::None,
180 };
181 let art = p.render_artifact(
182 "24.6.0",
183 &plat,
184 Checksum {
185 algo: "sha256".into(),
186 value: "abc".into(),
187 },
188 Some(100),
189 );
190 assert_eq!(
191 art.url,
192 "https://nodejs.org/dist/v24.6.0/node-v24.6.0-darwin-arm64.tar.gz"
193 );
194 assert_eq!(art.archive, "tar.gz");
195 assert_eq!(art.bin, vec!["bin/node".to_string()]);
196 assert_eq!(art.checksum.value, "abc");
197 }
198}