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 #[serde(default)]
55 pub build_steps: Option<Vec<Vec<String>>>,
56}
57
58impl ProviderDef {
59 pub fn render_artifact(
62 &self,
63 version: &str,
64 platform: &Platform,
65 checksum: Checksum,
66 size: Option<u64>,
67 ) -> Artifact {
68 let os = self.map_os(platform);
69 let arch = self.map_arch(platform);
70 let archive = self.archive_for(platform);
71 let minor = {
75 let mut it = version.split('.');
76 match (it.next(), it.next()) {
77 (Some(a), Some(b)) => format!("{a}.{b}"),
78 _ => version.to_string(),
79 }
80 };
81 let url = self
82 .url_template
83 .replace("{version}", version)
84 .replace("{minor}", &minor)
85 .replace("{os}", &os)
86 .replace("{arch}", &arch)
87 .replace("{ext}", ext_for(&archive));
88 Artifact {
89 url,
90 mirrors: Vec::new(),
91 archive,
92 size,
93 checksum,
94 signature: None,
95 signature_key: None,
96 bin: self.bin.clone(),
97 strip: self.strip,
98 build: self
99 .build_steps
100 .clone()
101 .map(|steps| vanta_core::BuildRecipe { steps }),
102 store_key: None,
103 }
104 }
105
106 pub fn archive_for(&self, platform: &Platform) -> String {
109 self.archive_map
110 .get(platform.os.as_str())
111 .cloned()
112 .unwrap_or_else(|| self.archive.clone())
113 }
114
115 fn map_os(&self, platform: &Platform) -> String {
116 let key = platform.os.as_str();
117 self.os_map
118 .get(key)
119 .cloned()
120 .unwrap_or_else(|| key.to_string())
121 }
122
123 fn map_arch(&self, platform: &Platform) -> String {
124 let key = platform.arch.as_str();
125 self.arch_map
126 .get(key)
127 .cloned()
128 .unwrap_or_else(|| key.to_string())
129 }
130}
131
132pub fn ext_for(archive: &str) -> &'static str {
134 match archive {
135 "tar.gz" | "tgz" => "tar.gz",
136 "tar.xz" => "tar.xz",
137 "zip" => "zip",
138 _ => "",
139 }
140}
141
142#[cfg(test)]
143mod tests {
144 use super::*;
145 use vanta_core::{Arch, Libc, Os};
146
147 fn node_provider() -> ProviderDef {
148 let mut os_map = BTreeMap::new();
149 os_map.insert("macos".into(), "darwin".into());
150 let mut arch_map = BTreeMap::new();
151 arch_map.insert("aarch64".into(), "arm64".into());
152 ProviderDef {
153 id: "official/node".into(),
154 tool: "node".into(),
155 url_template: "https://nodejs.org/dist/v{version}/node-v{version}-{os}-{arch}.{ext}"
156 .into(),
157 archive: "tar.gz".into(),
158 archive_map: BTreeMap::new(),
159 strip: 1,
160 bin: vec!["bin/node".into()],
161 os_map,
162 arch_map,
163 build_steps: None,
164 }
165 }
166
167 #[test]
168 fn archive_map_overrides_kind_and_ext_per_os() {
169 let mut p = node_provider();
170 p.archive_map.insert("macos".into(), "zip".into());
171 let mac = Platform {
172 os: Os::Macos,
173 arch: Arch::Aarch64,
174 libc: Libc::None,
175 };
176 let linux = Platform {
177 os: Os::Linux,
178 arch: Arch::X86_64,
179 libc: Libc::Gnu,
180 };
181 fn cs() -> Checksum {
182 Checksum {
183 algo: "sha256".into(),
184 value: "00".into(),
185 }
186 }
187 let mac_art = p.render_artifact("1.0.0", &mac, cs(), None);
188 let linux_art = p.render_artifact("1.0.0", &linux, cs(), None);
189 assert_eq!(mac_art.archive, "zip");
190 assert!(mac_art.url.ends_with(".zip"));
191 assert_eq!(linux_art.archive, "tar.gz");
192 assert!(linux_art.url.ends_with(".tar.gz"));
193 }
194
195 #[test]
196 fn renders_url_with_token_maps() {
197 let p = node_provider();
198 let plat = Platform {
199 os: Os::Macos,
200 arch: Arch::Aarch64,
201 libc: Libc::None,
202 };
203 let art = p.render_artifact(
204 "24.6.0",
205 &plat,
206 Checksum {
207 algo: "sha256".into(),
208 value: "abc".into(),
209 },
210 Some(100),
211 );
212 assert_eq!(
213 art.url,
214 "https://nodejs.org/dist/v24.6.0/node-v24.6.0-darwin-arm64.tar.gz"
215 );
216 assert_eq!(art.archive, "tar.gz");
217 assert_eq!(art.bin, vec!["bin/node".to_string()]);
218 assert_eq!(art.checksum.value, "abc");
219 }
220}