#![allow(clippy::missing_const_for_fn)]
use super::{lib_header::AbiHeader, root_mod_trait::RootModule};
use crate::{
sabi_types::{ParseVersionError, VersionNumber, VersionStrings},
std_types::{RBoxError, RResult, RVec},
};
use std::{
fmt::{self, Display},
path::PathBuf,
};
#[allow(unused_imports)]
use core_extensions::SelfOps;
#[derive(Debug)]
pub enum LibraryError {
OpenError {
path: PathBuf,
err: Box<libloading::Error>,
},
GetSymbolError {
library: PathBuf,
symbol: Vec<u8>,
err: Box<libloading::Error>,
},
ParseVersionError(ParseVersionError),
IncompatibleVersionNumber {
library_name: &'static str,
expected_version: VersionNumber,
actual_version: VersionNumber,
},
RootModule {
err: RootModuleError,
module_name: &'static str,
version: VersionStrings,
},
AbiInstability(RBoxError),
InvalidAbiHeader(AbiHeader),
InvalidCAbi {
expected: RBoxError,
found: RBoxError,
},
Many(RVec<Self>),
}
impl From<ParseVersionError> for LibraryError {
fn from(v: ParseVersionError) -> LibraryError {
LibraryError::ParseVersionError(v)
}
}
impl Display for LibraryError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("\n")?;
match self {
LibraryError::OpenError { path, err } => writeln!(
f,
"Could not open library at:\n\t{}\nbecause:\n\t{}",
path.display(),
err
),
LibraryError::GetSymbolError {
library,
symbol,
err,
} => writeln!(
f,
"Could load symbol:\n\t{}\nin library:\n\t{}\nbecause:\n\t{}",
String::from_utf8_lossy(symbol),
library.display(),
err
),
LibraryError::ParseVersionError(x) => fmt::Display::fmt(x, f),
LibraryError::IncompatibleVersionNumber {
library_name,
expected_version,
actual_version,
} => writeln!(
f,
"\n'{}' library version mismatch:\nuser:{}\nlibrary:{}",
library_name, expected_version, actual_version,
),
LibraryError::RootModule {
err,
module_name,
version,
} => {
writeln!(
f,
"An error ocurred while loading this library:\t\n{}",
module_name
)?;
writeln!(f, "version:\n\t{}", version)?;
f.write_str("the error:\n\n")?;
fmt::Display::fmt(err, f)
}
LibraryError::AbiInstability(x) => fmt::Display::fmt(x, f),
LibraryError::InvalidAbiHeader(found) => write!(
f,
"The abi of the library was:\n{:#?}\n\
When this library expected:\n{:#?}",
found,
AbiHeader::VALUE,
),
LibraryError::InvalidCAbi { expected, found } => {
write! {
f,
"The C abi of the library is different than expected:\n\
While running tests on the library:\n\
Found:\n {found}\n\
Expected:\n {expected}\n\
",
found=found,
expected=expected,
}
}
LibraryError::Many(list) => {
for e in list {
Display::fmt(e, f)?;
}
Ok(())
}
}?;
f.write_str("\n")?;
Ok(())
}
}
impl ::std::error::Error for LibraryError {}
#[repr(u8)]
#[derive(Debug, StableAbi)]
pub enum RootModuleError {
Returned(RBoxError),
Unwound,
}
impl RootModuleError {
pub fn reallocate(&mut self) {
match self {
Self::Returned(e) => {
*e = e.to_formatted_error();
}
Self::Unwound => {}
}
}
pub fn into_library_error<M: RootModule>(self) -> LibraryError {
LibraryError::RootModule {
err: self,
module_name: M::NAME,
version: M::VERSION_STRINGS,
}
}
}
impl Display for RootModuleError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("\n")?;
match self {
Self::Returned(e) => Display::fmt(e, f)?,
Self::Unwound => f.write_str("the root module loader panicked")?,
}
f.write_str("\n")?;
Ok(())
}
}
impl ::std::error::Error for RootModuleError {}
pub trait IntoRootModuleResult {
type Module: RootModule;
fn into_root_module_result(self) -> Result<Self::Module, RootModuleError>;
}
impl<M: RootModule> IntoRootModuleResult for Result<M, RBoxError> {
type Module = M;
fn into_root_module_result(self) -> Result<M, RootModuleError> {
self.map_err(RootModuleError::Returned)
}
}
impl<M: RootModule> IntoRootModuleResult for RResult<M, RBoxError> {
type Module = M;
fn into_root_module_result(self) -> Result<M, RootModuleError> {
self.into_result().map_err(RootModuleError::Returned)
}
}
impl<M: RootModule> IntoRootModuleResult for M {
type Module = M;
fn into_root_module_result(self) -> Result<M, RootModuleError> {
Ok(self)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
for_examples::{Module, Module_Ref},
prefix_type::WithMetadata,
std_types::{RBox, RErr, ROk, RSome},
};
use std::fmt::Error as FmtError;
const MOD_WM: &WithMetadata<Module> = &WithMetadata::new(Module {
first: RSome(5),
second: rstr!(""),
third: 13,
});
static PREFIX: Module_Ref = Module_Ref(MOD_WM.static_as_prefix());
#[test]
fn into_root_module_result_test() {
type Res = Result<Module_Ref, RBoxError>;
type RRes = RResult<Module_Ref, RBoxError>;
{
assert_eq!(
PREFIX.into_root_module_result().unwrap().0.to_raw_ptr() as usize,
PREFIX.0.to_raw_ptr() as usize,
);
}
fn test_case(
ok: Result<Module_Ref, RootModuleError>,
err: Result<Module_Ref, RootModuleError>,
) {
assert_eq!(
ok.unwrap().0.to_raw_ptr() as usize,
PREFIX.0.to_raw_ptr() as usize
);
let downcasted = match err.err().unwrap() {
RootModuleError::Returned(x) => x.downcast::<FmtError>().unwrap(),
RootModuleError::Unwound => unreachable!(),
};
assert_eq!(downcasted, RBox::new(FmtError));
}
{
let ok: Res = Ok(PREFIX);
let err: Res = Err(RBoxError::new(FmtError));
test_case(ok.into_root_module_result(), err.into_root_module_result());
}
{
let ok: RRes = ROk(PREFIX);
let err: RRes = RErr(RBoxError::new(FmtError));
test_case(ok.into_root_module_result(), err.into_root_module_result());
}
}
}