Skip to main content

mpv_engine/
error.rs

1use thiserror::Error;
2
3/// Everything this crate's fallible calls can return.
4#[derive(Debug, Error)]
5#[non_exhaustive]
6pub enum Error {
7    /// An error from libmpv itself, via rsmpv (its `Display` carries
8    /// mpv's `mpv_error_string` text plus the numeric code).
9    #[error("mpv: {0}")]
10    Mpv(#[from] rsmpv::Error),
11    /// An attach called while a render context is already live. mpv
12    /// supports exactly one render context per handle, of either backend.
13    #[error("a render context is already attached")]
14    AlreadyAttached,
15    /// A render call against the wrong attached backend (e.g. `render_gl`
16    /// while the software context is attached) — a wiring bug in the
17    /// shell, surfaced loudly rather than silently dropped.
18    #[error("render call does not match the attached render backend")]
19    RenderBackendMismatch,
20    /// Setting up the exported-frame backend's hidden GL context or its
21    /// exportable framebuffers (IOSurface / DMA-BUF / shared D3D11
22    /// texture) failed (`export` feature,
23    /// [`Engine::attach_exported_render`](crate::Engine::attach_exported_render)).
24    /// The common non-bug cause is a session without GPU access —
25    /// no WindowServer on macOS, no readable DRM render node on Linux
26    /// (SSH, bare CI, missing render/video group), no OpenGL ICD or no
27    /// `WGL_NV_DX_interop2` on Windows — treat it like a missing display
28    /// and fall back to another backend or skip.
29    #[cfg(all(
30        feature = "export",
31        any(target_os = "macos", target_os = "linux", target_os = "windows")
32    ))]
33    #[error("exported render setup: {0}")]
34    ExportSetup(String),
35    /// Importing an exported frame into a wgpu device failed (`wgpu`
36    /// feature,
37    /// [`ExportedFrame::into_wgpu_texture`](crate::ExportedFrame::into_wgpu_texture))
38    /// — the device isn't on the platform's native backend (Metal on
39    /// macOS, Vulkan on Linux, DX12 on Windows), lacks a required wgpu
40    /// feature (`VULKAN_EXTERNAL_MEMORY_DMA_BUF` on Linux), sits on a
41    /// different GPU than the engine's hidden context (Windows), or the
42    /// driver refused the IOSurface wrap / DMA-BUF import / shared-handle
43    /// open.
44    #[cfg(all(
45        feature = "wgpu",
46        any(target_os = "macos", target_os = "linux", target_os = "windows")
47    ))]
48    #[error("wgpu import: {0}")]
49    WgpuImport(String),
50    /// A call that needs a live render context ran before any attach.
51    /// Today only
52    /// [`Engine::set_render_update_callback`](crate::Engine::set_render_update_callback)
53    /// returns this — a callback that could never fire is a wiring bug,
54    /// surfaced loudly like
55    /// [`RenderBackendMismatch`](Self::RenderBackendMismatch). The frame
56    /// path deliberately does *not* use it: unattached,
57    /// `render_gl`/`render_sw` return `Ok` untouched and `render_update`
58    /// returns `false`, because "not attached yet" is an ordinary
59    /// startup state there, not a bug — don't match on this variant to
60    /// detect a missing attach from a draw handler.
61    #[error("no render context is attached")]
62    NotAttached,
63}
64
65/// Shorthand for results carrying this crate's [`enum@Error`].
66pub type Result<T> = std::result::Result<T, Error>;
67
68/// Diagnostic text for a raw `client.h` `mpv_error` code: mpv's own
69/// `mpv_error_string` text plus the numeric code (rsmpv's `Display`
70/// carries both) — not user-facing copy.
71pub(crate) fn describe_code(code: i32) -> String {
72    rsmpv::Error::from_raw(code).to_string()
73}
74
75#[cfg(test)]
76mod tests {
77    use super::*;
78
79    #[test]
80    fn describe_code_carries_mpv_text_and_code() {
81        // -17 is MPV_ERROR_UNKNOWN_FORMAT; the exact wording belongs to
82        // mpv, so pin only that its text came through alongside the code.
83        let msg = describe_code(-17);
84        assert!(msg.contains("format"), "unexpected message: {msg}");
85        assert!(msg.contains("-17"), "unexpected message: {msg}");
86        // Unknown codes still produce a code-bearing string.
87        assert!(describe_code(-99).contains("-99"));
88    }
89}