1use std::path::PathBuf;
4use thiserror::Error;
5use tokio::process::Command;
6
7#[derive(Error, Debug)]
9pub enum CrossError {
10 #[error("Invalid argument: {0}")]
11 InvalidArgument(String),
12
13 #[error("Missing required value for option: {0}")]
14 MissingValue(String),
15
16 #[error("Unknown option: {0}")]
17 UnknownOption(String),
18
19 #[error("Target not found: {target}\nUse '--list-targets' to see available targets")]
20 TargetNotFound { target: String },
21
22 #[error("Unsupported target: {0}")]
23 UnsupportedTarget(String),
24
25 #[error("Unsupported glibc version '{version}'\nSupported versions: {supported}")]
26 UnsupportedGlibcVersion { version: String, supported: String },
27
28 #[error("Unsupported iPhone SDK version '{version}'\nSupported versions: {supported}")]
29 UnsupportedIphoneSdkVersion { version: String, supported: String },
30
31 #[error("Unsupported macOS SDK version '{version}'\nSupported versions: {supported}")]
32 UnsupportedMacosSdkVersion { version: String, supported: String },
33
34 #[error("Unsupported FreeBSD version '{version}'\nSupported versions: {supported}")]
35 UnsupportedFreebsdVersion { version: String, supported: String },
36
37 #[error("Download failed: {0}")]
38 DownloadFailed(String),
39
40 #[error("HTTP request failed: {0}")]
41 HttpError(#[from] reqwest::Error),
42
43 #[error("IO error: {message}\nDetails: {source}")]
44 IoError {
45 message: String,
46 source: std::io::Error,
47 },
48
49 #[error("Program not found: '{program}'\nPlease ensure it is installed and available in PATH")]
50 ProgramNotFound { program: String },
51
52 #[error("Failed to execute command: {command}\nError: {reason}")]
53 CommandExecutionFailed { command: String, reason: String },
54
55 #[error("Failed to extract archive: {0}")]
56 ExtractionFailed(String),
57
58 #[error("Unsupported archive format: {0}")]
59 UnsupportedArchiveFormat(String),
60
61 #[error("Cross-compiler not found at: {path}\nPlease check the toolchain installation")]
62 CompilerNotFound { path: PathBuf },
63
64 #[error("SDK not found at: {path}")]
65 SdkNotFound { path: PathBuf },
66
67 #[error("SDK path does not exist: {path}")]
68 SdkPathNotExist { path: PathBuf },
69
70 #[error("Command failed: {command}")]
71 CommandFailed { command: String },
72
73 #[error("Failed to install Rust target: {target}\nRun 'rustup target add {target}' manually to see details")]
74 TargetInstallFailed { target: String },
75
76 #[error("Target '{target}' requires build-std but is not in rustc target list\nUse BUILD_STD=core,alloc or similar to enable build-std")]
77 BuildStdRequired { target: String },
78
79 #[error("Cross-compilation to {target_os} is not supported from {host_os}")]
80 CrossCompilationNotSupported { target_os: String, host_os: String },
81
82 #[error("Unsupported architecture '{arch}' for {os}")]
83 UnsupportedArchitecture { arch: String, os: String },
84
85 #[error("Environment variable error: {0}")]
86 EnvError(String),
87
88 #[error("No matching targets found for pattern '{pattern}'\nUse '{prog} targets' to see available targets", prog = crate::cli::program_name())]
89 NoMatchingTargets { pattern: String },
90
91 #[error("Invalid target triple '{target}': contains invalid character '{char}'\nTarget triples may only contain lowercase letters (a-z), digits (0-9), hyphens (-), and underscores (_)")]
92 InvalidTargetTriple { target: String, char: char },
93
94 #[error("Cargo exited with code {code}")]
95 CargoFailed { code: i32 },
96
97 #[error("JSON serialization error: {0}")]
98 JsonError(#[from] serde_json::Error),
99
100 #[error("Regex error: {0}")]
101 RegexError(#[from] regex_lite::Error),
102
103 #[error("{0}")]
104 Other(String),
105
106 #[error("CLI argument error: {0}")]
107 ClapError(String),
108}
109
110impl From<std::io::Error> for CrossError {
111 fn from(err: std::io::Error) -> Self {
112 match err.kind() {
113 std::io::ErrorKind::NotFound => Self::ProgramNotFound {
114 program: "unknown".to_string(),
115 },
116 std::io::ErrorKind::PermissionDenied => Self::IoError {
117 message: "Permission denied".to_string(),
118 source: err,
119 },
120 _ => Self::IoError {
121 message: err.kind().to_string(),
122 source: err,
123 },
124 }
125 }
126}
127
128pub type Result<T> = std::result::Result<T, CrossError>;
130
131pub async fn run_command(cmd: &mut Command, program: &str) -> Result<std::process::ExitStatus> {
133 cmd.status().await.map_err(|e| match e.kind() {
134 std::io::ErrorKind::NotFound => CrossError::ProgramNotFound {
135 program: program.to_string(),
136 },
137 std::io::ErrorKind::PermissionDenied => CrossError::CommandExecutionFailed {
138 command: program.to_string(),
139 reason: "Permission denied".to_string(),
140 },
141 _ => CrossError::CommandExecutionFailed {
142 command: program.to_string(),
143 reason: e.to_string(),
144 },
145 })
146}
147
148pub async fn run_command_output(cmd: &mut Command, program: &str) -> Result<std::process::Output> {
150 cmd.output().await.map_err(|e| match e.kind() {
151 std::io::ErrorKind::NotFound => CrossError::ProgramNotFound {
152 program: program.to_string(),
153 },
154 std::io::ErrorKind::PermissionDenied => CrossError::CommandExecutionFailed {
155 command: program.to_string(),
156 reason: "Permission denied".to_string(),
157 },
158 _ => CrossError::CommandExecutionFailed {
159 command: program.to_string(),
160 reason: e.to_string(),
161 },
162 })
163}