Skip to main content

cargo_cross/
error.rs

1//! Error types for cargo-cross
2
3use std::path::PathBuf;
4use thiserror::Error;
5use tokio::process::Command;
6
7/// Main error type for cargo-cross
8#[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
128/// Result type alias for cargo-cross
129pub type Result<T> = std::result::Result<T, CrossError>;
130
131/// Execute a command and return its status, with improved error messages
132pub 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
148/// Execute a command and return its output, with improved error messages
149pub 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}