1use serde::{Deserialize, Serialize};
7#[cfg(feature = "openapi")]
8use utoipa::ToSchema;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum CargoBuildStrategy {
13 Native,
15 Zigbuild,
17 Xwin,
19}
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23pub enum BuildHost {
24 LinuxX64,
26 LinuxArm64,
28 WindowsX64,
30 DarwinArm64,
32 Unsupported,
35}
36
37impl BuildHost {
38 pub fn current() -> Self {
40 match (std::env::consts::OS, std::env::consts::ARCH) {
41 ("linux", "x86_64") => Self::LinuxX64,
42 ("linux", "aarch64") => Self::LinuxArm64,
43 ("windows", "x86_64") => Self::WindowsX64,
44 ("macos", "aarch64") => Self::DarwinArm64,
45 _ => Self::Unsupported,
46 }
47 }
48
49 pub fn id(self) -> &'static str {
51 match self {
52 Self::LinuxX64 => "linux-x64",
53 Self::LinuxArm64 => "linux-arm64",
54 Self::WindowsX64 => "windows-x64",
55 Self::DarwinArm64 => "darwin-arm64",
56 Self::Unsupported => "unsupported",
57 }
58 }
59}
60
61impl CargoBuildStrategy {
62 pub fn cargo_subcommand(&self) -> &'static str {
64 match self {
65 Self::Native => "build",
66 Self::Zigbuild => "zigbuild",
67 Self::Xwin => "xwin",
68 }
69 }
70
71 pub fn cargo_args(&self) -> Vec<&'static str> {
73 match self {
74 Self::Native => vec!["build"],
75 Self::Zigbuild => vec!["zigbuild"],
76 Self::Xwin => vec!["xwin", "build"],
77 }
78 }
79
80 pub fn display_name(&self) -> &'static str {
82 match self {
83 Self::Native => "cargo build",
84 Self::Zigbuild => "cargo zigbuild",
85 Self::Xwin => "cargo xwin build",
86 }
87 }
88
89 pub fn install_package(&self) -> Option<&'static str> {
91 match self {
92 Self::Native => None,
93 Self::Zigbuild => Some("cargo-zigbuild"),
94 Self::Xwin => Some("cargo-xwin"),
95 }
96 }
97}
98
99#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
101#[cfg_attr(feature = "openapi", derive(ToSchema))]
102#[serde(rename_all = "kebab-case")]
103pub enum SourceBinaryType {
104 Cli,
106 Terraform,
108 Operator,
110}
111
112impl SourceBinaryType {
113 pub fn binary_name(&self) -> &'static str {
115 match self {
116 SourceBinaryType::Cli => "alien-deploy",
117 SourceBinaryType::Terraform => "alien-terraform",
118 SourceBinaryType::Operator => "alien-operator",
119 }
120 }
121}
122
123impl std::fmt::Display for SourceBinaryType {
124 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
125 match self {
126 SourceBinaryType::Cli => write!(f, "cli"),
127 SourceBinaryType::Terraform => write!(f, "terraform"),
128 SourceBinaryType::Operator => write!(f, "operator"),
129 }
130 }
131}
132
133#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
138#[cfg_attr(feature = "openapi", derive(ToSchema))]
139#[serde(rename_all = "kebab-case")]
140pub enum BinaryTarget {
141 WindowsX64,
143 LinuxX64,
145 LinuxArm64,
147 DarwinArm64,
149}
150
151impl BinaryTarget {
152 pub const ALL: &'static [BinaryTarget] = &[
154 BinaryTarget::WindowsX64,
155 BinaryTarget::LinuxX64,
156 BinaryTarget::LinuxArm64,
157 BinaryTarget::DarwinArm64,
158 ];
159
160 pub const LINUX: &'static [BinaryTarget] = &[BinaryTarget::LinuxX64, BinaryTarget::LinuxArm64];
162
163 pub fn rust_target_triple(&self) -> &'static str {
165 match self {
166 Self::WindowsX64 => "x86_64-pc-windows-msvc",
167 Self::LinuxX64 => "x86_64-unknown-linux-musl",
168 Self::LinuxArm64 => "aarch64-unknown-linux-musl",
169 Self::DarwinArm64 => "aarch64-apple-darwin",
170 }
171 }
172
173 pub fn cargo_build_strategy(&self) -> CargoBuildStrategy {
181 self.cargo_build_strategy_for(BuildHost::current())
182 }
183
184 pub fn cargo_build_strategy_for(&self, host: BuildHost) -> CargoBuildStrategy {
186 match (self, host) {
187 (Self::LinuxX64, BuildHost::LinuxX64)
188 | (Self::LinuxArm64, BuildHost::LinuxArm64)
189 | (Self::WindowsX64, BuildHost::WindowsX64)
190 | (Self::DarwinArm64, BuildHost::DarwinArm64) => CargoBuildStrategy::Native,
191 (Self::WindowsX64, _) => CargoBuildStrategy::Xwin,
192 (Self::LinuxX64 | Self::LinuxArm64, _) => CargoBuildStrategy::Zigbuild,
193 (Self::DarwinArm64, _) => CargoBuildStrategy::Native,
195 }
196 }
197
198 pub fn binary_extension(&self) -> &'static str {
200 match self {
201 Self::WindowsX64 => ".exe",
202 _ => "",
203 }
204 }
205
206 pub fn runtime_platform_id(&self) -> &'static str {
208 match self {
209 Self::WindowsX64 => "windows-x64",
210 Self::LinuxX64 => "linux-x64",
211 Self::LinuxArm64 => "linux-aarch64",
212 Self::DarwinArm64 => "darwin-aarch64",
213 }
214 }
215
216 pub fn from_runtime_platform_id(id: &str) -> Option<Self> {
219 match id {
220 "windows-x64" => Some(Self::WindowsX64),
221 "linux-x64" => Some(Self::LinuxX64),
222 "linux-aarch64" => Some(Self::LinuxArm64),
223 "darwin-aarch64" => Some(Self::DarwinArm64),
224 _ => None,
225 }
226 }
227
228 pub fn oci_os(&self) -> &'static str {
230 match self {
231 Self::WindowsX64 => "windows",
232 Self::LinuxX64 | Self::LinuxArm64 => "linux",
233 Self::DarwinArm64 => "darwin",
234 }
235 }
236
237 pub fn oci_arch(&self) -> &'static str {
239 match self {
240 Self::WindowsX64 | Self::LinuxX64 => "amd64",
241 Self::LinuxArm64 | Self::DarwinArm64 => "arm64",
242 }
243 }
244
245 pub fn bun_target(&self) -> &'static str {
247 match self {
248 Self::WindowsX64 => "bun-windows-x64",
249 Self::LinuxX64 => "bun-linux-x64",
250 Self::LinuxArm64 => "bun-linux-arm64",
251 Self::DarwinArm64 => "bun-darwin-arm64",
252 }
253 }
254
255 pub fn terraform_key(&self) -> &'static str {
257 match self {
258 BinaryTarget::LinuxX64 => "linux_amd64",
259 BinaryTarget::LinuxArm64 => "linux_arm64",
260 BinaryTarget::DarwinArm64 => "darwin_arm64",
261 BinaryTarget::WindowsX64 => "windows_amd64",
262 }
263 }
264
265 pub fn terraform_os(&self) -> &'static str {
267 match self {
268 BinaryTarget::LinuxX64 | BinaryTarget::LinuxArm64 => "linux",
269 BinaryTarget::DarwinArm64 => "darwin",
270 BinaryTarget::WindowsX64 => "windows",
271 }
272 }
273
274 pub fn terraform_arch(&self) -> &'static str {
276 match self {
277 BinaryTarget::LinuxX64 | BinaryTarget::WindowsX64 => "amd64",
278 BinaryTarget::LinuxArm64 | BinaryTarget::DarwinArm64 => "arm64",
279 }
280 }
281
282 pub fn is_darwin(&self) -> bool {
284 matches!(self, Self::DarwinArm64)
285 }
286
287 pub fn is_windows(&self) -> bool {
289 matches!(self, Self::WindowsX64)
290 }
291
292 pub fn linux_container_target() -> Self {
296 match Self::current_os() {
297 Self::DarwinArm64 | Self::LinuxArm64 => Self::LinuxArm64,
298 Self::LinuxX64 | Self::WindowsX64 => Self::LinuxX64,
299 }
300 }
301
302 pub fn all() -> Vec<Self> {
304 Self::ALL.to_vec()
305 }
306
307 pub fn defaults_for_platform(platform: crate::Platform) -> Vec<Self> {
309 match platform {
310 crate::Platform::Aws => vec![Self::LinuxArm64],
311 crate::Platform::Gcp => vec![Self::LinuxX64],
312 crate::Platform::Azure => vec![Self::LinuxX64],
313 crate::Platform::Kubernetes => Self::LINUX.to_vec(),
314 crate::Platform::Machines => Self::LINUX.to_vec(),
315 crate::Platform::Local => vec![Self::current_os()],
316 crate::Platform::Test => vec![Self::LinuxX64],
317 }
318 }
319
320 pub fn current_os() -> Self {
322 #[cfg(all(target_os = "windows", target_arch = "x86_64"))]
323 return Self::WindowsX64;
324
325 #[cfg(all(target_os = "linux", target_arch = "x86_64"))]
326 return Self::LinuxX64;
327
328 #[cfg(all(target_os = "linux", target_arch = "aarch64"))]
329 return Self::LinuxArm64;
330
331 #[cfg(all(target_os = "macos", target_arch = "aarch64"))]
332 return Self::DarwinArm64;
333
334 #[cfg(not(any(
335 all(target_os = "windows", target_arch = "x86_64"),
336 all(target_os = "linux", target_arch = "x86_64"),
337 all(target_os = "linux", target_arch = "aarch64"),
338 all(target_os = "macos", target_arch = "aarch64")
339 )))]
340 {
341 panic!("Alien does not support native binaries on this host")
342 }
343 }
344}
345
346impl std::fmt::Display for BinaryTarget {
347 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
348 match self {
349 BinaryTarget::WindowsX64 => write!(f, "windows-x64"),
350 BinaryTarget::LinuxX64 => write!(f, "linux-x64"),
351 BinaryTarget::LinuxArm64 => write!(f, "linux-arm64"),
352 BinaryTarget::DarwinArm64 => write!(f, "darwin-arm64"),
353 }
354 }
355}
356
357impl std::str::FromStr for BinaryTarget {
358 type Err = String;
359
360 fn from_str(s: &str) -> Result<Self, Self::Err> {
361 match s {
362 "windows-x64" => Ok(BinaryTarget::WindowsX64),
363 "linux-x64" => Ok(BinaryTarget::LinuxX64),
364 "linux-arm64" => Ok(BinaryTarget::LinuxArm64),
365 "darwin-arm64" => Ok(BinaryTarget::DarwinArm64),
366 _ => Err(format!("Unknown binary target: {}", s)),
367 }
368 }
369}
370
371#[cfg(test)]
372mod tests {
373 use super::{BinaryTarget, BuildHost, CargoBuildStrategy};
374 use crate::Platform;
375
376 #[test]
377 fn local_platform_defaults_to_current_host_target() {
378 assert_eq!(
379 BinaryTarget::defaults_for_platform(Platform::Local),
380 vec![BinaryTarget::current_os()]
381 );
382 }
383
384 #[test]
385 fn runtime_platform_id_round_trips() {
386 for target in BinaryTarget::ALL {
387 assert_eq!(
388 BinaryTarget::from_runtime_platform_id(target.runtime_platform_id()),
389 Some(*target),
390 "round-trip failed for {target}"
391 );
392 }
393 assert_eq!(
395 BinaryTarget::from_runtime_platform_id("linux-aarch64"),
396 Some(BinaryTarget::LinuxArm64)
397 );
398 assert_eq!(BinaryTarget::from_runtime_platform_id("linux-arm64"), None);
399 assert_eq!(BinaryTarget::from_runtime_platform_id("nonsense"), None);
400 }
401
402 #[test]
403 fn cloud_platform_defaults_remain_stable() {
404 assert_eq!(
405 BinaryTarget::defaults_for_platform(Platform::Aws),
406 vec![BinaryTarget::LinuxArm64]
407 );
408 assert_eq!(
409 BinaryTarget::defaults_for_platform(Platform::Gcp),
410 vec![BinaryTarget::LinuxX64]
411 );
412 assert_eq!(
413 BinaryTarget::defaults_for_platform(Platform::Azure),
414 vec![BinaryTarget::LinuxX64]
415 );
416 assert_eq!(
417 BinaryTarget::defaults_for_platform(Platform::Kubernetes),
418 vec![BinaryTarget::LinuxX64, BinaryTarget::LinuxArm64]
419 );
420 }
421
422 #[test]
423 fn cargo_build_strategy_uses_native_tools_only_on_compatible_hosts() {
424 assert_eq!(
425 BinaryTarget::LinuxX64.cargo_build_strategy_for(BuildHost::LinuxX64),
426 CargoBuildStrategy::Native
427 );
428 assert_eq!(
429 BinaryTarget::LinuxArm64.cargo_build_strategy_for(BuildHost::LinuxArm64),
430 CargoBuildStrategy::Native
431 );
432 assert_eq!(
433 BinaryTarget::LinuxArm64.cargo_build_strategy_for(BuildHost::LinuxX64),
434 CargoBuildStrategy::Zigbuild
435 );
436 assert_eq!(
437 BinaryTarget::WindowsX64.cargo_build_strategy_for(BuildHost::WindowsX64),
438 CargoBuildStrategy::Native
439 );
440 assert_eq!(
441 BinaryTarget::WindowsX64.cargo_build_strategy_for(BuildHost::DarwinArm64),
442 CargoBuildStrategy::Xwin
443 );
444 assert_eq!(
445 BinaryTarget::DarwinArm64.cargo_build_strategy_for(BuildHost::DarwinArm64),
446 CargoBuildStrategy::Native
447 );
448 }
449}