use crate::docker_service::error::{DockerServiceError, DockerServiceResult};
use tracing::{info, warn};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Architecture {
Amd64,
Arm64,
}
impl Architecture {
pub fn as_str(&self) -> &'static str {
match self {
Architecture::Amd64 => "amd64",
Architecture::Arm64 => "arm64",
}
}
pub fn display_name(&self) -> &'static str {
match self {
Architecture::Amd64 => "x86_64 (AMD64)",
Architecture::Arm64 => "ARM64 (AArch64)",
}
}
pub fn from_str(s: &str) -> Option<Self> {
match s.to_lowercase().as_str() {
"amd64" | "x86_64" | "x86-64" => Some(Architecture::Amd64),
"arm64" | "aarch64" | "arm" => Some(Architecture::Arm64),
_ => None,
}
}
}
impl std::fmt::Display for Architecture {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_str())
}
}
pub fn detect_architecture() -> Architecture {
let arch = std::env::consts::ARCH;
info!("Detected system architecture: {}", arch);
let detected = match arch {
"x86_64" => Architecture::Amd64,
"aarch64" => Architecture::Arm64,
_ => {
warn!("Unknown architecture '{}', defaulting to amd64", arch);
Architecture::Amd64
}
};
info!(
"Mapped to supported architecture: {}",
detected.display_name()
);
detected
}
#[allow(dead_code)]
pub fn validate_architecture(arch: Architecture) -> DockerServiceResult<()> {
match arch {
Architecture::Amd64 | Architecture::Arm64 => {
info!("Architecture {} is supported", arch.display_name());
Ok(())
}
}
}
#[allow(dead_code)]
pub fn get_image_pattern(arch: Architecture) -> String {
format!("*-{}.tar", arch.as_str())
}
#[allow(dead_code)]
pub fn check_architecture_images_exist(
images_dir: &std::path::Path,
arch: Architecture,
) -> DockerServiceResult<Vec<std::path::PathBuf>> {
if !images_dir.exists() {
return Err(DockerServiceError::FileSystem(format!(
"Image directory does not exist: {}",
images_dir.display()
)));
}
let pattern = get_image_pattern(arch);
let mut found_images = Vec::new();
if let Ok(entries) = std::fs::read_dir(images_dir) {
for entry in entries.flatten() {
let path = entry.path();
if path.is_file()
&& let Some(file_name) = path.file_name().and_then(|n| n.to_str())
{
if file_name.ends_with(&format!("-{}.tar", arch.as_str())) {
found_images.push(path);
}
}
}
}
if found_images.is_empty() {
warn!(
"No image files found for architecture {} (pattern: {})",
arch.display_name(),
pattern
);
} else {
info!(
"Found {} image files for architecture {}",
found_images.len(),
arch.display_name()
);
}
Ok(found_images)
}
#[allow(dead_code)]
pub fn get_available_architectures(
images_dir: &std::path::Path,
) -> DockerServiceResult<std::collections::HashMap<Architecture, Vec<std::path::PathBuf>>> {
let mut result = std::collections::HashMap::new();
for &arch in &[Architecture::Amd64, Architecture::Arm64] {
let images = check_architecture_images_exist(images_dir, arch)?;
if !images.is_empty() {
result.insert(arch, images);
}
}
if result.is_empty() {
return Err(DockerServiceError::ArchitectureDetection(format!(
"No image files for supported architectures found in directory {}",
images_dir.display()
)));
}
info!("Available architecture summary:");
for (arch, images) in &result {
info!(" {} -> {} image files", arch.display_name(), images.len());
}
Ok(result)
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs;
use tempfile::tempdir;
#[test]
fn test_architecture_from_str() {
assert_eq!(Architecture::from_str("amd64"), Some(Architecture::Amd64));
assert_eq!(Architecture::from_str("x86_64"), Some(Architecture::Amd64));
assert_eq!(Architecture::from_str("arm64"), Some(Architecture::Arm64));
assert_eq!(Architecture::from_str("aarch64"), Some(Architecture::Arm64));
assert_eq!(Architecture::from_str("unknown"), None);
}
#[test]
fn test_architecture_display() {
assert_eq!(Architecture::Amd64.as_str(), "amd64");
assert_eq!(Architecture::Arm64.as_str(), "arm64");
assert_eq!(format!("{}", Architecture::Amd64), "amd64");
}
#[test]
fn test_detect_architecture() {
let arch = detect_architecture();
assert!(matches!(arch, Architecture::Amd64 | Architecture::Arm64));
}
#[test]
fn test_check_architecture_images_exist() {
let temp_dir = tempdir().unwrap();
let images_dir = temp_dir.path().join("images");
fs::create_dir_all(&images_dir).unwrap();
fs::write(images_dir.join("test-amd64.tar"), b"fake image").unwrap();
fs::write(images_dir.join("another-amd64.tar"), b"fake image").unwrap();
fs::write(images_dir.join("service-arm64.tar"), b"fake image").unwrap();
let amd64_images =
check_architecture_images_exist(&images_dir, Architecture::Amd64).unwrap();
assert_eq!(amd64_images.len(), 2);
let arm64_images =
check_architecture_images_exist(&images_dir, Architecture::Arm64).unwrap();
assert_eq!(arm64_images.len(), 1);
}
}