mujoco_rs/error.rs
1//! Error types for MuJoCo-rs operations.
2//!
3//! - [`MjDataError`] - physics data, model-compatibility, and Jacobian operations.
4//! - [`MjSceneError`] - 3-D scene and visualization operations (`MjvScene`).
5//! - [`MjrContextError`] - GPU rendering-context operations (`MjrContext`).
6//! - [`MjEditError`] - model-specification editing operations (`MjSpec`).
7//! - [`MjModelError`] - model loading, saving, and state operations (`MjModel`).
8//! - [`MjVfsError`] - virtual file system operations (`MjVfs`).
9//! - [`MjPluginError`] - plugin library loading operations.
10//! - [`MjBatchError`] - batched simulation operations ([`MjBatch`](crate::wrappers::MjBatch)).
11//! - [`GlInitError`] - OpenGL / window initialization (feature-gated).
12use std::path::PathBuf;
13use std::sync::Arc;
14use std::fmt;
15use std::io;
16
17use crate::wrappers::mj_model::{MjModelLayout, MjtObjBase};
18
19/// Errors that can occur in [`MjData`](crate::wrappers::MjData) physics data
20/// and Jacobian operations.
21#[derive(Debug, Clone, PartialEq, Eq)]
22#[non_exhaustive]
23pub enum MjDataError {
24 /// A provided object or body index was out of the valid range.
25 IndexOutOfBounds {
26 /// Name of the index parameter, e.g. `"body_id"` or `"geom_id"`.
27 kind: &'static str,
28 /// The index value that was passed.
29 id: usize,
30 /// Exclusive upper bound of the valid range.
31 upper: usize,
32 },
33 /// The provided MuJoCo object type is not supported by this operation.
34 UnsupportedObjectType(MjtObjBase),
35 /// MuJoCo failed to allocate the requested structure.
36 AllocationFailed,
37 /// A buffer passed to the operation is too small for the required data.
38 BufferTooSmall {
39 /// Descriptive name of the buffer (e.g. `"destination"`, `"state"`).
40 name: &'static str,
41 /// Actual length of the buffer that was provided.
42 got: usize,
43 /// Minimum length the buffer must have.
44 needed: usize,
45 },
46 /// A slice or array parameter has the wrong length for the operation.
47 LengthMismatch {
48 /// Descriptive name of the parameter.
49 name: &'static str,
50 /// Expected length.
51 expected: usize,
52 /// Actual length that was provided.
53 got: usize,
54 },
55 /// Two model-bound objects were created from models that are not compatible.
56 ///
57 /// This is returned by APIs that require a matching model memory layout,
58 /// including data-copy operations and info-view accessors.
59 IncompatibleModel {
60 /// Layout of the source object's model.
61 source: Arc<MjModelLayout>,
62 /// Layout of the destination object's model.
63 destination: Arc<MjModelLayout>,
64 },
65 /// The specified actuator or sensor has no associated history buffer.
66 NoHistoryBuffer {
67 /// `"actuator"` or `"sensor"`.
68 kind: &'static str,
69 /// The zero-based index of the actuator or sensor.
70 id: usize,
71 },
72 /// A history buffer cursor in the written state is outside its valid range.
73 ///
74 /// [`MjData::set_state`](crate::wrappers::MjData::set_state) rejects the write and leaves
75 /// the history buffer unchanged.
76 InvalidHistoryCursor {
77 /// `"actuator"` or `"sensor"`.
78 kind: &'static str,
79 /// The zero-based index of the actuator or sensor.
80 id: usize,
81 /// Number of samples in that history buffer; a valid cursor lies in `0..nsample`.
82 nsample: usize,
83 },
84 /// The contact buffer is full; no more contacts can be added.
85 ContactBufferFull,
86 /// A filesystem path argument contains invalid UTF-8.
87 InvalidUtf8Path,
88 /// A file I/O operation failed.
89 IoError {
90 /// Path of the file.
91 path: PathBuf,
92 /// Kind of the I/O failure.
93 kind: io::ErrorKind,
94 },
95}
96
97impl fmt::Display for MjDataError {
98 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
99 match self {
100 Self::IndexOutOfBounds { kind, id, upper } => {
101 write!(f, "{kind} {id} is out of bounds [0, {upper})")
102 }
103 Self::UnsupportedObjectType(obj_type) => {
104 write!(f, "object type {obj_type:?} is not supported by this operation")
105 }
106 Self::AllocationFailed => {
107 write!(f, "MuJoCo allocation failed")
108 }
109 Self::BufferTooSmall { name, got, needed } => {
110 write!(
111 f,
112 "{name} buffer is too small: got {got} elements, \
113 but need at least {needed}"
114 )
115 }
116 Self::IncompatibleModel { .. } => write!(f, "incompatible model"),
117 Self::LengthMismatch { name, expected, got } => {
118 write!(
119 f,
120 "{name} has wrong length: expected {expected}, got {got}"
121 )
122 }
123 Self::NoHistoryBuffer { kind, id } => {
124 write!(f, "{kind} {id} has no history buffer")
125 }
126 Self::InvalidHistoryCursor { kind, id, nsample } => {
127 write!(f, "{kind} {id} history cursor is out of bounds [0, {nsample})")
128 }
129 Self::ContactBufferFull => {
130 write!(f, "contact buffer is full")
131 }
132 Self::InvalidUtf8Path => {
133 write!(f, "path contains invalid UTF-8")
134 }
135 Self::IoError { path, kind } => {
136 write!(f, "file I/O failed for {}: {kind}", path.display())
137 }
138 }
139 }
140}
141
142impl std::error::Error for MjDataError {}
143
144
145/// Errors that can occur in 3-D scene and visualization operations
146/// ([`MjvScene`](crate::wrappers::MjvScene)).
147#[derive(Debug, Clone, PartialEq, Eq)]
148#[non_exhaustive]
149pub enum MjSceneError {
150 /// No more space is available for new geoms in the scene.
151 ///
152 /// Increase the `max_geom` capacity passed to
153 /// [`MjvScene::new`](crate::wrappers::MjvScene::new).
154 SceneFull {
155 /// The current maximum geom capacity of the scene.
156 capacity: i32,
157 },
158 /// A string label exceeds the fixed-size label buffer of an [`MjvGeom`](crate::wrappers::MjvGeom).
159 LabelTooLong {
160 /// Length of the label in bytes.
161 len: usize,
162 /// Maximum number of bytes (excluding the NUL terminator) the buffer can hold.
163 capacity: usize,
164 },
165 /// The figure's line-data buffer for a given plot is full.
166 FigureBufferFull {
167 /// Index of the plot whose buffer is full.
168 plot_index: usize,
169 /// Maximum number of data points the buffer can hold.
170 capacity: usize,
171 },
172 /// A point index is out of range for the current data in a figure plot.
173 FigureIndexOutOfBounds {
174 /// Index of the plot.
175 plot_index: usize,
176 /// The point index that was provided.
177 point_index: usize,
178 /// Current number of data points in the plot.
179 current_len: usize,
180 },
181 /// A plot index is out of the valid range `[0, mjMAXLINE)`.
182 InvalidPlotIndex {
183 /// The out-of-range plot index that was provided.
184 plot_index: usize,
185 /// Maximum number of plots (`mjMAXLINE`).
186 max_plots: usize,
187 },
188 /// A geom label string contains non-ASCII bytes.
189 ///
190 /// MuJoCo's renderer treats the label buffer as ASCII; multi-byte UTF-8
191 /// sequences would be rendered as garbage characters.
192 NonAsciiLabel,
193 /// An integer value does not correspond to any known camera type variant.
194 InvalidCameraType(i32),
195}
196
197impl fmt::Display for MjSceneError {
198 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
199 match self {
200 Self::SceneFull { capacity } => {
201 write!(
202 f,
203 "scene geom buffer is full (capacity = {capacity}); \
204 increase max_geom when constructing MjvScene"
205 )
206 }
207 Self::LabelTooLong { len, capacity } => {
208 write!(
209 f,
210 "label of {len} bytes exceeds the fixed buffer capacity of {capacity} bytes"
211 )
212 }
213 Self::FigureBufferFull { plot_index, capacity } => {
214 write!(
215 f,
216 "figure plot {plot_index} buffer is full \
217 (capacity = {capacity} data points)"
218 )
219 }
220 Self::FigureIndexOutOfBounds { plot_index, point_index, current_len } => {
221 write!(
222 f,
223 "point index {point_index} is out of bounds for plot {plot_index} \
224 (current length = {current_len})"
225 )
226 }
227 Self::InvalidPlotIndex { plot_index, max_plots } => {
228 write!(
229 f,
230 "plot index {plot_index} is out of range [0, {max_plots})"
231 )
232 }
233 Self::NonAsciiLabel => {
234 write!(f, "label contains non-ASCII characters")
235 }
236 Self::InvalidCameraType(raw) => {
237 write!(f, "unknown camera type {raw}")
238 }
239 }
240 }
241}
242
243impl std::error::Error for MjSceneError {}
244
245
246/// Errors that can occur in GPU rendering-context ([`MjrContext`](crate::wrappers::MjrContext)) operations.
247#[derive(Debug, Clone, PartialEq, Eq)]
248#[non_exhaustive]
249pub enum MjrContextError {
250 /// An index passed to a rendering-context operation is out of the valid range.
251 IndexOutOfBounds {
252 /// The index that was supplied.
253 id: usize,
254 /// Exclusive upper bound of the valid range.
255 len: usize,
256 },
257 /// A viewport has invalid (negative) dimensions.
258 InvalidViewport {
259 /// The viewport width that was provided.
260 width: i32,
261 /// The viewport height that was provided.
262 height: i32,
263 },
264 /// A pixel buffer passed to a rendering-context operation is too small.
265 BufferTooSmall {
266 /// Descriptive name of the buffer (e.g. `"rgb"`, `"depth"`).
267 name: &'static str,
268 /// Actual length of the buffer that was provided.
269 got: usize,
270 /// Minimum length the buffer must have.
271 needed: usize,
272 },
273}
274
275impl fmt::Display for MjrContextError {
276 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
277 match self {
278 Self::IndexOutOfBounds { id, len } => {
279 write!(f, "index {id} is out of range [0, {len})")
280 }
281 Self::InvalidViewport { width, height } => {
282 write!(
283 f,
284 "viewport dimensions must be non-negative, got {width}x{height}"
285 )
286 }
287 Self::BufferTooSmall { name, got, needed } => {
288 write!(
289 f,
290 "{name} buffer is too small: got {got} elements, \
291 but need at least {needed}"
292 )
293 }
294 }
295 }
296}
297
298impl std::error::Error for MjrContextError {}
299
300
301/// Errors that can occur in model-specification editing operations
302/// ([`MjSpec`](crate::wrappers::mj_editing::MjSpec) and related types).
303#[derive(Debug, Clone, PartialEq, Eq)]
304#[non_exhaustive]
305pub enum MjEditError {
306 /// MuJoCo failed to allocate the requested spec or model element.
307 AllocationFailed,
308 /// A filesystem path argument contains invalid UTF-8.
309 InvalidUtf8Path,
310 /// MuJoCo failed to parse the XML (or other format) input.
311 ParseFailed(String),
312 /// Compiling the spec into a model failed. Carries MuJoCo's error message, or the reason the
313 /// wrapper rejected the spec before the compile step.
314 CompileFailed(String),
315 /// MuJoCo failed to save the spec to XML.
316 SaveFailed(String),
317 /// Some operation on [`MjSpec`](crate::wrappers::mj_editing::MjSpec) failed in MuJoCo.
318 OperationFailed(String),
319 /// A referenced element (e.g. parent default class) was not found.
320 NotFound,
321 /// An element with the same name already exists.
322 AlreadyExists,
323 /// This operation is not supported for the current element.
324 UnsupportedOperation,
325 /// Deleting the element failed. Carries MuJoCo's error message, or the reason the wrapper
326 /// rejected the element.
327 DeleteFailed(String),
328 /// Attaching the child element failed. Carries MuJoCo's error message.
329 AttachFailed(String),
330 /// The output buffer passed to [`MjSpec::save_xml_string`](crate::wrappers::mj_editing::MjSpec::save_xml_string)
331 /// was too small to hold the XML.
332 ///
333 /// `required_size` follows `snprintf`-style semantics: it is the number of bytes MuJoCo would
334 /// write, **not** counting the NUL terminator. To retry successfully, pass a buffer of at
335 /// least `required_size + 1` bytes.
336 XmlBufferTooSmall {
337 /// Number of bytes MuJoCo would write (excluding the NUL terminator).
338 /// Pass a buffer of at least `required_size + 1` bytes to retry.
339 required_size: usize,
340 },
341 /// Returned when the parameters to the given method/function are invalid.
342 InvalidParameter(String),
343 /// The given index is out of range.
344 IndexOutOfBounds {
345 /// The index that was supplied.
346 id: usize,
347 /// Exclusive upper bound of the valid range.
348 len: usize,
349 },
350 /// The frame belongs to a body different from the one where a frame is being set.
351 FrameParentMismatch,
352}
353
354impl fmt::Display for MjEditError {
355 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
356 match self {
357 Self::AllocationFailed => write!(f, "MuJoCo failed to allocate the spec or model element"),
358 Self::InvalidUtf8Path => write!(f, "path contains invalid UTF-8"),
359 Self::ParseFailed(msg) => write!(f, "parse failed: {msg}"),
360 Self::CompileFailed(msg) => write!(f, "compilation failed: {msg}"),
361 Self::SaveFailed(msg) => write!(f, "save failed: {msg}"),
362 Self::OperationFailed(msg) => write!(f, "operation failed: {msg}"),
363 Self::NotFound => write!(f, "referenced element not found"),
364 Self::AlreadyExists => write!(f, "element with the same name already exists"),
365 Self::UnsupportedOperation => write!(f, "this operation is not supported"),
366 Self::DeleteFailed(msg) => write!(f, "delete failed: {msg}"),
367 Self::AttachFailed(msg) => write!(f, "attach failed: {msg}"),
368 Self::XmlBufferTooSmall { required_size } => write!(
369 f,
370 "XML output buffer too small; retry with at least {} bytes",
371 required_size + 1
372 ),
373 Self::InvalidParameter(msg) => write!(f, "{msg}"),
374 Self::IndexOutOfBounds { id, len } => write!(
375 f,
376 "index {id} is out of bounds (length is {len})"
377 ),
378 Self::FrameParentMismatch => write!(f, "frame belongs to a different body"),
379 }
380 }
381}
382
383impl std::error::Error for MjEditError {}
384
385
386/// Errors that can occur in [`MjModel`](crate::wrappers::MjModel) operations.
387#[derive(Debug, Clone, PartialEq, Eq)]
388#[non_exhaustive]
389pub enum MjModelError {
390 /// A filesystem path argument contains invalid UTF-8.
391 InvalidUtf8Path,
392 /// MuJoCo failed to load the model from a file, string, or buffer.
393 LoadFailed(String),
394 /// MuJoCo failed to load an asset of an already constructed [`MjModel`](crate::wrappers::MjModel).
395 AssetLoadFailed(String),
396 /// MuJoCo failed to save the model XML.
397 SaveFailed(String),
398 /// MuJoCo failed to allocate the requested structure.
399 AllocationFailed,
400 /// The state source slice has the wrong length for the given spec.
401 StateSliceLengthMismatch {
402 /// Expected length.
403 expected: usize,
404 /// Actual length.
405 got: usize,
406 },
407 /// A vector has the wrong length for the model layout.
408 LengthMismatch {
409 /// Descriptive name of the parameter.
410 name: &'static str,
411 /// Expected length.
412 expected: usize,
413 /// Actual length that was provided.
414 got: usize,
415 },
416 /// The destination spec is not a subset of the source spec.
417 SpecNotSubset,
418 /// A destination buffer is too small for the operation.
419 BufferTooSmall {
420 /// Minimum number of elements required.
421 needed: usize,
422 /// Actual number of elements available.
423 available: usize,
424 },
425 /// Two model-bound objects were created from models that are not compatible.
426 IncompatibleModel {
427 /// Layout of the source object's model.
428 source: Arc<MjModelLayout>,
429 /// Layout of the destination object's model.
430 destination: Arc<MjModelLayout>,
431 },
432 /// A virtual-file-system operation failed while loading a model from a string.
433 VfsError(MjVfsError),
434 /// The given index is out of range.
435 IndexOutOfBounds {
436 /// The index that was supplied.
437 id: usize,
438 /// Exclusive upper bound of the valid range.
439 len: usize,
440 },
441 /// A file I/O operation failed.
442 IoError {
443 /// Path of the file.
444 path: PathBuf,
445 /// Kind of the I/O failure.
446 kind: io::ErrorKind,
447 },
448}
449
450impl fmt::Display for MjModelError {
451 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
452 match self {
453 Self::InvalidUtf8Path => write!(f, "path contains invalid UTF-8"),
454 Self::LoadFailed(msg) => write!(f, "model load failed: {msg}"),
455 Self::SaveFailed(msg) => write!(f, "model save failed: {msg}"),
456 Self::AssetLoadFailed(msg) => write!(f, "asset load failed: {msg}"),
457 Self::AllocationFailed => write!(f, "MuJoCo failed to allocate the requested structure"),
458 Self::StateSliceLengthMismatch { expected, got } => {
459 write!(f, "state slice length mismatch: expected {expected}, got {got}")
460 }
461 Self::LengthMismatch { name, expected, got } => {
462 write!(f, "{name} has wrong length: expected {expected}, got {got}")
463 }
464 Self::SpecNotSubset => {
465 write!(f, "dst_spec must be a subset of src_spec")
466 }
467 Self::BufferTooSmall { needed, available } => {
468 write!(
469 f,
470 "buffer is too small: got {available} elements, \
471 but need at least {needed}"
472 )
473 }
474 Self::IncompatibleModel { .. } => write!(f, "incompatible model"),
475 Self::VfsError(_) => write!(f, "VFS operation failed"),
476 Self::IndexOutOfBounds { id, len } => write!(
477 f,
478 "index {id} is out of bounds (length is {len})"
479 ),
480 Self::IoError { path, kind } => {
481 write!(f, "file I/O failed for {}: {kind}", path.display())
482 }
483 }
484 }
485}
486
487impl std::error::Error for MjModelError {
488 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
489 match self {
490 Self::VfsError(e) => Some(e),
491 _ => None,
492 }
493 }
494}
495
496impl From<MjVfsError> for MjModelError {
497 fn from(e: MjVfsError) -> Self {
498 Self::VfsError(e)
499 }
500}
501
502
503/// Errors that can occur in virtual file system ([`MjVfs`](crate::wrappers::MjVfs)) operations.
504#[derive(Debug, Clone, PartialEq, Eq)]
505#[non_exhaustive]
506pub enum MjVfsError {
507 /// A file with the same name already exists in the VFS.
508 AlreadyExists,
509 /// MuJoCo failed to load the file or register the buffer.
510 LoadFailed,
511 /// The specified file was not found in the VFS.
512 NotFound,
513 /// The provided path contains invalid UTF-8.
514 InvalidUtf8Path,
515 /// The buffer length exceeds `i32::MAX` bytes.
516 BufferTooLarge,
517 /// An unrecognized MuJoCo return code.
518 Unknown(i32),
519}
520
521impl fmt::Display for MjVfsError {
522 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
523 match self {
524 Self::AlreadyExists => write!(f, "file already exists in VFS"),
525 Self::LoadFailed => write!(f, "failed to load file into VFS"),
526 Self::NotFound => write!(f, "file not found in VFS"),
527 Self::InvalidUtf8Path => write!(f, "path contains invalid UTF-8"),
528 Self::BufferTooLarge => write!(f, "buffer length exceeds i32::MAX bytes"),
529 Self::Unknown(code) => write!(f, "unknown VFS error (code {code})"),
530 }
531 }
532}
533
534impl std::error::Error for MjVfsError {}
535
536
537/// Errors that can occur when loading MuJoCo plugin libraries.
538#[derive(Debug, Clone, PartialEq, Eq)]
539#[non_exhaustive]
540pub enum MjPluginError {
541 /// The provided path contains invalid UTF-8.
542 InvalidUtf8Path,
543 /// The path string contains an interior null byte.
544 NullBytePath,
545}
546
547impl fmt::Display for MjPluginError {
548 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
549 match self {
550 Self::InvalidUtf8Path => write!(f, "path contains invalid UTF-8"),
551 Self::NullBytePath => write!(f, "path contains an interior null byte"),
552 }
553 }
554}
555
556impl std::error::Error for MjPluginError {}
557
558/// Errors that can occur in batched simulation ([`MjBatch`](crate::wrappers::MjBatch)) operations.
559#[derive(Debug, Clone, PartialEq, Eq)]
560#[non_exhaustive]
561pub enum MjBatchError {
562 /// MuJoCo failed to create the batch. Carries MuJoCo's error message.
563 MakeFailed(String),
564 /// The data was created from a model that is not compatible with the batch's model.
565 IncompatibleModel {
566 /// Layout of the batch's model.
567 source: Arc<MjModelLayout>,
568 /// Layout of the data's model.
569 destination: Arc<MjModelLayout>,
570 },
571 /// The name is not an [`MjData`](crate::wrappers::MjData) field that the batch can output.
572 /// Carries the name.
573 InvalidOutput(String),
574 /// The name is not an [`MjModel`](crate::wrappers::MjModel) field that the batch can expand.
575 /// Carries the name.
576 InvalidExpand(String),
577 /// The name is not an expanded [`MjModel`](crate::wrappers::MjModel) field of the batch.
578 /// Carries the name.
579 NotExpanded(String),
580 /// The requested element type does not match the elements of the field.
581 FieldTypeMismatch {
582 /// Name of the field.
583 name: String,
584 /// Size of one element of the field, in bytes.
585 element_size: usize,
586 },
587}
588
589impl fmt::Display for MjBatchError {
590 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
591 match self {
592 Self::MakeFailed(msg) => write!(f, "batch creation failed: {msg}"),
593 Self::IncompatibleModel { .. } => write!(f, "incompatible model"),
594 Self::InvalidOutput(name) => write!(f, "invalid batch output field '{name}'"),
595 Self::InvalidExpand(name) => write!(f, "invalid batch expand field '{name}'"),
596 Self::NotExpanded(name) => write!(f, "batch field '{name}' is not expanded"),
597 Self::FieldTypeMismatch { name, element_size } => write!(
598 f, "element type does not match field '{name}' ({element_size}-byte elements)"
599 ),
600 }
601 }
602}
603
604impl std::error::Error for MjBatchError {}
605
606/// Errors that can occur during OpenGL / window initialization.
607///
608/// Each variant represents a distinct step in the initialization pipeline.
609#[cfg(any(feature = "viewer", feature = "renderer-winit-fallback"))]
610#[derive(Debug, Clone)]
611#[non_exhaustive]
612pub enum GlInitError {
613 /// The windowing / display builder failed to initialize.
614 DisplayBuild(String),
615 /// No window was produced: the display builder returned no window, or the event loop never
616 /// resumed.
617 NoWindow,
618 /// Failed to obtain the native window handle.
619 WindowHandle(String),
620 /// OpenGL context creation failed. Wraps [`glutin::error::Error`].
621 ContextCreation(glutin::error::Error),
622 /// Window surface attributes could not be constructed.
623 SurfaceAttributes(String),
624 /// Rendering surface creation failed. Wraps [`glutin::error::Error`].
625 SurfaceCreation(glutin::error::Error),
626 /// Making the GL context current on the surface failed. Wraps [`glutin::error::Error`].
627 MakeCurrent(glutin::error::Error),
628}
629
630#[cfg(any(feature = "viewer", feature = "renderer-winit-fallback"))]
631impl fmt::Display for GlInitError {
632 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
633 match self {
634 Self::DisplayBuild(e) => write!(f, "display build failed: {e}"),
635 Self::NoWindow => write!(f, "display builder did not create a window"),
636 Self::WindowHandle(e) => write!(f, "failed to obtain window handle: {e}"),
637 Self::ContextCreation(_) => write!(f, "GL context creation failed"),
638 Self::SurfaceAttributes(e) => write!(f, "failed to build surface attributes: {e}"),
639 Self::SurfaceCreation(_) => write!(f, "window surface creation failed"),
640 Self::MakeCurrent(_) => write!(f, "failed to make GL context current"),
641 }
642 }
643}
644
645#[cfg(any(feature = "viewer", feature = "renderer-winit-fallback"))]
646impl std::error::Error for GlInitError {
647 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
648 match self {
649 Self::ContextCreation(e) |
650 Self::SurfaceCreation(e) |
651 Self::MakeCurrent(e) => Some(e),
652 _ => None,
653 }
654 }
655}