use anyhow::{bail, Result};
use std::path::{Path, PathBuf};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Arch {
Arm64,
Amd64,
}
#[allow(dead_code)]
impl Arch {
pub fn rust_triple(self) -> &'static str {
match self {
Arch::Arm64 => "aarch64-unknown-linux-gnu",
Arch::Amd64 => "x86_64-unknown-linux-gnu",
}
}
pub fn bun_target(self) -> &'static str {
match self {
Arch::Arm64 => "bun-linux-arm64",
Arch::Amd64 => "bun-linux-x64",
}
}
pub fn deb_arch(self) -> &'static str {
match self {
Arch::Arm64 => "arm64",
Arch::Amd64 => "amd64",
}
}
pub fn from_deb_arch(s: &str) -> Result<Arch> {
match s {
"arm64" => Ok(Arch::Arm64),
"amd64" => Ok(Arch::Amd64),
other => bail!(
"unsupported --arch '{}' for native/standalone apps (use arm64 or amd64)",
other
),
}
}
pub fn host() -> Option<Arch> {
match std::env::consts::ARCH {
"aarch64" => Some(Arch::Arm64),
"x86_64" => Some(Arch::Amd64),
_ => None,
}
}
}
#[allow(dead_code)]
pub fn needs_cross(arch: Arch) -> bool {
Arch::host() != Some(arch)
}
#[allow(dead_code)]
pub fn cargo_cdylib_command(arch: Arch, use_cross: bool) -> (String, Vec<String>) {
let prog = if use_cross { "cross" } else { "cargo" }.to_string();
let args = vec![
"build".into(),
"--release".into(),
"--target".into(),
arch.rust_triple().into(),
];
(prog, args)
}
#[allow(dead_code)]
pub fn cargo_bin_command(arch: Arch, use_cross: bool, bin: &str) -> (String, Vec<String>) {
let prog = if use_cross { "cross" } else { "cargo" }.to_string();
let args = vec![
"build".into(),
"--release".into(),
"--target".into(),
arch.rust_triple().into(),
"--bin".into(),
bin.into(),
];
(prog, args)
}
#[allow(dead_code)]
pub fn bun_compile_command(arch: Arch, entry: &str, outfile: &str) -> (String, Vec<String>) {
(
"bun".to_string(),
vec![
"build".into(),
entry.into(),
"--compile".into(),
format!("--target={}", arch.bun_target()),
"--outfile".into(),
outfile.into(),
],
)
}
#[allow(dead_code)]
pub fn cdylib_output_path(project: &Path, arch: Arch, crate_lib_name: &str) -> PathBuf {
project
.join("target")
.join(arch.rust_triple())
.join("release")
.join(format!("lib{}.so", crate_lib_name))
}
#[allow(dead_code)]
pub fn bin_output_path(project: &Path, arch: Arch, bin: &str) -> PathBuf {
project
.join("target")
.join(arch.rust_triple())
.join("release")
.join(bin)
}
#[allow(dead_code)]
pub fn staged_artifact_dir(project: &Path, arch: Arch) -> PathBuf {
project.join("target").join("node-app").join(arch.deb_arch())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn arch_mappings_are_stable() {
assert_eq!(Arch::Arm64.rust_triple(), "aarch64-unknown-linux-gnu");
assert_eq!(Arch::Amd64.rust_triple(), "x86_64-unknown-linux-gnu");
assert_eq!(Arch::Arm64.bun_target(), "bun-linux-arm64");
assert_eq!(Arch::Amd64.bun_target(), "bun-linux-x64");
assert_eq!(Arch::Arm64.deb_arch(), "arm64");
assert_eq!(Arch::Amd64.deb_arch(), "amd64");
}
#[test]
fn from_deb_arch_parses_and_rejects() {
assert_eq!(Arch::from_deb_arch("arm64").unwrap(), Arch::Arm64);
assert_eq!(Arch::from_deb_arch("amd64").unwrap(), Arch::Amd64);
let err = Arch::from_deb_arch("all").unwrap_err().to_string();
assert!(err.contains("arm64 or amd64"), "got: {err}");
}
#[test]
fn cargo_commands_pick_cross_and_target() {
let (prog, args) = cargo_cdylib_command(Arch::Arm64, true);
assert_eq!(prog, "cross");
assert_eq!(
args,
vec!["build", "--release", "--target", "aarch64-unknown-linux-gnu"]
);
let (prog, args) = cargo_bin_command(Arch::Amd64, false, "node-app-foo");
assert_eq!(prog, "cargo");
assert_eq!(
args,
vec![
"build",
"--release",
"--target",
"x86_64-unknown-linux-gnu",
"--bin",
"node-app-foo"
]
);
}
#[test]
fn bun_compile_command_targets_arch() {
let (prog, args) =
bun_compile_command(Arch::Arm64, "src/index.ts", "target/node-app/arm64/node-app-foo");
assert_eq!(prog, "bun");
assert_eq!(
args,
vec![
"build",
"src/index.ts",
"--compile",
"--target=bun-linux-arm64",
"--outfile",
"target/node-app/arm64/node-app-foo"
]
);
}
#[test]
fn artifact_paths_follow_cargo_layout() {
let p = Path::new("/proj");
assert_eq!(
cdylib_output_path(p, Arch::Arm64, "node_app_foo"),
Path::new("/proj/target/aarch64-unknown-linux-gnu/release/libnode_app_foo.so")
);
assert_eq!(
bin_output_path(p, Arch::Amd64, "node-app-foo"),
Path::new("/proj/target/x86_64-unknown-linux-gnu/release/node-app-foo")
);
assert_eq!(
staged_artifact_dir(p, Arch::Arm64),
Path::new("/proj/target/node-app/arm64")
);
}
}