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:
Generated
+10
-1
@@ -600,11 +600,19 @@ dependencies = [
|
||||
"cxx",
|
||||
"cxx-build",
|
||||
"delegate",
|
||||
"libloot",
|
||||
"libloot-ffi-errors",
|
||||
"unicase",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libloot-ffi-errors"
|
||||
version = "0.25.5"
|
||||
dependencies = [
|
||||
"esplugin",
|
||||
"libloadorder",
|
||||
"libloot",
|
||||
"loot-condition-interpreter",
|
||||
"unicase",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -612,6 +620,7 @@ name = "libloot_pyo3"
|
||||
version = "0.25.5"
|
||||
dependencies = [
|
||||
"libloot",
|
||||
"libloot-ffi-errors",
|
||||
"pyo3",
|
||||
]
|
||||
|
||||
|
||||
+9
-4
@@ -6,10 +6,10 @@ license = "GPL-3.0"
|
||||
|
||||
[dependencies]
|
||||
crc32fast = "1.4.2"
|
||||
esplugin = "6.1.1"
|
||||
libloadorder = { git = "https://github.com/Ortham/libloadorder.git", rev = "6245dbc6824c4751daef5ab01dfe7b9497f5446f" }
|
||||
esplugin.workspace = true
|
||||
log = { version = "0.4.26", features = ["std"] }
|
||||
loot-condition-interpreter = { git = "https://github.com/loot/loot-condition-interpreter.git", rev = "f7d9947fa434037743ce81e2d409184ec0bfb693" }
|
||||
libloadorder.workspace = true
|
||||
loot-condition-interpreter.workspace = true
|
||||
petgraph = "0.7.1"
|
||||
fancy-regex = "0.14.0"
|
||||
saphyr = "0.0.3"
|
||||
@@ -26,4 +26,9 @@ rstest_reuse = "0.7.0"
|
||||
tempfile = "3.17.1"
|
||||
|
||||
[workspace]
|
||||
members = ["cxx", "pyo3"]
|
||||
members = ["cxx", "ffi-errors", "pyo3"]
|
||||
|
||||
[workspace.dependencies]
|
||||
esplugin = "6.1.1"
|
||||
libloadorder = { git = "https://github.com/Ortham/libloadorder.git", rev = "6245dbc6824c4751daef5ab01dfe7b9497f5446f" }
|
||||
loot-condition-interpreter = { git = "https://github.com/loot/loot-condition-interpreter.git", rev = "f7d9947fa434037743ce81e2d409184ec0bfb693" }
|
||||
|
||||
+1
-4
@@ -8,12 +8,9 @@ license = "GPL-3.0"
|
||||
cxx = { version = "1.0", features = ["c++17"] }
|
||||
delegate = "0.13.2"
|
||||
libloot = { path = ".." }
|
||||
libloot-ffi-errors = { path = "../ffi-errors" }
|
||||
unicase = "2.8.1"
|
||||
|
||||
esplugin = "6.1.1"
|
||||
libloadorder = { git = "https://github.com/Ortham/libloadorder.git", rev = "6245dbc6824c4751daef5ab01dfe7b9497f5446f" }
|
||||
loot-condition-interpreter = { git = "https://github.com/loot/loot-condition-interpreter.git", rev = "f7d9947fa434037743ce81e2d409184ec0bfb693" }
|
||||
|
||||
[build-dependencies]
|
||||
cxx-build = "1.0"
|
||||
|
||||
|
||||
@@ -1,236 +0,0 @@
|
||||
pub(crate) mod lci {
|
||||
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(crate) 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) mod libloadorder {
|
||||
use loadorder::Error;
|
||||
use std::ffi::c_uint;
|
||||
|
||||
/// 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_uint = 2;
|
||||
|
||||
/// The specified file could not be found.
|
||||
#[unsafe(no_mangle)]
|
||||
pub static LIBLO_ERROR_FILE_NOT_FOUND: c_uint = 6;
|
||||
|
||||
/// A file could not be renamed.
|
||||
#[unsafe(no_mangle)]
|
||||
pub static LIBLO_ERROR_FILE_RENAME_FAIL: c_uint = 7;
|
||||
|
||||
/// There was an error parsing a plugin file.
|
||||
#[unsafe(no_mangle)]
|
||||
pub static LIBLO_ERROR_FILE_PARSE_FAIL: c_uint = 10;
|
||||
|
||||
/// Invalid arguments were given for the function.
|
||||
#[unsafe(no_mangle)]
|
||||
pub static LIBLO_ERROR_INVALID_ARGS: c_uint = 12;
|
||||
|
||||
/// A thread lock was poisoned.
|
||||
#[unsafe(no_mangle)]
|
||||
pub static LIBLO_ERROR_POISONED_THREAD_LOCK: c_uint = 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_uint = 15;
|
||||
|
||||
/// Permission denied while trying to access a filesystem path.
|
||||
#[unsafe(no_mangle)]
|
||||
pub static LIBLO_ERROR_IO_PERMISSION_DENIED: c_uint = 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_uint = 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_uint = 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_uint = 19;
|
||||
|
||||
/// Something panicked.
|
||||
#[unsafe(no_mangle)]
|
||||
pub static LIBLO_ERROR_PANICKED: c_uint = 20;
|
||||
|
||||
/// A path cannot be encoded in UTF-8.
|
||||
#[unsafe(no_mangle)]
|
||||
pub static LIBLO_ERROR_PATH_ENCODE_FAIL: c_uint = 21;
|
||||
|
||||
/// An unknown operating system error occurred.
|
||||
#[unsafe(no_mangle)]
|
||||
pub static LIBLO_ERROR_SYSTEM_ERROR: c_uint = 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_uint = 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_uint = 23;
|
||||
|
||||
fn map_io_error(err: &std::io::Error) -> c_uint {
|
||||
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(crate) fn map_error(err: &Error) -> c_uint {
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) mod esplugin {
|
||||
use std::ffi::c_uint;
|
||||
|
||||
use esplugin::Error;
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub static ESP_ERROR_NULL_POINTER: u32 = 1;
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub static ESP_ERROR_NOT_UTF8: u32 = 2;
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub static ESP_ERROR_STRING_CONTAINS_NUL: u32 = 3;
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub static ESP_ERROR_INVALID_GAME_ID: u32 = 4;
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub static ESP_ERROR_PARSE_ERROR: u32 = 5;
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub static ESP_ERROR_PANICKED: u32 = 6;
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub static ESP_ERROR_NO_FILENAME: u32 = 7;
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub static ESP_ERROR_TEXT_DECODE_ERROR: u32 = 8;
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub static ESP_ERROR_TEXT_ENCODE_ERROR: u32 = 9;
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub static ESP_ERROR_IO_ERROR: u32 = 10;
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub static ESP_ERROR_FILE_NOT_FOUND: u32 = 11;
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub static ESP_ERROR_IO_PERMISSION_DENIED: u32 = 12;
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub static ESP_ERROR_UNRESOLVED_RECORD_IDS: u32 = 13;
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub static ESP_ERROR_PLUGIN_METADATA_NOT_FOUND: u32 = 14;
|
||||
|
||||
fn map_io_error(err: &std::io::Error) -> c_uint {
|
||||
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(crate) fn map_error(err: &Error) -> c_uint {
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
+9
-25
@@ -1,11 +1,11 @@
|
||||
mod database;
|
||||
mod error_codes;
|
||||
mod game;
|
||||
mod metadata;
|
||||
mod plugin;
|
||||
|
||||
use database::{Database, Vertex, new_vertex};
|
||||
use game::{Game, NotValidUtf8, new_game, new_game_with_local_path};
|
||||
use libloot_ffi_errors::SystemError;
|
||||
use metadata::{
|
||||
File, Filename, Group, Location, Message, MessageContent, OptionalMessageContentRef,
|
||||
OptionalPluginMetadata, PluginCleaningData, PluginMetadata, Tag, group_default_name,
|
||||
@@ -15,7 +15,6 @@ use metadata::{
|
||||
};
|
||||
use plugin::{OptionalPlugin, Plugin};
|
||||
use std::{
|
||||
error::Error,
|
||||
ffi::{CString, c_char, c_uchar, c_uint, c_void},
|
||||
sync::{Mutex, atomic::AtomicPtr},
|
||||
};
|
||||
@@ -38,8 +37,8 @@ pub use libloot::{is_compatible, libloot_revision, libloot_version};
|
||||
pub enum VerboseError {
|
||||
CyclicInteractionError(Vec<libloot::Vertex>),
|
||||
UndefinedGroupError(String),
|
||||
EspluginError(u32, String),
|
||||
LibloadorderError(u32, String),
|
||||
EspluginError(i32, String),
|
||||
LibloadorderError(i32, String),
|
||||
LciError(i32, String),
|
||||
FileAccessError(String),
|
||||
InvalidArgument(String),
|
||||
@@ -148,13 +147,8 @@ impl From<LoadOrderStateError> for VerboseError {
|
||||
|
||||
impl From<LoadOrderError> for VerboseError {
|
||||
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 = error_codes::libloadorder::map_error(error);
|
||||
Self::LibloadorderError(error_code, error.to_string())
|
||||
let error = SystemError::from(value);
|
||||
Self::LibloadorderError(error.code(), error.message().to_string())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,13 +166,8 @@ impl From<WriteMetadataError> for VerboseError {
|
||||
|
||||
impl From<ConditionEvaluationError> for VerboseError {
|
||||
fn from(value: ConditionEvaluationError) -> Self {
|
||||
let error = value
|
||||
.source()
|
||||
.expect("LoadOrderError has source")
|
||||
.downcast_ref::<loot_condition_interpreter::Error>()
|
||||
.expect("LoadOrderError source is a loot_condition_interpreter::Error");
|
||||
let error_code = error_codes::lci::map_error(error);
|
||||
Self::LciError(error_code, error.to_string())
|
||||
let error = SystemError::from(value);
|
||||
Self::LciError(error.code(), error.message().to_string())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,13 +192,8 @@ impl From<MetadataRetrievalError> for VerboseError {
|
||||
|
||||
impl From<PluginDataError> for VerboseError {
|
||||
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 = error_codes::esplugin::map_error(error);
|
||||
Self::EspluginError(error_code, error.to_string())
|
||||
let error = SystemError::from(value);
|
||||
Self::EspluginError(error.code(), error.message().to_string())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
@@ -10,4 +10,5 @@ crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
libloot = { path = ".." }
|
||||
libloot-ffi-errors = { path = "../ffi-errors" }
|
||||
pyo3 = "0.24.0"
|
||||
|
||||
+8
-2
@@ -31,8 +31,7 @@ An **incomplete** and **experimental** Python wrapper around the libloot Rust im
|
||||
- [x] `MessageType`
|
||||
- [x] `TagSuggestion`
|
||||
- [x] `select_message_content()`
|
||||
- [ ] Error types
|
||||
- All errors are currently raised as Python `ValueError` values that contain the full detail of the error.
|
||||
- [x] Error types
|
||||
|
||||
## Build
|
||||
|
||||
@@ -58,3 +57,10 @@ Then import it in Python:
|
||||
py
|
||||
> import loot
|
||||
```
|
||||
|
||||
## Usage notes
|
||||
|
||||
- The Python exceptions that errors are mapped to are not the same as in the Rust or C++ interfaces:
|
||||
- The API provides the custom `CyclicInteractionError`, `UndefinedGroupError`, `EspluginError` exception types.
|
||||
- All other errors are raised as `ValueError` exceptions.
|
||||
- There's no equivalent to the C++ interface's `FileAccessError` or `ConditionSyntaxError` classes or the libloadorder and loot-condition-interpreter system error categories.
|
||||
|
||||
@@ -11,7 +11,7 @@ use pyo3::{
|
||||
};
|
||||
|
||||
use crate::{
|
||||
UnsupportedEnumValueError, VerboseError,
|
||||
error::{UnsupportedEnumValueError, VerboseError},
|
||||
metadata::{Group, Message, NONE_REPR, PluginMetadata},
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,208 @@
|
||||
use libloot::{
|
||||
error::{
|
||||
ConditionEvaluationError, DatabaseLockPoisonError, GameHandleCreationError,
|
||||
GroupsPathError, LoadOrderError, LoadOrderStateError, LoadPluginsError,
|
||||
MetadataRetrievalError, PluginDataError, SortPluginsError,
|
||||
},
|
||||
metadata::error::{
|
||||
LoadMetadataError, MultilingualMessageContentsError, RegexError, WriteMetadataError,
|
||||
},
|
||||
};
|
||||
use libloot_ffi_errors::SystemError;
|
||||
use pyo3::{PyErr, exceptions::PyValueError};
|
||||
|
||||
use crate::{CyclicInteractionError, EspluginError, UndefinedGroupError, database::Vertex};
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
pub struct UnsupportedEnumValueError;
|
||||
|
||||
impl std::fmt::Display for UnsupportedEnumValueError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "Enum value is unsupported")
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for UnsupportedEnumValueError {}
|
||||
|
||||
impl From<UnsupportedEnumValueError> for PyErr {
|
||||
fn from(value: UnsupportedEnumValueError) -> Self {
|
||||
PyValueError::new_err(value.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum VerboseError {
|
||||
CyclicInteractionError(Vec<libloot::Vertex>),
|
||||
UndefinedGroupError(String),
|
||||
EspluginError(i32, String),
|
||||
Other(Box<dyn std::error::Error>),
|
||||
}
|
||||
|
||||
impl std::fmt::Display for VerboseError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::CyclicInteractionError(c) => {
|
||||
let cycle = display_cycle(c);
|
||||
write!(f, "cyclic interaction detected: {}", cycle)
|
||||
}
|
||||
Self::UndefinedGroupError(g) => {
|
||||
write!(f, "the group \"{}\" does not exist", g)
|
||||
}
|
||||
Self::EspluginError(_, s) => s.fmt(f),
|
||||
Self::Other(e) => {
|
||||
write!(f, "{}", e)?;
|
||||
let mut error = e.as_ref();
|
||||
while let Some(source) = error.source() {
|
||||
write!(f, ": {}", source)?;
|
||||
error = source;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<GameHandleCreationError> for VerboseError {
|
||||
fn from(value: GameHandleCreationError) -> Self {
|
||||
match value {
|
||||
GameHandleCreationError::LoadOrderError(e) => e.into(),
|
||||
_ => Self::Other(Box::new(value)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<UnsupportedEnumValueError> for VerboseError {
|
||||
fn from(value: UnsupportedEnumValueError) -> Self {
|
||||
Self::Other(Box::new(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DatabaseLockPoisonError> for VerboseError {
|
||||
fn from(value: DatabaseLockPoisonError) -> Self {
|
||||
Self::Other(Box::new(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LoadPluginsError> for VerboseError {
|
||||
fn from(value: LoadPluginsError) -> Self {
|
||||
match value {
|
||||
LoadPluginsError::PluginDataError(e) => e.into(),
|
||||
_ => Self::Other(Box::new(value)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SortPluginsError> for VerboseError {
|
||||
fn from(value: SortPluginsError) -> Self {
|
||||
match value {
|
||||
SortPluginsError::MetadataRetrievalError(e) => e.into(),
|
||||
SortPluginsError::UndefinedGroup(g) => Self::UndefinedGroupError(g),
|
||||
SortPluginsError::CycleFound(cycle) => Self::CyclicInteractionError(cycle),
|
||||
SortPluginsError::PluginDataError(e) => e.into(),
|
||||
_ => Self::Other(Box::new(value)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LoadOrderStateError> for VerboseError {
|
||||
fn from(value: LoadOrderStateError) -> Self {
|
||||
match value {
|
||||
LoadOrderStateError::LoadOrderError(e) => e.into(),
|
||||
_ => Self::Other(Box::new(value)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LoadOrderError> for VerboseError {
|
||||
fn from(value: LoadOrderError) -> Self {
|
||||
Self::Other(Box::new(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LoadMetadataError> for VerboseError {
|
||||
fn from(value: LoadMetadataError) -> Self {
|
||||
Self::Other(Box::new(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<WriteMetadataError> for VerboseError {
|
||||
fn from(value: WriteMetadataError) -> Self {
|
||||
Self::Other(Box::new(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ConditionEvaluationError> for VerboseError {
|
||||
fn from(value: ConditionEvaluationError) -> Self {
|
||||
Self::Other(Box::new(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<GroupsPathError> for VerboseError {
|
||||
fn from(value: GroupsPathError) -> Self {
|
||||
match value {
|
||||
GroupsPathError::UndefinedGroup(g) => Self::UndefinedGroupError(g),
|
||||
GroupsPathError::CycleFound(cycle) => Self::CyclicInteractionError(cycle),
|
||||
_ => Self::Other(Box::new(value)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MetadataRetrievalError> for VerboseError {
|
||||
fn from(value: MetadataRetrievalError) -> Self {
|
||||
match value {
|
||||
MetadataRetrievalError::ConditionEvaluationError(e) => e.into(),
|
||||
_ => Self::Other(Box::new(value)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PluginDataError> for VerboseError {
|
||||
fn from(value: PluginDataError) -> Self {
|
||||
let error = SystemError::from(value);
|
||||
Self::EspluginError(error.code(), error.message().to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MultilingualMessageContentsError> for VerboseError {
|
||||
fn from(value: MultilingualMessageContentsError) -> Self {
|
||||
Self::Other(Box::new(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RegexError> for VerboseError {
|
||||
fn from(value: RegexError) -> Self {
|
||||
Self::Other(Box::new(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<VerboseError> for PyErr {
|
||||
fn from(value: VerboseError) -> Self {
|
||||
let message = value.to_string();
|
||||
|
||||
match value {
|
||||
VerboseError::CyclicInteractionError(c) => PyErr::new::<CyclicInteractionError, _>((
|
||||
c.into_iter().map(Vertex::from).collect::<Vec<_>>(),
|
||||
message,
|
||||
)),
|
||||
VerboseError::UndefinedGroupError(g) => {
|
||||
PyErr::new::<UndefinedGroupError, _>((g, message))
|
||||
}
|
||||
VerboseError::EspluginError(i, s) => PyErr::new::<EspluginError, _>((i, s)),
|
||||
VerboseError::Other(_) => PyValueError::new_err(message),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn display_cycle(cycle: &[libloot::Vertex]) -> String {
|
||||
cycle
|
||||
.iter()
|
||||
.map(|v| {
|
||||
if let Some(edge_type) = v.out_edge_type() {
|
||||
format!("{} --[{}]-> ", v.name(), edge_type)
|
||||
} else {
|
||||
v.name().to_string()
|
||||
}
|
||||
})
|
||||
.chain(cycle.first().iter().map(|v| v.name().to_string()))
|
||||
.collect()
|
||||
}
|
||||
+5
-1
@@ -2,7 +2,11 @@ use std::path::{Path, PathBuf};
|
||||
|
||||
use pyo3::{pyclass, pymethods};
|
||||
|
||||
use crate::{UnsupportedEnumValueError, VerboseError, database::Database, plugin::Plugin};
|
||||
use crate::{
|
||||
database::Database,
|
||||
error::{UnsupportedEnumValueError, VerboseError},
|
||||
plugin::Plugin,
|
||||
};
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
#[pyclass(eq, frozen, hash, ord)]
|
||||
|
||||
+13
-48
@@ -1,4 +1,5 @@
|
||||
mod database;
|
||||
mod error;
|
||||
mod game;
|
||||
mod metadata;
|
||||
mod plugin;
|
||||
@@ -10,7 +11,7 @@ use metadata::{
|
||||
TagSuggestion, select_message_content,
|
||||
};
|
||||
use plugin::Plugin;
|
||||
use pyo3::{exceptions::PyValueError, prelude::*};
|
||||
use pyo3::{create_exception, exceptions::PyException, prelude::*};
|
||||
|
||||
#[pyfunction]
|
||||
fn is_compatible(major: u32, minor: u32, patch: u32) -> bool {
|
||||
@@ -27,56 +28,13 @@ fn libloot_version() -> String {
|
||||
libloot::libloot_version()
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
pub struct UnsupportedEnumValueError;
|
||||
|
||||
impl std::fmt::Display for UnsupportedEnumValueError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "Enum value is unsupported")
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for UnsupportedEnumValueError {}
|
||||
|
||||
impl From<UnsupportedEnumValueError> for PyErr {
|
||||
fn from(value: UnsupportedEnumValueError) -> Self {
|
||||
PyValueError::new_err(value.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct VerboseError(Box<dyn std::error::Error>);
|
||||
|
||||
impl std::fmt::Display for VerboseError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let e = &self.0;
|
||||
write!(f, "{}", e)?;
|
||||
let mut error = e.as_ref();
|
||||
while let Some(source) = error.source() {
|
||||
write!(f, ": {}", source)?;
|
||||
error = source;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// impl std::error::Error for VerboseError {}
|
||||
|
||||
impl<T: std::error::Error + 'static> From<T> for VerboseError {
|
||||
fn from(value: T) -> Self {
|
||||
VerboseError(Box::new(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<VerboseError> for PyErr {
|
||||
fn from(value: VerboseError) -> Self {
|
||||
PyValueError::new_err(value.to_string())
|
||||
}
|
||||
}
|
||||
create_exception!(loot, CyclicInteractionError, PyException);
|
||||
create_exception!(loot, UndefinedGroupError, PyException);
|
||||
create_exception!(loot, EspluginError, PyException);
|
||||
|
||||
/// A Python module implemented in Rust.
|
||||
#[pymodule(name = "loot")]
|
||||
fn libloot_pyo3(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
||||
fn libloot_pyo3(py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
|
||||
m.add("LIBLOOT_VERSION_MAJOR", libloot::LIBLOOT_VERSION_MAJOR)?;
|
||||
m.add("LIBLOOT_VERSION_MINOR", libloot::LIBLOOT_VERSION_MINOR)?;
|
||||
m.add("LIBLOOT_VERSION_PATCH", libloot::LIBLOOT_VERSION_PATCH)?;
|
||||
@@ -104,5 +62,12 @@ fn libloot_pyo3(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
||||
m.add_class::<Location>()?;
|
||||
m.add_class::<PluginMetadata>()?;
|
||||
|
||||
m.add(
|
||||
"CyclicInteractionError",
|
||||
py.get_type::<CyclicInteractionError>(),
|
||||
)?;
|
||||
m.add("UndefinedGroupError", py.get_type::<UndefinedGroupError>())?;
|
||||
m.add("EspluginError", py.get_type::<EspluginError>())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ use pyo3::{
|
||||
types::{PyAnyMethods, PyTypeMethods},
|
||||
};
|
||||
|
||||
use crate::{UnsupportedEnumValueError, VerboseError};
|
||||
use crate::error::{UnsupportedEnumValueError, VerboseError};
|
||||
|
||||
pub const NONE_REPR: &str = "None";
|
||||
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ use std::sync::Arc;
|
||||
|
||||
use pyo3::{pyclass, pymethods};
|
||||
|
||||
use crate::VerboseError;
|
||||
use crate::error::VerboseError;
|
||||
|
||||
#[pyclass(eq, frozen)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
|
||||
Reference in New Issue
Block a user