screencapturekit/utils/error.rs
1//! Error types for `ScreenCaptureKit`
2//!
3//! This module provides comprehensive error types for all operations in the library.
4//! All operations return [`SCResult<T>`] which is an alias for `Result<T, SCError>`.
5//!
6//! # Examples
7//!
8//! ```
9//! use screencapturekit::prelude::*;
10//!
11//! fn setup_capture() -> SCResult<()> {
12//! // Configure with mutable configuration
13//! let mut config = SCStreamConfiguration::default();
14//! config.set_width(1920);
15//! config.set_height(1080);
16//! Ok(())
17//! }
18//!
19//! // Pattern matching on errors
20//! match setup_capture() {
21//! Ok(_) => println!("Success!"),
22//! Err(SCError::InvalidDimension { field, value }) => {
23//! eprintln!("Invalid {}: {}", field, value);
24//! }
25//! Err(e) => eprintln!("Error: {}", e),
26//! }
27//! ```
28
29use std::fmt;
30
31/// Result type alias for `ScreenCaptureKit` operations
32///
33/// This is a convenience alias for `Result<T, SCError>` used throughout the library.
34///
35/// # Examples
36///
37/// ```
38/// use screencapturekit::prelude::*;
39///
40/// fn validate_dimensions(width: u32, height: u32) -> SCResult<()> {
41/// if width == 0 {
42/// return Err(SCError::invalid_dimension("width", 0));
43/// }
44/// if height == 0 {
45/// return Err(SCError::invalid_dimension("height", 0));
46/// }
47/// Ok(())
48/// }
49///
50/// assert!(validate_dimensions(0, 1080).is_err());
51/// assert!(validate_dimensions(1920, 1080).is_ok());
52/// ```
53pub type SCResult<T> = Result<T, SCError>;
54
55/// Comprehensive error type for `ScreenCaptureKit` operations
56///
57/// This enum covers all possible error conditions that can occur when using
58/// the `ScreenCaptureKit` API. Each variant provides specific context about
59/// what went wrong.
60///
61/// # Examples
62///
63/// ## Creating Errors
64///
65/// ```
66/// use screencapturekit::error::SCError;
67///
68/// // Using helper methods (recommended)
69/// let err = SCError::invalid_dimension("width", 0);
70/// assert_eq!(err.to_string(), "Invalid dimension: width must be greater than 0 (got 0)");
71///
72/// let err = SCError::permission_denied("Screen Recording");
73/// assert!(err.to_string().contains("Screen Recording"));
74/// ```
75///
76/// ## Pattern Matching
77///
78/// ```
79/// use screencapturekit::error::SCError;
80///
81/// fn handle_error(err: SCError) {
82/// match err {
83/// SCError::InvalidDimension { field, value } => {
84/// println!("Invalid {}: {}", field, value);
85/// }
86/// SCError::PermissionDenied(msg) => {
87/// println!("Permission needed: {}", msg);
88/// }
89/// _ => println!("Other error: {}", err),
90/// }
91/// }
92/// ```
93#[derive(Debug, Clone, PartialEq, Eq)]
94pub enum SCError {
95 /// Invalid configuration parameter
96 InvalidConfiguration(String),
97
98 /// Invalid dimension value (width or height)
99 InvalidDimension { field: String, value: usize },
100
101 /// Invalid pixel format
102 InvalidPixelFormat(String),
103
104 /// No shareable content available
105 NoShareableContent(String),
106
107 /// Display not found
108 DisplayNotFound(String),
109
110 /// Window not found
111 WindowNotFound(String),
112
113 /// Application not found
114 ApplicationNotFound(String),
115
116 /// Stream operation error
117 StreamError(String),
118
119 /// Stream already running
120 StreamAlreadyRunning,
121
122 /// Stream not running
123 StreamNotRunning,
124
125 /// Failed to start capture
126 CaptureStartFailed(String),
127
128 /// Failed to stop capture
129 CaptureStopFailed(String),
130
131 /// Buffer lock error
132 BufferLockError(String),
133
134 /// Buffer unlock error
135 BufferUnlockError(String),
136
137 /// Invalid buffer
138 InvalidBuffer(String),
139
140 /// Screenshot capture error
141 ScreenshotError(String),
142
143 /// Permission denied
144 PermissionDenied(String),
145
146 /// Feature not available on this macOS version
147 FeatureNotAvailable {
148 feature: String,
149 required_version: String,
150 },
151
152 /// FFI error
153 FFIError(String),
154
155 /// Null pointer encountered
156 NullPointer(String),
157
158 /// Timeout error
159 Timeout(String),
160
161 /// Generic internal error
162 InternalError(String),
163
164 /// OS error with code
165 OSError { code: i32, message: String },
166}
167
168impl fmt::Display for SCError {
169 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
170 match self {
171 Self::InvalidConfiguration(msg) => write!(f, "Invalid configuration: {msg}"),
172 Self::InvalidDimension { field, value } => {
173 write!(
174 f,
175 "Invalid dimension: {field} must be greater than 0 (got {value})"
176 )
177 }
178 Self::InvalidPixelFormat(msg) => write!(f, "Invalid pixel format: {msg}"),
179 Self::NoShareableContent(msg) => write!(f, "No shareable content available: {msg}"),
180 Self::DisplayNotFound(msg) => write!(f, "Display not found: {msg}"),
181 Self::WindowNotFound(msg) => write!(f, "Window not found: {msg}"),
182 Self::ApplicationNotFound(msg) => write!(f, "Application not found: {msg}"),
183 Self::StreamError(msg) => write!(f, "Stream error: {msg}"),
184 Self::StreamAlreadyRunning => write!(f, "Stream is already running"),
185 Self::StreamNotRunning => write!(f, "Stream is not running"),
186 Self::CaptureStartFailed(msg) => write!(f, "Failed to start capture: {msg}"),
187 Self::CaptureStopFailed(msg) => write!(f, "Failed to stop capture: {msg}"),
188 Self::BufferLockError(msg) => write!(f, "Failed to lock pixel buffer: {msg}"),
189 Self::BufferUnlockError(msg) => write!(f, "Failed to unlock pixel buffer: {msg}"),
190 Self::InvalidBuffer(msg) => write!(f, "Invalid buffer: {msg}"),
191 Self::ScreenshotError(msg) => write!(f, "Screenshot capture failed: {msg}"),
192 Self::PermissionDenied(msg) => {
193 write!(f, "Permission denied: {msg}. Check System Preferences → Security & Privacy → Screen Recording")
194 }
195 Self::FeatureNotAvailable {
196 feature,
197 required_version,
198 } => {
199 write!(
200 f,
201 "Feature not available: {feature} requires macOS {required_version}+"
202 )
203 }
204 Self::FFIError(msg) => write!(f, "FFI error: {msg}"),
205 Self::NullPointer(msg) => write!(f, "Null pointer: {msg}"),
206 Self::Timeout(msg) => write!(f, "Operation timed out: {msg}"),
207 Self::InternalError(msg) => write!(f, "Internal error: {msg}"),
208 Self::OSError { code, message } => write!(f, "OS error {code}: {message}"),
209 }
210 }
211}
212
213impl std::error::Error for SCError {}
214
215impl SCError {
216 /// Create an invalid configuration error
217 ///
218 /// # Examples
219 ///
220 /// ```
221 /// use screencapturekit::error::SCError;
222 ///
223 /// let err = SCError::invalid_config("Queue depth must be positive");
224 /// assert!(err.to_string().contains("Queue depth"));
225 /// ```
226 pub fn invalid_config(message: impl Into<String>) -> Self {
227 Self::InvalidConfiguration(message.into())
228 }
229
230 /// Create an invalid dimension error
231 ///
232 /// Use this when width or height validation fails.
233 ///
234 /// # Examples
235 ///
236 /// ```
237 /// use screencapturekit::error::SCError;
238 ///
239 /// let err = SCError::invalid_dimension("width", 0);
240 /// assert_eq!(
241 /// err.to_string(),
242 /// "Invalid dimension: width must be greater than 0 (got 0)"
243 /// );
244 ///
245 /// let err = SCError::invalid_dimension("height", 0);
246 /// assert!(err.to_string().contains("height"));
247 /// ```
248 pub fn invalid_dimension(field: impl Into<String>, value: usize) -> Self {
249 Self::InvalidDimension {
250 field: field.into(),
251 value,
252 }
253 }
254
255 /// Create a stream error
256 ///
257 /// # Examples
258 ///
259 /// ```
260 /// use screencapturekit::error::SCError;
261 ///
262 /// let err = SCError::stream_error("Failed to start");
263 /// assert!(err.to_string().contains("Stream error"));
264 /// ```
265 pub fn stream_error(message: impl Into<String>) -> Self {
266 Self::StreamError(message.into())
267 }
268
269 /// Create a permission denied error
270 ///
271 /// The error message automatically includes instructions to check System Preferences.
272 ///
273 /// # Examples
274 ///
275 /// ```
276 /// use screencapturekit::error::SCError;
277 ///
278 /// let err = SCError::permission_denied("Screen Recording");
279 /// let msg = err.to_string();
280 /// assert!(msg.contains("Screen Recording"));
281 /// assert!(msg.contains("System Preferences"));
282 /// ```
283 pub fn permission_denied(message: impl Into<String>) -> Self {
284 Self::PermissionDenied(message.into())
285 }
286
287 /// Create an FFI error
288 ///
289 /// Use for errors crossing the Rust/Swift boundary.
290 ///
291 /// # Examples
292 ///
293 /// ```
294 /// use screencapturekit::error::SCError;
295 ///
296 /// let err = SCError::ffi_error("Swift bridge call failed");
297 /// assert!(err.to_string().contains("FFI error"));
298 /// ```
299 pub fn ffi_error(message: impl Into<String>) -> Self {
300 Self::FFIError(message.into())
301 }
302
303 /// Create an internal error
304 ///
305 /// # Examples
306 ///
307 /// ```
308 /// use screencapturekit::error::SCError;
309 ///
310 /// let err = SCError::internal_error("Unexpected state");
311 /// assert!(err.to_string().contains("Internal error"));
312 /// ```
313 pub fn internal_error(message: impl Into<String>) -> Self {
314 Self::InternalError(message.into())
315 }
316
317 /// Create a null pointer error
318 ///
319 /// # Examples
320 ///
321 /// ```
322 /// use screencapturekit::error::SCError;
323 ///
324 /// let err = SCError::null_pointer("Display pointer");
325 /// assert!(err.to_string().contains("Null pointer"));
326 /// assert!(err.to_string().contains("Display pointer"));
327 /// ```
328 pub fn null_pointer(context: impl Into<String>) -> Self {
329 Self::NullPointer(context.into())
330 }
331
332 /// Create a feature not available error
333 ///
334 /// Use when a feature requires a newer macOS version.
335 ///
336 /// # Examples
337 ///
338 /// ```
339 /// use screencapturekit::error::SCError;
340 ///
341 /// let err = SCError::feature_not_available("Screenshot Manager", "14.0");
342 /// let msg = err.to_string();
343 /// assert!(msg.contains("Screenshot Manager"));
344 /// assert!(msg.contains("14.0"));
345 /// ```
346 pub fn feature_not_available(feature: impl Into<String>, version: impl Into<String>) -> Self {
347 Self::FeatureNotAvailable {
348 feature: feature.into(),
349 required_version: version.into(),
350 }
351 }
352
353 /// Create a buffer lock error
354 ///
355 /// # Examples
356 ///
357 /// ```
358 /// use screencapturekit::error::SCError;
359 ///
360 /// let err = SCError::buffer_lock_error("Already locked");
361 /// assert!(err.to_string().contains("lock pixel buffer"));
362 /// ```
363 pub fn buffer_lock_error(message: impl Into<String>) -> Self {
364 Self::BufferLockError(message.into())
365 }
366
367 /// Create an OS error with error code
368 ///
369 /// # Examples
370 ///
371 /// ```
372 /// use screencapturekit::error::SCError;
373 ///
374 /// let err = SCError::os_error(-1, "System call failed");
375 /// let msg = err.to_string();
376 /// assert!(msg.contains("-1"));
377 /// assert!(msg.contains("System call failed"));
378 /// ```
379 pub fn os_error(code: i32, message: impl Into<String>) -> Self {
380 Self::OSError {
381 code,
382 message: message.into(),
383 }
384 }
385}
386
387// Legacy compatibility
388impl SCError {
389 /// Create from a message string (for backward compatibility)
390 ///
391 /// **Note:** Prefer using specific error constructors like [`SCError::invalid_config`]
392 /// or other helper methods for better error categorization.
393 ///
394 /// # Examples
395 ///
396 /// ```
397 /// use screencapturekit::error::SCError;
398 ///
399 /// // Old style (still works)
400 /// let err = SCError::new("Something went wrong");
401 /// assert!(err.to_string().contains("Something went wrong"));
402 /// ```
403 pub fn new(message: impl Into<String>) -> Self {
404 Self::InternalError(message.into())
405 }
406
407 /// Get the error message (for backward compatibility)
408 ///
409 /// **Note:** Prefer using [`ToString::to_string`] which provides the same functionality.
410 ///
411 /// # Examples
412 ///
413 /// ```
414 /// use screencapturekit::error::SCError;
415 ///
416 /// let err = SCError::invalid_dimension("width", 0);
417 /// let msg = err.message();
418 /// assert!(msg.contains("width"));
419 /// assert!(msg.contains("0"));
420 /// ```
421 pub fn message(&self) -> String {
422 self.to_string()
423 }
424}
425
426/// Helper function to create an error (for backward compatibility)
427///
428/// **Note:** Prefer using [`SCError::new`] or specific constructors.
429///
430/// # Examples
431///
432/// ```
433/// use screencapturekit::utils::error::create_sc_error;
434///
435/// let err = create_sc_error("Something failed");
436/// assert!(err.to_string().contains("Something failed"));
437/// ```
438pub fn create_sc_error(message: &str) -> SCError {
439 SCError::new(message)
440}