abi_stable/library/
errors.rs

1#![allow(clippy::missing_const_for_fn)]
2
3use super::{lib_header::AbiHeader, root_mod_trait::RootModule};
4
5use crate::{
6    sabi_types::{ParseVersionError, VersionNumber, VersionStrings},
7    std_types::{RBoxError, RResult, RVec},
8};
9
10use std::{
11    fmt::{self, Display},
12    path::PathBuf,
13};
14
15#[allow(unused_imports)]
16use core_extensions::SelfOps;
17
18/// All the possible errors that could happen when loading a library,
19/// or a module.
20#[derive(Debug)]
21pub enum LibraryError {
22    /// When a library can't be loaded, because it doesn't exist.
23    OpenError {
24        /// The path to the library
25        path: PathBuf,
26        /// The cause of the error
27        err: Box<libloading::Error>,
28    },
29    /// When a function/static does not exist.
30    GetSymbolError {
31        /// The path to the library
32        library: PathBuf,
33        /// The name of the function/static.Does not have to be utf-8.
34        symbol: Vec<u8>,
35        /// The cause of the error
36        err: Box<libloading::Error>,
37    },
38    /// The version string could not be parsed into a version number.
39    ParseVersionError(ParseVersionError),
40    /// The version numbers of the library was incompatible.
41    IncompatibleVersionNumber {
42        ///
43        library_name: &'static str,
44        ///
45        expected_version: VersionNumber,
46        ///
47        actual_version: VersionNumber,
48    },
49    /// Error returned by the root module
50    RootModule {
51        /// The error returned by the `#[export_root_module]` function.
52        err: RootModuleError,
53        ///
54        module_name: &'static str,
55        ///
56        version: VersionStrings,
57    },
58    /// The abi is incompatible.
59    /// The error is opaque,since the error always comes from the main binary
60    /// (dynamic libraries can be loaded from other dynamic libraries).
61    AbiInstability(RBoxError),
62    /// The type used to check that this is a compatible abi_stable
63    /// is not the same.
64    InvalidAbiHeader(AbiHeader),
65    /// When Rust changes how it implements the C abi,
66    InvalidCAbi {
67        ///
68        expected: RBoxError,
69        ///
70        found: RBoxError,
71    },
72    /// There could have been 0 or more errors in the function.
73    Many(RVec<Self>),
74}
75
76impl From<ParseVersionError> for LibraryError {
77    fn from(v: ParseVersionError) -> LibraryError {
78        LibraryError::ParseVersionError(v)
79    }
80}
81
82impl Display for LibraryError {
83    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
84        f.write_str("\n")?;
85        match self {
86            LibraryError::OpenError { path, err } => writeln!(
87                f,
88                "Could not open library at:\n\t{}\nbecause:\n\t{}",
89                path.display(),
90                err
91            ),
92            LibraryError::GetSymbolError {
93                library,
94                symbol,
95                err,
96            } => writeln!(
97                f,
98                "Could load symbol:\n\t{}\nin library:\n\t{}\nbecause:\n\t{}",
99                String::from_utf8_lossy(symbol),
100                library.display(),
101                err
102            ),
103            LibraryError::ParseVersionError(x) => fmt::Display::fmt(x, f),
104            LibraryError::IncompatibleVersionNumber {
105                library_name,
106                expected_version,
107                actual_version,
108            } => writeln!(
109                f,
110                "\n'{}' library version mismatch:\nuser:{}\nlibrary:{}",
111                library_name, expected_version, actual_version,
112            ),
113            LibraryError::RootModule {
114                err,
115                module_name,
116                version,
117            } => {
118                writeln!(
119                    f,
120                    "An error ocurred while loading this library:\t\n{}",
121                    module_name
122                )?;
123                writeln!(f, "version:\n\t{}", version)?;
124                f.write_str("the error:\n\n")?;
125                fmt::Display::fmt(err, f)
126            }
127            LibraryError::AbiInstability(x) => fmt::Display::fmt(x, f),
128            LibraryError::InvalidAbiHeader(found) => write!(
129                f,
130                "The abi of the library was:\n{:#?}\n\
131                 When this library expected:\n{:#?}",
132                found,
133                AbiHeader::VALUE,
134            ),
135            LibraryError::InvalidCAbi { expected, found } => {
136                write! {
137                    f,
138                    "The C abi of the library is different than expected:\n\
139                     While running tests on the library:\n\
140                         Found:\n        {found}\n\
141                         Expected:\n        {expected}\n\
142                    ",
143                    found=found,
144                    expected=expected,
145                }
146            }
147            LibraryError::Many(list) => {
148                for e in list {
149                    Display::fmt(e, f)?;
150                }
151                Ok(())
152            }
153        }?;
154        f.write_str("\n")?;
155        Ok(())
156    }
157}
158
159impl ::std::error::Error for LibraryError {}
160
161//////////////////////////////////////////////////////////////////////
162
163/// The errors that a `#[export_root_module]` function can return.
164#[repr(u8)]
165#[derive(Debug, StableAbi)]
166pub enum RootModuleError {
167    /// When the root loader function returned an error normally
168    Returned(RBoxError),
169    /// When the root loader function panicked
170    Unwound,
171}
172
173impl RootModuleError {
174    /// Reallocates the error using the current global allocator,
175    /// to ensure that there is no pointer into the dynamic library.
176    pub fn reallocate(&mut self) {
177        match self {
178            Self::Returned(e) => {
179                *e = e.to_formatted_error();
180            }
181            Self::Unwound => {}
182        }
183    }
184
185    /// Converts this `RootModuleError` into a `LibraryError`,
186    /// with metadata about the module that failed to load.
187    pub fn into_library_error<M: RootModule>(self) -> LibraryError {
188        LibraryError::RootModule {
189            err: self,
190            module_name: M::NAME,
191            version: M::VERSION_STRINGS,
192        }
193    }
194}
195
196impl Display for RootModuleError {
197    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
198        f.write_str("\n")?;
199        match self {
200            Self::Returned(e) => Display::fmt(e, f)?,
201            Self::Unwound => f.write_str("the root module loader panicked")?,
202        }
203        f.write_str("\n")?;
204        Ok(())
205    }
206}
207
208impl ::std::error::Error for RootModuleError {}
209
210//////////////////////////////////////////////////////////////////////
211
212/// For converting the return value of a `#[export_root_module]` function
213/// to a `Result<_, RootModuleError>`.
214pub trait IntoRootModuleResult {
215    /// The module that is loaded in the success case.
216    type Module: RootModule;
217
218    /// Performs the conversion
219    fn into_root_module_result(self) -> Result<Self::Module, RootModuleError>;
220}
221
222impl<M: RootModule> IntoRootModuleResult for Result<M, RBoxError> {
223    type Module = M;
224
225    fn into_root_module_result(self) -> Result<M, RootModuleError> {
226        self.map_err(RootModuleError::Returned)
227    }
228}
229
230impl<M: RootModule> IntoRootModuleResult for RResult<M, RBoxError> {
231    type Module = M;
232
233    fn into_root_module_result(self) -> Result<M, RootModuleError> {
234        self.into_result().map_err(RootModuleError::Returned)
235    }
236}
237
238impl<M: RootModule> IntoRootModuleResult for M {
239    type Module = M;
240
241    fn into_root_module_result(self) -> Result<M, RootModuleError> {
242        Ok(self)
243    }
244}
245
246#[cfg(test)]
247mod tests {
248    use super::*;
249
250    use crate::{
251        for_examples::{Module, Module_Ref},
252        prefix_type::WithMetadata,
253        std_types::{RBox, RErr, ROk, RSome},
254    };
255
256    use std::fmt::Error as FmtError;
257
258    const MOD_WM: &WithMetadata<Module> = &WithMetadata::new(Module {
259        first: RSome(5),
260        second: rstr!(""),
261        third: 13,
262    });
263
264    // `const PREFIX` can have different address every time it's used,
265    // to fix that I made it a static
266    static PREFIX: Module_Ref = Module_Ref(MOD_WM.static_as_prefix());
267
268    #[test]
269    fn into_root_module_result_test() {
270        type Res = Result<Module_Ref, RBoxError>;
271        type RRes = RResult<Module_Ref, RBoxError>;
272
273        {
274            assert_eq!(
275                PREFIX.into_root_module_result().unwrap().0.to_raw_ptr() as usize,
276                PREFIX.0.to_raw_ptr() as usize,
277            );
278        }
279
280        fn test_case(
281            ok: Result<Module_Ref, RootModuleError>,
282            err: Result<Module_Ref, RootModuleError>,
283        ) {
284            assert_eq!(
285                ok.unwrap().0.to_raw_ptr() as usize,
286                PREFIX.0.to_raw_ptr() as usize
287            );
288
289            let downcasted = match err.err().unwrap() {
290                RootModuleError::Returned(x) => x.downcast::<FmtError>().unwrap(),
291                RootModuleError::Unwound => unreachable!(),
292            };
293            assert_eq!(downcasted, RBox::new(FmtError));
294        }
295
296        // From Result
297        {
298            let ok: Res = Ok(PREFIX);
299            let err: Res = Err(RBoxError::new(FmtError));
300
301            test_case(ok.into_root_module_result(), err.into_root_module_result());
302        }
303
304        // From RResult
305        {
306            let ok: RRes = ROk(PREFIX);
307            let err: RRes = RErr(RBoxError::new(FmtError));
308
309            test_case(ok.into_root_module_result(), err.into_root_module_result());
310        }
311    }
312}