1use std::io;
2
3use camino::Utf8PathBuf;
4use thiserror::Error;
5
6#[derive(Debug, Error)]
8pub enum AppleError {
9 #[error(transparent)]
11 Assets(#[from] rustferry_core::AssetError),
12 #[error(transparent)]
14 AssetPipeline(#[from] rustferry_codegen::AssetPipelineError),
15 #[error("invalid iOS build request: {0}")]
17 InvalidRequest(String),
18 #[error("invalid RustFerry configuration: {0}")]
20 InvalidConfig(String),
21 #[error(
23 "iOS builds require macOS; detected host `{host}`. Run this build on a Mac with full Xcode installed"
24 )]
25 UnsupportedHost {
26 host: String,
28 },
29 #[error("Apple build tool `{tool}` was not found; searched: {searched:?}. {fix}")]
31 ToolMissing {
32 tool: String,
34 searched: Vec<Utf8PathBuf>,
36 fix: String,
38 },
39 #[error("full Xcode was not found; developer directory: {developer_dir:?}. {fix}")]
41 XcodeMissing {
42 developer_dir: Option<Utf8PathBuf>,
44 fix: String,
46 },
47 #[error("an iPhone Simulator SDK was not found through xcrun. {fix}")]
49 SimulatorSdkMissing {
50 fix: String,
52 },
53 #[error(
55 "Rust target `{target}` is not installed. Run `rustup target add {target}`, then `cargo ferry doctor`"
56 )]
57 RustTargetMissing {
58 target: String,
60 },
61 #[error("could not start `{program}` during {stage}: {source}")]
63 CommandSpawn {
64 stage: String,
66 program: Utf8PathBuf,
68 #[source]
70 source: io::Error,
71 },
72 #[error("`{program}` timed out during {stage}; log: {log:?}")]
74 CommandTimedOut {
75 stage: String,
77 program: Utf8PathBuf,
79 log: Option<Utf8PathBuf>,
81 },
82 #[error(
84 "`{program}` produced more than {limit_bytes} bytes on {stream} during {stage}; its process tree was stopped; log: {log:?}"
85 )]
86 ProcessOutputTooLarge {
87 stage: String,
89 program: Utf8PathBuf,
91 stream: String,
93 limit_bytes: usize,
95 log: Option<Utf8PathBuf>,
97 },
98 #[error("`{program}` was interrupted during {stage}")]
100 CommandInterrupted {
101 stage: String,
103 program: Utf8PathBuf,
105 },
106 #[error("`{program}` failed during {stage} with status {status}; {summary}; log: {log:?}")]
108 CommandFailed {
109 stage: String,
111 program: Utf8PathBuf,
113 status: String,
115 summary: String,
117 log: Option<Box<Utf8PathBuf>>,
119 },
120 #[error("could not {operation} `{path}`: {source}")]
122 Io {
123 operation: &'static str,
125 path: Utf8PathBuf,
127 #[source]
129 source: io::Error,
130 },
131 #[error("unsafe generated path `{path}`: {reason}")]
133 UnsafeGeneratedPath {
134 path: Utf8PathBuf,
136 reason: String,
138 },
139 #[error("iOS artifact validation failed for `{path}`: {reason}")]
141 InvalidArtifact {
142 path: Utf8PathBuf,
144 reason: String,
146 },
147 #[error("path is not valid UTF-8: {0:?}")]
149 NonUtf8Path(std::path::PathBuf),
150}
151
152pub(crate) fn io_error(
153 operation: &'static str,
154 path: impl Into<Utf8PathBuf>,
155 source: io::Error,
156) -> AppleError {
157 AppleError::Io {
158 operation,
159 path: path.into(),
160 source,
161 }
162}
163
164#[cfg(test)]
165mod tests {
166 use super::*;
167
168 #[test]
169 fn command_failure_stays_compact_and_preserves_its_diagnostic() {
170 assert!(std::mem::size_of::<AppleError>() < 128);
171
172 let error = AppleError::CommandFailed {
173 stage: "archive application".to_owned(),
174 program: Utf8PathBuf::from("xcodebuild"),
175 status: "65".to_owned(),
176 summary: "archive failed".to_owned(),
177 log: Some(Box::new(Utf8PathBuf::from("archive.log"))),
178 };
179 assert_eq!(
180 error.to_string(),
181 "`xcodebuild` failed during archive application with status 65; archive failed; log: Some(\"archive.log\")"
182 );
183 }
184}