mirror of
https://github.com/loot/libloot.git
synced 2026-07-27 14:16:01 -07:00
Improve Python wrapper error handling
The new libloot-ffi-errors crate is for sharing code between the CXX and PyO3 wrappers.
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
[package]
|
||||
name = "libloot-ffi-errors"
|
||||
version = "0.25.5"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
libloot = { path = ".." }
|
||||
|
||||
esplugin.workspace = true
|
||||
libloadorder.workspace = true
|
||||
loot-condition-interpreter.workspace = true
|
||||
@@ -0,0 +1,65 @@
|
||||
use std::ffi::c_int;
|
||||
|
||||
use esplugin::Error;
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub static ESP_ERROR_NULL_POINTER: c_int = 1;
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub static ESP_ERROR_NOT_UTF8: c_int = 2;
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub static ESP_ERROR_STRING_CONTAINS_NUL: c_int = 3;
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub static ESP_ERROR_INVALID_GAME_ID: c_int = 4;
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub static ESP_ERROR_PARSE_ERROR: c_int = 5;
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub static ESP_ERROR_PANICKED: c_int = 6;
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub static ESP_ERROR_NO_FILENAME: c_int = 7;
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub static ESP_ERROR_TEXT_DECODE_ERROR: c_int = 8;
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub static ESP_ERROR_TEXT_ENCODE_ERROR: c_int = 9;
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub static ESP_ERROR_IO_ERROR: c_int = 10;
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub static ESP_ERROR_FILE_NOT_FOUND: c_int = 11;
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub static ESP_ERROR_IO_PERMISSION_DENIED: c_int = 12;
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub static ESP_ERROR_UNRESOLVED_RECORD_IDS: c_int = 13;
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub static ESP_ERROR_PLUGIN_METADATA_NOT_FOUND: c_int = 14;
|
||||
|
||||
fn map_io_error(err: &std::io::Error) -> c_int {
|
||||
match err.kind() {
|
||||
std::io::ErrorKind::NotFound => ESP_ERROR_FILE_NOT_FOUND,
|
||||
std::io::ErrorKind::PermissionDenied => ESP_ERROR_IO_PERMISSION_DENIED,
|
||||
_ => ESP_ERROR_IO_ERROR,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_error(err: &Error) -> c_int {
|
||||
match *err {
|
||||
Error::IoError(ref x) => map_io_error(x),
|
||||
Error::NoFilename(_) => ESP_ERROR_NO_FILENAME,
|
||||
Error::ParsingIncomplete(_) => ESP_ERROR_PARSE_ERROR,
|
||||
Error::ParsingError(_, _) => ESP_ERROR_PARSE_ERROR,
|
||||
Error::DecodeError(_) => ESP_ERROR_TEXT_DECODE_ERROR,
|
||||
Error::UnresolvedRecordIds(_) => ESP_ERROR_UNRESOLVED_RECORD_IDS,
|
||||
Error::PluginMetadataNotFound(_) => ESP_ERROR_PLUGIN_METADATA_NOT_FOUND,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
use loot_condition_interpreter::Error;
|
||||
use std::ffi::c_int;
|
||||
|
||||
/// Invalid arguments were given for the function.
|
||||
#[unsafe(no_mangle)]
|
||||
pub static LCI_ERROR_INVALID_ARGS: c_int = -1;
|
||||
|
||||
/// Something went wrong while parsing the condition expression.
|
||||
#[unsafe(no_mangle)]
|
||||
pub static LCI_ERROR_PARSING_ERROR: c_int = -2;
|
||||
|
||||
/// Something went wrong while getting the version of an executable.
|
||||
#[unsafe(no_mangle)]
|
||||
pub static LCI_ERROR_PE_PARSING_ERROR: c_int = -3;
|
||||
|
||||
/// Some sort of I/O error occurred.
|
||||
#[unsafe(no_mangle)]
|
||||
pub static LCI_ERROR_IO_ERROR: c_int = -4;
|
||||
|
||||
/// Something panicked.
|
||||
#[unsafe(no_mangle)]
|
||||
pub static LCI_ERROR_PANICKED: c_int = -5;
|
||||
|
||||
/// A thread lock was poisoned.
|
||||
#[unsafe(no_mangle)]
|
||||
pub static LCI_ERROR_POISONED_THREAD_LOCK: c_int = -6;
|
||||
|
||||
/// Failed to encode string as a C string, e.g. because there was a nul present.
|
||||
#[unsafe(no_mangle)]
|
||||
pub static LCI_ERROR_TEXT_ENCODE_FAIL: c_int = -7;
|
||||
|
||||
/// The library encountered an error that should not have been possible to encounter.
|
||||
#[unsafe(no_mangle)]
|
||||
pub static LCI_ERROR_INTERNAL_LOGIC_ERROR: c_int = -8;
|
||||
|
||||
pub fn map_error(err: &Error) -> c_int {
|
||||
match err {
|
||||
Error::ParsingIncomplete(_) => LCI_ERROR_PARSING_ERROR,
|
||||
Error::UnconsumedInput(_) => LCI_ERROR_PARSING_ERROR,
|
||||
Error::ParsingError(_, _) => LCI_ERROR_PARSING_ERROR,
|
||||
Error::PeParsingError(_, _) => LCI_ERROR_PE_PARSING_ERROR,
|
||||
Error::IoError(_, _) => LCI_ERROR_IO_ERROR,
|
||||
_ => LCI_ERROR_INTERNAL_LOGIC_ERROR,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
use std::{
|
||||
error::Error,
|
||||
ffi::{c_int, c_uchar},
|
||||
};
|
||||
|
||||
use libloot::error::{ConditionEvaluationError, LoadOrderError, PluginDataError};
|
||||
|
||||
pub mod esplugin;
|
||||
pub mod lci;
|
||||
pub mod libloadorder;
|
||||
|
||||
/// An error from esplugin.
|
||||
#[unsafe(no_mangle)]
|
||||
pub static LIBLOOT_SYSTEM_ERROR_CATEGORY_ESPLUGIN: c_uchar = 1;
|
||||
|
||||
/// An error from libloadorder.
|
||||
#[unsafe(no_mangle)]
|
||||
pub static LIBLOOT_SYSTEM_ERROR_CATEGORY_LIBLOADORDER: c_uchar = 2;
|
||||
|
||||
/// An error from loot-condition-interpreter.
|
||||
#[unsafe(no_mangle)]
|
||||
pub static LIBLOOT_SYSTEM_ERROR_CATEGORY_LCI: c_uchar = 3;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
#[repr(u8)]
|
||||
pub enum SystemErrorCategory {
|
||||
Esplugin = LIBLOOT_SYSTEM_ERROR_CATEGORY_ESPLUGIN,
|
||||
Libloadorder = LIBLOOT_SYSTEM_ERROR_CATEGORY_LIBLOADORDER,
|
||||
LootConditionInterpreter = LIBLOOT_SYSTEM_ERROR_CATEGORY_LCI,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
pub struct SystemError {
|
||||
code: c_int,
|
||||
category: SystemErrorCategory,
|
||||
message: String,
|
||||
}
|
||||
|
||||
impl SystemError {
|
||||
pub fn code(&self) -> c_int {
|
||||
self.code
|
||||
}
|
||||
|
||||
pub fn category(&self) -> SystemErrorCategory {
|
||||
self.category
|
||||
}
|
||||
|
||||
pub fn message(&self) -> &str {
|
||||
&self.message
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PluginDataError> for SystemError {
|
||||
fn from(value: PluginDataError) -> Self {
|
||||
let error = value
|
||||
.source()
|
||||
.expect("LoadOrderError has source")
|
||||
.downcast_ref::<::esplugin::Error>()
|
||||
.expect("LoadOrderError source is an esplugin::Error");
|
||||
let error_code = crate::esplugin::map_error(error);
|
||||
SystemError {
|
||||
code: error_code,
|
||||
category: SystemErrorCategory::Esplugin,
|
||||
message: error.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LoadOrderError> for SystemError {
|
||||
fn from(value: LoadOrderError) -> Self {
|
||||
let error = value
|
||||
.source()
|
||||
.expect("LoadOrderError has source")
|
||||
.downcast_ref::<loadorder::Error>()
|
||||
.expect("LoadOrderError source is a loadorder::Error");
|
||||
let error_code = crate::libloadorder::map_error(error);
|
||||
SystemError {
|
||||
code: error_code,
|
||||
category: SystemErrorCategory::Libloadorder,
|
||||
message: error.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ConditionEvaluationError> for SystemError {
|
||||
fn from(value: ConditionEvaluationError) -> Self {
|
||||
let error = value
|
||||
.source()
|
||||
.expect("ConditionEvaluationError has source")
|
||||
.downcast_ref::<loot_condition_interpreter::Error>()
|
||||
.expect("ConditionEvaluationError source is a loot_condition_interpreter::Error");
|
||||
let error_code = crate::lci::map_error(error);
|
||||
SystemError {
|
||||
code: error_code,
|
||||
category: SystemErrorCategory::LootConditionInterpreter,
|
||||
message: error.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
use loadorder::Error;
|
||||
use std::ffi::c_int;
|
||||
|
||||
/// There is a mismatch between the files used to keep track of load order.
|
||||
///
|
||||
/// This warning can only occur when using libloadorder with a game that uses the textfile-based
|
||||
/// load order system. The load order in the active plugins list file (`plugins.txt`) does not
|
||||
/// match the load order in the full load order file (`loadorder.txt`). Synchronisation between
|
||||
/// the two is automatic when load order is managed through libloadorder. It is left to the client
|
||||
/// to decide how best to restore synchronisation.
|
||||
#[unsafe(no_mangle)]
|
||||
pub static LIBLO_WARN_LO_MISMATCH: c_int = 2;
|
||||
|
||||
/// The specified file could not be found.
|
||||
#[unsafe(no_mangle)]
|
||||
pub static LIBLO_ERROR_FILE_NOT_FOUND: c_int = 6;
|
||||
|
||||
/// A file could not be renamed.
|
||||
#[unsafe(no_mangle)]
|
||||
pub static LIBLO_ERROR_FILE_RENAME_FAIL: c_int = 7;
|
||||
|
||||
/// There was an error parsing a plugin file.
|
||||
#[unsafe(no_mangle)]
|
||||
pub static LIBLO_ERROR_FILE_PARSE_FAIL: c_int = 10;
|
||||
|
||||
/// Invalid arguments were given for the function.
|
||||
#[unsafe(no_mangle)]
|
||||
pub static LIBLO_ERROR_INVALID_ARGS: c_int = 12;
|
||||
|
||||
/// A thread lock was poisoned.
|
||||
#[unsafe(no_mangle)]
|
||||
pub static LIBLO_ERROR_POISONED_THREAD_LOCK: c_int = 14;
|
||||
|
||||
/// An unknown I/O error occurred. This is used when the I/O error kind doesn't fit another error
|
||||
/// code.
|
||||
#[unsafe(no_mangle)]
|
||||
pub static LIBLO_ERROR_IO_ERROR: c_int = 15;
|
||||
|
||||
/// Permission denied while trying to access a filesystem path.
|
||||
#[unsafe(no_mangle)]
|
||||
pub static LIBLO_ERROR_IO_PERMISSION_DENIED: c_int = 16;
|
||||
|
||||
/// A plugin filename contains characters that do not have Windows-1252 code points, or a character
|
||||
/// string contains a null character.
|
||||
#[unsafe(no_mangle)]
|
||||
pub static LIBLO_ERROR_TEXT_ENCODE_FAIL: c_int = 17;
|
||||
|
||||
/// Text expected to be encoded in Windows-1252 could not be decoded to UTF-8.
|
||||
#[unsafe(no_mangle)]
|
||||
pub static LIBLO_ERROR_TEXT_DECODE_FAIL: c_int = 18;
|
||||
|
||||
/// The library encountered an error that should not have been possible to encounter.
|
||||
#[unsafe(no_mangle)]
|
||||
pub static LIBLO_ERROR_INTERNAL_LOGIC_ERROR: c_int = 19;
|
||||
|
||||
/// Something panicked.
|
||||
#[unsafe(no_mangle)]
|
||||
pub static LIBLO_ERROR_PANICKED: c_int = 20;
|
||||
|
||||
/// A path cannot be encoded in UTF-8.
|
||||
#[unsafe(no_mangle)]
|
||||
pub static LIBLO_ERROR_PATH_ENCODE_FAIL: c_int = 21;
|
||||
|
||||
/// An unknown operating system error occurred.
|
||||
#[unsafe(no_mangle)]
|
||||
pub static LIBLO_ERROR_SYSTEM_ERROR: c_int = 22;
|
||||
|
||||
/// A system path definition (e.g. for local app data on Windows, or $HOME on Linux) could not be
|
||||
/// found.
|
||||
#[unsafe(no_mangle)]
|
||||
pub static LIBLO_ERROR_NO_PATH: c_int = 23;
|
||||
|
||||
/// Matches the value of the highest-numbered return code.
|
||||
///
|
||||
/// Provided in case clients wish to incorporate additional return codes in their implementation
|
||||
/// and desire some method of avoiding value conflicts.
|
||||
#[unsafe(no_mangle)]
|
||||
pub static LIBLO_RETURN_MAX: c_int = 23;
|
||||
|
||||
fn map_io_error(err: &std::io::Error) -> c_int {
|
||||
use std::io::ErrorKind::*;
|
||||
match err.kind() {
|
||||
NotFound => LIBLO_ERROR_FILE_NOT_FOUND,
|
||||
AlreadyExists => LIBLO_ERROR_FILE_RENAME_FAIL,
|
||||
PermissionDenied => LIBLO_ERROR_IO_PERMISSION_DENIED,
|
||||
_ => LIBLO_ERROR_IO_ERROR,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_error(err: &Error) -> c_int {
|
||||
use Error::*;
|
||||
match *err {
|
||||
InvalidPath(_) => LIBLO_ERROR_FILE_NOT_FOUND,
|
||||
IoError(_, ref x) => map_io_error(x),
|
||||
NoFilename(_) => LIBLO_ERROR_FILE_PARSE_FAIL,
|
||||
DecodeError(_) => LIBLO_ERROR_TEXT_DECODE_FAIL,
|
||||
EncodeError(_) => LIBLO_ERROR_TEXT_ENCODE_FAIL,
|
||||
PluginParsingError(_, _) => LIBLO_ERROR_FILE_PARSE_FAIL,
|
||||
PluginNotFound(_) => LIBLO_ERROR_INVALID_ARGS,
|
||||
TooManyActivePlugins { .. } => LIBLO_ERROR_INVALID_ARGS,
|
||||
DuplicatePlugin(_) => LIBLO_ERROR_INVALID_ARGS,
|
||||
NonMasterBeforeMaster { .. } => LIBLO_ERROR_INVALID_ARGS,
|
||||
InvalidEarlyLoadingPluginPosition { .. } => LIBLO_ERROR_INVALID_ARGS,
|
||||
ImplicitlyActivePlugin(_) => LIBLO_ERROR_INVALID_ARGS,
|
||||
NoLocalAppData => LIBLO_ERROR_INVALID_ARGS,
|
||||
NoDocumentsPath => LIBLO_ERROR_INVALID_ARGS,
|
||||
NoUserConfigPath => LIBLO_ERROR_NO_PATH,
|
||||
NoUserDataPath => LIBLO_ERROR_NO_PATH,
|
||||
NoProgramFilesPath => LIBLO_ERROR_NO_PATH,
|
||||
UnrepresentedHoist { .. } => LIBLO_ERROR_INVALID_ARGS,
|
||||
InstalledPlugin(_) => LIBLO_ERROR_INVALID_ARGS,
|
||||
IniParsingError { .. } => LIBLO_ERROR_FILE_PARSE_FAIL,
|
||||
VdfParsingError(_, _) => LIBLO_ERROR_FILE_PARSE_FAIL,
|
||||
SystemError(_, _) => LIBLO_ERROR_SYSTEM_ERROR,
|
||||
InvalidBlueprintPluginPosition { .. } => LIBLO_ERROR_INVALID_ARGS,
|
||||
_ => LIBLO_ERROR_INTERNAL_LOGIC_ERROR,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user