librashader_capi/
error.rs

1//! librashader error C API. (`libra_error_*`).
2use std::any::Any;
3use std::ffi::{c_char, CString};
4use std::mem::MaybeUninit;
5use std::ptr::NonNull;
6use thiserror::Error;
7
8/// The error type for librashader C API.
9#[non_exhaustive]
10#[derive(Error, Debug)]
11pub enum LibrashaderError {
12    /// An unknown error or panic occurred.
13    #[error("There was an unknown error.")]
14    UnknownError(Box<dyn Any + Send + 'static>),
15
16    /// An invalid parameter (likely null), was passed.
17    #[error("The parameter was null or invalid.")]
18    InvalidParameter(&'static str),
19
20    /// The string provided was not valid UTF-8.
21    #[error("The provided string was not valid UTF8.")]
22    InvalidString(#[from] std::str::Utf8Error),
23
24    /// An error occurred in the preset parser.
25    #[error("There was an error parsing the preset.")]
26    PresetError(#[from] librashader::presets::ParsePresetError),
27
28    /// An error occurred in the shader preprocessor.
29    #[error("There was an error preprocessing the shader source.")]
30    PreprocessError(#[from] librashader::preprocess::PreprocessError),
31
32    /// An error occurred in the shader compiler.
33    #[error("There was an error compiling the shader source.")]
34    ShaderCompileError(#[from] librashader::reflect::ShaderCompileError),
35
36    /// An error occrred when validating and reflecting the shader.
37    #[error("There was an error reflecting the shader source.")]
38    ShaderReflectError(#[from] librashader::reflect::ShaderReflectError),
39
40    /// An invalid shader parameter name was provided.
41    #[error("The provided parameter name was invalid.")]
42    UnknownShaderParameter(*const c_char),
43
44    /// An error occurred with the OpenGL filter chain.
45    #[cfg(feature = "runtime-opengl")]
46    #[cfg_attr(feature = "docsrs", doc(cfg(feature = "runtime-opengl")))]
47    #[error("There was an error in the OpenGL filter chain.")]
48    OpenGlFilterError(#[from] librashader::runtime::gl::error::FilterChainError),
49
50    /// An error occurred with the Direct3D 11 filter chain.
51    #[cfg(all(target_os = "windows", feature = "runtime-d3d11"))]
52    #[cfg_attr(
53        feature = "docsrs",
54        doc(cfg(all(target_os = "windows", feature = "runtime-d3d11")))
55    )]
56    #[error("There was an error in the D3D11 filter chain.")]
57    D3D11FilterError(#[from] librashader::runtime::d3d11::error::FilterChainError),
58
59    /// An error occurred with the Direct3D 12 filter chain.
60    #[cfg(all(target_os = "windows", feature = "runtime-d3d12"))]
61    #[cfg_attr(
62        feature = "docsrs",
63        doc(cfg(all(target_os = "windows", feature = "runtime-d3d12")))
64    )]
65    #[error("There was an error in the D3D12 filter chain.")]
66    D3D12FilterError(#[from] librashader::runtime::d3d12::error::FilterChainError),
67
68    /// An error occurred with the Direct3D 9 filter chain.
69    #[cfg(all(target_os = "windows", feature = "runtime-d3d9"))]
70    #[cfg_attr(
71        feature = "docsrs",
72        doc(cfg(all(target_os = "windows", feature = "runtime-d3d9")))
73    )]
74    #[error("There was an error in the D3D9 filter chain.")]
75    D3D9FilterError(#[from] librashader::runtime::d3d9::error::FilterChainError),
76
77    /// An error occurred with the Vulkan filter chain.
78
79    #[cfg(feature = "runtime-vulkan")]
80    #[cfg_attr(feature = "docsrs", doc(cfg(feature = "runtime-vulkan")))]
81    #[error("There was an error in the Vulkan filter chain.")]
82    VulkanFilterError(#[from] librashader::runtime::vk::error::FilterChainError),
83
84    /// An error occurred with the Metal filter chain.
85    #[cfg_attr(
86        feature = "docsrs",
87        doc(cfg(all(target_vendor = "apple", feature = "runtime-metal")))
88    )]
89    #[cfg(all(target_vendor = "apple", feature = "runtime-metal"))]
90    #[error("There was an error in the Metal filter chain.")]
91    MetalFilterError(#[from] librashader::runtime::mtl::error::FilterChainError),
92    /// This error is unreachable.
93    #[error("This error is not reachable")]
94    Infallible(#[from] std::convert::Infallible),
95}
96
97/// Error codes for librashader error types.
98#[repr(i32)]
99pub enum LIBRA_ERRNO {
100    /// Error code for an unknown error.
101    UNKNOWN_ERROR = 0,
102
103    /// Error code for an invalid parameter.
104    INVALID_PARAMETER = 1,
105
106    /// Error code for an invalid (non-UTF8) string.
107    INVALID_STRING = 2,
108
109    /// Error code for a preset parser error.
110    PRESET_ERROR = 3,
111
112    /// Error code for a preprocessor error.
113    PREPROCESS_ERROR = 4,
114
115    /// Error code for a shader parameter error.
116    SHADER_PARAMETER_ERROR = 5,
117
118    /// Error code for a reflection error.
119    REFLECT_ERROR = 6,
120
121    /// Error code for a runtime error.
122    RUNTIME_ERROR = 7,
123}
124
125// Nothing here can use extern_fn because they are lower level than libra_error_t.
126
127/// Function pointer definition for libra_error_errno
128pub type PFN_libra_error_errno = extern "C" fn(error: libra_error_t) -> LIBRA_ERRNO;
129#[no_mangle]
130/// Get the error code corresponding to this error object.
131///
132/// ## Safety
133///   - `error` must be valid and initialized.
134pub unsafe extern "C" fn libra_error_errno(error: libra_error_t) -> LIBRA_ERRNO {
135    let Some(error) = error else {
136        return LIBRA_ERRNO::UNKNOWN_ERROR;
137    };
138
139    unsafe { error.as_ref().get_code() }
140}
141
142/// Function pointer definition for libra_error_print
143pub type PFN_libra_error_print = extern "C" fn(error: libra_error_t) -> i32;
144#[no_mangle]
145/// Print the error message.
146///
147/// If `error` is null, this function does nothing and returns 1. Otherwise, this function returns 0.
148/// ## Safety
149///   - `error` must be a valid and initialized instance of `libra_error_t`.
150pub unsafe extern "C" fn libra_error_print(error: libra_error_t) -> i32 {
151    let Some(error) = error else { return 1 };
152    unsafe {
153        let error = error.as_ref();
154        println!("{error:?}: {error}");
155    }
156    0
157}
158
159/// Function pointer definition for libra_error_free
160pub type PFN_libra_error_free = extern "C" fn(error: *mut libra_error_t) -> i32;
161#[no_mangle]
162/// Frees any internal state kept by the error.
163///
164/// If `error` is null, this function does nothing and returns 1. Otherwise, this function returns 0.
165/// The resulting error object becomes null.
166/// ## Safety
167///   - `error` must be null or a pointer to a valid and initialized instance of `libra_error_t`.
168pub unsafe extern "C" fn libra_error_free(error: *mut libra_error_t) -> i32 {
169    if error.is_null() {
170        return 1;
171    }
172
173    let error = unsafe { &mut *error };
174    let error = error.take();
175    let Some(error) = error else {
176        return 1;
177    };
178
179    unsafe { drop(Box::from_raw(error.as_ptr())) }
180    0
181}
182
183/// Function pointer definition for libra_error_write
184pub type PFN_libra_error_write =
185    extern "C" fn(error: libra_error_t, out: *mut MaybeUninit<*mut c_char>) -> i32;
186#[no_mangle]
187/// Writes the error message into `out`
188///
189/// If `error` is null, this function does nothing and returns 1. Otherwise, this function returns 0.
190/// ## Safety
191///   - `error` must be a valid and initialized instance of `libra_error_t`.
192///   - `out` must be a non-null pointer. The resulting string must not be modified.
193pub unsafe extern "C" fn libra_error_write(
194    error: libra_error_t,
195    out: *mut MaybeUninit<*mut c_char>,
196) -> i32 {
197    let Some(error) = error else { return 1 };
198    if out.is_null() {
199        return 1;
200    }
201
202    unsafe {
203        let error = error.as_ref();
204        let Ok(cstring) = CString::new(format!("{error:?}: {error}")) else {
205            return 1;
206        };
207
208        out.write(MaybeUninit::new(cstring.into_raw()))
209    }
210    0
211}
212
213/// Function pointer definition for libra_error_free_string
214pub type PFN_libra_error_free_string = extern "C" fn(out: *mut *mut c_char) -> i32;
215#[no_mangle]
216/// Frees an error string previously allocated by `libra_error_write`.
217///
218/// After freeing, the pointer will be set to null.
219/// ## Safety
220///   - If `libra_error_write` is not null, it must point to a string previously returned by `libra_error_write`.
221///     Attempting to free anything else, including strings or objects from other librashader functions, is immediate
222///     Undefined Behaviour.
223pub unsafe extern "C" fn libra_error_free_string(out: *mut *mut c_char) -> i32 {
224    if out.is_null() {
225        return 1;
226    }
227
228    unsafe {
229        let ptr = out.read();
230        *out = std::ptr::null_mut();
231        drop(CString::from_raw(ptr))
232    }
233    0
234}
235
236impl LibrashaderError {
237    pub(crate) const fn get_code(&self) -> LIBRA_ERRNO {
238        match self {
239            LibrashaderError::UnknownError(_) => LIBRA_ERRNO::UNKNOWN_ERROR,
240            LibrashaderError::InvalidParameter(_) => LIBRA_ERRNO::INVALID_PARAMETER,
241            LibrashaderError::InvalidString(_) => LIBRA_ERRNO::INVALID_STRING,
242            LibrashaderError::PresetError(_) => LIBRA_ERRNO::PRESET_ERROR,
243            LibrashaderError::PreprocessError(_) => LIBRA_ERRNO::PREPROCESS_ERROR,
244            LibrashaderError::ShaderCompileError(_) | LibrashaderError::ShaderReflectError(_) => {
245                LIBRA_ERRNO::REFLECT_ERROR
246            }
247            LibrashaderError::UnknownShaderParameter(_) => LIBRA_ERRNO::SHADER_PARAMETER_ERROR,
248            #[cfg(feature = "runtime-opengl")]
249            LibrashaderError::OpenGlFilterError(_) => LIBRA_ERRNO::RUNTIME_ERROR,
250            #[cfg(all(target_os = "windows", feature = "runtime-d3d11"))]
251            LibrashaderError::D3D11FilterError(_) => LIBRA_ERRNO::RUNTIME_ERROR,
252            #[cfg(all(target_os = "windows", feature = "runtime-d3d12"))]
253            LibrashaderError::D3D12FilterError(_) => LIBRA_ERRNO::RUNTIME_ERROR,
254            #[cfg(all(target_os = "windows", feature = "runtime-d3d9"))]
255            LibrashaderError::D3D9FilterError(_) => LIBRA_ERRNO::RUNTIME_ERROR,
256            #[cfg(feature = "runtime-vulkan")]
257            LibrashaderError::VulkanFilterError(_) => LIBRA_ERRNO::RUNTIME_ERROR,
258            #[cfg(all(target_vendor = "apple", feature = "runtime-metal"))]
259            LibrashaderError::MetalFilterError(_) => LIBRA_ERRNO::RUNTIME_ERROR,
260            LibrashaderError::Infallible(_) => LIBRA_ERRNO::UNKNOWN_ERROR,
261        }
262    }
263    pub(crate) const fn ok() -> libra_error_t {
264        None
265    }
266
267    pub(crate) fn export(self) -> libra_error_t {
268        NonNull::new(Box::into_raw(Box::new(self)))
269    }
270}
271
272macro_rules! assert_non_null {
273    (@EXPORT $value:ident) => {
274        if $value.is_null() || !$crate::ffi::ptr_is_aligned($value) {
275            return $crate::error::LibrashaderError::InvalidParameter(stringify!($value)).export();
276        }
277    };
278    ($value:ident) => {
279        if $value.is_null() || !$crate::ffi::ptr_is_aligned($value) {
280            return Err($crate::error::LibrashaderError::InvalidParameter(
281                stringify!($value),
282            ));
283        }
284    };
285}
286
287macro_rules! assert_some_ptr {
288    ($value:ident) => {
289        if $value.is_none() {
290            return Err($crate::error::LibrashaderError::InvalidParameter(
291                stringify!($value),
292            ));
293        }
294
295        let $value = unsafe { $value.as_ref().unwrap_unchecked().as_ref() };
296    };
297    (mut $value:ident) => {
298        if $value.is_none() {
299            return Err($crate::error::LibrashaderError::InvalidParameter(
300                stringify!($value),
301            ));
302        }
303
304        let $value = unsafe { $value.as_mut().unwrap_unchecked().as_mut() };
305    };
306}
307
308use crate::ctypes::libra_error_t;
309pub(crate) use assert_non_null;
310
311// pub(crate) use assert_some;
312pub(crate) use assert_some_ptr;