par_term_render/error.rs
1//! Typed error types for par-term-render.
2//!
3//! This module provides structured error types so callers at the crate boundary
4//! can match on specific error variants instead of relying on opaque `anyhow`
5//! strings.
6//!
7//! TODO(QA-010): Other workspace crates (53 files) use bare `anyhow::Result`.
8//! Consider migrating each crate's error types to thiserror-based enums for
9//! better error handling. Priority: `par-term-mcp` (IPC protocol errors),
10//! `par-term-terminal` (PTY/terminal errors).
11
12use thiserror::Error;
13
14/// Top-level error type for the GPU rendering engine.
15///
16/// Covers the main failure categories that callers may want to distinguish:
17/// - GPU initialisation (adapter, device, surface)
18/// - Shader compilation and reload
19/// - Image / texture loading
20/// - GPU surface / presentation
21/// - Screenshot capture
22#[derive(Debug, Error)]
23pub enum RenderError {
24 // -----------------------------------------------------------------------
25 // GPU initialisation
26 // -----------------------------------------------------------------------
27 /// A suitable wgpu GPU adapter could not be found for the given surface.
28 #[error("GPU adapter not found: no compatible GPU adapter available for this surface")]
29 AdapterNotFound,
30
31 /// The wgpu device could not be created or the device was lost.
32 #[error("GPU device error: {0}")]
33 DeviceError(String),
34
35 /// The wgpu surface could not be created for the window.
36 #[error("GPU surface creation failed: {0}")]
37 SurfaceCreation(String),
38
39 // -----------------------------------------------------------------------
40 // Shader errors
41 // -----------------------------------------------------------------------
42 /// The shader source file could not be read from disk.
43 #[error("Shader file read failed for '{path}': {source}")]
44 ShaderFileRead {
45 /// Path to the shader file that could not be read.
46 path: String,
47 /// Underlying I/O error.
48 #[source]
49 source: std::io::Error,
50 },
51
52 /// The GLSL source could not be parsed (transpilation step 1).
53 #[error("GLSL parse error in '{name}':\n{details}")]
54 GlslParse {
55 /// Shader name or path.
56 name: String,
57 /// Human-readable parse error messages.
58 details: String,
59 },
60
61 /// The intermediate WGSL source could not be parsed.
62 #[error("WGSL parse error for '{name}': {details}")]
63 WgslParse {
64 /// Shader name or path.
65 name: String,
66 /// Human-readable parse error details.
67 details: String,
68 },
69
70 /// The shader module failed naga validation.
71 #[error("Shader validation failed for '{name}': {details}")]
72 ShaderValidation {
73 /// Shader name or path.
74 name: String,
75 /// Human-readable validation error details.
76 details: String,
77 },
78
79 /// WGSL generation (from the naga IR) failed.
80 #[error("WGSL code generation failed for '{name}': {details}")]
81 WgslGeneration {
82 /// Shader name or path.
83 name: String,
84 /// Human-readable generation error details.
85 details: String,
86 },
87
88 /// A shader reload was requested but no shader is currently active,
89 /// or a shader compilation error occurred during reload.
90 #[error("Shader error: {0}")]
91 NoActiveShader(String),
92
93 // -----------------------------------------------------------------------
94 // Image / texture loading
95 // -----------------------------------------------------------------------
96 /// An image file could not be opened or decoded.
97 #[error("Image load failed for '{path}': {source}")]
98 ImageLoad {
99 /// Path to the image that failed to load.
100 path: String,
101 /// Underlying image error.
102 #[source]
103 source: image::ImageError,
104 },
105
106 /// The supplied raw RGBA byte slice has an unexpected length.
107 #[error("Invalid RGBA data size: expected {expected} bytes, got {actual} bytes")]
108 InvalidTextureData {
109 /// Expected byte count (`width * height * 4`).
110 expected: usize,
111 /// Actual byte count received.
112 actual: usize,
113 },
114
115 /// A cubemap face image is not square or all faces are not the same size.
116 #[error("Cubemap geometry error: {0}")]
117 CubemapGeometry(String),
118
119 /// A required cubemap face file could not be found on disk.
120 #[error("Cubemap face file not found: {0}")]
121 CubemapFaceNotFound(String),
122
123 // -----------------------------------------------------------------------
124 // Shader / renderer state
125 // -----------------------------------------------------------------------
126 /// A shader renderer that was previously active is no longer available,
127 /// typically due to GPU device loss. The frame should be skipped and
128 /// reinitialization attempted on the next frame.
129 #[error("Shader renderer unavailable (possible GPU device loss): {0}")]
130 ShaderUnavailable(String),
131
132 // -----------------------------------------------------------------------
133 // Surface / presentation
134 // -----------------------------------------------------------------------
135 /// `Surface::get_current_texture()` returned a non-recoverable state
136 /// (outdated, lost, timeout, occluded, validation).
137 #[error("GPU surface error: {0}")]
138 Surface(String),
139
140 // -----------------------------------------------------------------------
141 // Screenshot
142 // -----------------------------------------------------------------------
143 /// The GPU buffer could not be mapped back to CPU memory, or a render
144 /// step during screenshot capture failed.
145 #[error("Screenshot capture failed: {0}")]
146 ScreenshotMap(String),
147
148 /// The pixel data could not be assembled into a final `RgbaImage`.
149 #[error("Screenshot image assembly failed")]
150 ScreenshotImageAssembly,
151}
152
153// ---------------------------------------------------------------------------
154// Convenience conversions from common upstream error types
155// ---------------------------------------------------------------------------
156
157impl From<wgpu::CreateSurfaceError> for RenderError {
158 fn from(e: wgpu::CreateSurfaceError) -> Self {
159 RenderError::SurfaceCreation(e.to_string())
160 }
161}
162
163impl From<wgpu::RequestDeviceError> for RenderError {
164 fn from(e: wgpu::RequestDeviceError) -> Self {
165 RenderError::DeviceError(e.to_string())
166 }
167}