use std::env;
use std::path::{Path, PathBuf};
use std::process::{Command, ExitStatus};
const EMBEDDED_DOCKER_SCRIPT: &str = include_str!("../copter-docker.sh");
pub fn is_docker_available() -> bool {
Command::new("docker").arg("--version").output().map(|o| o.status.success()).unwrap_or(false)
}
fn find_local_script() -> Option<PathBuf> {
let local = Path::new("copter-docker.sh");
if local.exists() {
return Some(local.to_path_buf());
}
if let Ok(exe) = env::current_exe()
&& let Some(dir) = exe.parent()
{
let beside_exe = dir.join("copter-docker.sh");
if beside_exe.exists() {
return Some(beside_exe);
}
}
None
}
pub fn run_in_docker(original_args: &[String]) -> Result<ExitStatus, String> {
if !is_docker_available() {
return Err("Docker is not installed or not running. Please install Docker first.".to_string());
}
let filtered_args: Vec<&str> = original_args.iter().map(|s| s.as_str()).filter(|&arg| arg != "--docker").collect();
if let Some(local_script) = find_local_script() {
eprintln!("Using local script: {}", local_script.display());
return run_local_script(&local_script, &filtered_args);
}
run_embedded_script(&filtered_args)
}
fn run_local_script(script_path: &Path, args: &[&str]) -> Result<ExitStatus, String> {
Command::new("bash")
.arg(script_path)
.args(args)
.status()
.map_err(|e| format!("Failed to execute local docker script: {}", e))
}
fn run_embedded_script(args: &[&str]) -> Result<ExitStatus, String> {
let temp_dir = env::temp_dir();
let script_path = temp_dir.join("copter-docker-embedded.sh");
std::fs::write(&script_path, EMBEDDED_DOCKER_SCRIPT)
.map_err(|e| format!("Failed to write temporary docker script: {}", e))?;
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let mut perms = std::fs::metadata(&script_path)
.map_err(|e| format!("Failed to get script permissions: {}", e))?
.permissions();
perms.set_mode(0o755);
std::fs::set_permissions(&script_path, perms)
.map_err(|e| format!("Failed to set script permissions: {}", e))?;
}
let status = Command::new("bash")
.arg(&script_path)
.args(args)
.status()
.map_err(|e| format!("Failed to execute docker script: {}", e))?;
let _ = std::fs::remove_file(&script_path);
Ok(status)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_embedded_script_not_empty() {
assert!(!EMBEDDED_DOCKER_SCRIPT.is_empty());
assert!(EMBEDDED_DOCKER_SCRIPT.contains("cargo-copter"));
}
#[test]
fn test_find_local_script_returns_none_when_missing() {
let _ = find_local_script();
}
}