use std::path::PathBuf;
use std::process::Stdio;
use thiserror::Error;
use tokio::process::Command;
#[derive(Error, Debug)]
pub enum CrossError {
#[error("Invalid argument: {0}")]
InvalidArgument(String),
#[error("Missing required value for option: {0}")]
MissingValue(String),
#[error("Unknown option: {0}")]
UnknownOption(String),
#[error("Target not found: {target}\nUse '--list-targets' to see available targets")]
TargetNotFound { target: String },
#[error("Unsupported target: {0}")]
UnsupportedTarget(String),
#[error("Unsupported glibc version '{version}'\nSupported versions: {supported}")]
UnsupportedGlibcVersion { version: String, supported: String },
#[error("Unsupported iPhone SDK version '{version}'\nSupported versions: {supported}")]
UnsupportedIphoneSdkVersion { version: String, supported: String },
#[error("Unsupported macOS SDK version '{version}'\nSupported versions: {supported}")]
UnsupportedMacosSdkVersion { version: String, supported: String },
#[error("Unsupported FreeBSD version '{version}'\nSupported versions: {supported}")]
UnsupportedFreebsdVersion { version: String, supported: String },
#[error("Download failed: {0}")]
DownloadFailed(String),
#[error("HTTP request failed: {0}")]
HttpError(#[from] reqwest::Error),
#[error("IO error: {message}\nDetails: {source}")]
IoError {
message: String,
source: std::io::Error,
},
#[error("Program not found: '{program}'\nPlease ensure it is installed and available in PATH")]
ProgramNotFound { program: String },
#[error("Failed to execute command: {command}\nError: {reason}")]
CommandExecutionFailed { command: String, reason: String },
#[error("Failed to extract archive: {0}")]
ExtractionFailed(String),
#[error("Unsupported archive format: {0}")]
UnsupportedArchiveFormat(String),
#[error("Cross-compiler not found at: {path}\nPlease check the toolchain installation")]
CompilerNotFound { path: PathBuf },
#[error("SDK not found at: {path}")]
SdkNotFound { path: PathBuf },
#[error("SDK path does not exist: {path}")]
SdkPathNotExist { path: PathBuf },
#[error("Command failed: {command}")]
CommandFailed { command: String },
#[error("Failed to install Rust target: {target}\nRun 'rustup target add {target}' manually to see details")]
TargetInstallFailed { target: String },
#[error("Target '{target}' requires build-std but is not in rustc target list\nUse BUILD_STD=core,alloc or similar to enable build-std")]
BuildStdRequired { target: String },
#[error("Cross-compilation to {target_os} is not supported from {host_os}")]
CrossCompilationNotSupported { target_os: String, host_os: String },
#[error("Unsupported architecture '{arch}' for {os}")]
UnsupportedArchitecture { arch: String, os: String },
#[error("Environment variable error: {0}")]
EnvError(String),
#[error("No matching targets found for pattern '{pattern}'\nUse '{prog} targets' to see available targets", prog = crate::cli::program_name())]
NoMatchingTargets { pattern: String },
#[error("Invalid target triple '{target}': contains invalid character '{char}'\nTarget triples may only contain lowercase letters (a-z), digits (0-9), hyphens (-), and underscores (_)")]
InvalidTargetTriple { target: String, char: char },
#[error("Cargo exited with code {code}")]
CargoFailed { code: i32 },
#[error("JSON serialization error: {0}")]
JsonError(#[from] serde_json::Error),
#[error("Regex error: {0}")]
RegexError(#[from] regex_lite::Error),
#[error("{0}")]
Other(String),
#[error("CLI argument error: {0}")]
ClapError(String),
}
impl From<std::io::Error> for CrossError {
fn from(err: std::io::Error) -> Self {
match err.kind() {
std::io::ErrorKind::NotFound => Self::ProgramNotFound {
program: "unknown".to_string(),
},
std::io::ErrorKind::PermissionDenied => Self::IoError {
message: "Permission denied".to_string(),
source: err,
},
_ => Self::IoError {
message: err.kind().to_string(),
source: err,
},
}
}
}
pub type Result<T> = std::result::Result<T, CrossError>;
pub async fn run_command(cmd: &mut Command, program: &str) -> Result<std::process::ExitStatus> {
if std::env::var_os("CARGO_CROSS_SILENT").is_some() {
cmd.stdout(Stdio::null()).stderr(Stdio::null());
}
cmd.status().await.map_err(|e| match e.kind() {
std::io::ErrorKind::NotFound => CrossError::ProgramNotFound {
program: program.to_string(),
},
std::io::ErrorKind::PermissionDenied => CrossError::CommandExecutionFailed {
command: program.to_string(),
reason: "Permission denied".to_string(),
},
_ => CrossError::CommandExecutionFailed {
command: program.to_string(),
reason: e.to_string(),
},
})
}
pub async fn run_command_output(cmd: &mut Command, program: &str) -> Result<std::process::Output> {
cmd.output().await.map_err(|e| match e.kind() {
std::io::ErrorKind::NotFound => CrossError::ProgramNotFound {
program: program.to_string(),
},
std::io::ErrorKind::PermissionDenied => CrossError::CommandExecutionFailed {
command: program.to_string(),
reason: "Permission denied".to_string(),
},
_ => CrossError::CommandExecutionFailed {
command: program.to_string(),
reason: e.to_string(),
},
})
}