mirror of
https://github.com/loot/libloot.git
synced 2026-07-27 14:16:01 -07:00
Rename pyo3 directory to python
Also update the crate's name.
This commit is contained in:
Vendored
+181
@@ -0,0 +1,181 @@
|
||||
# This file is autogenerated by maturin v1.8.3
|
||||
# To update, run
|
||||
#
|
||||
# maturin generate-ci github
|
||||
#
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- master
|
||||
tags:
|
||||
- '*'
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
linux:
|
||||
runs-on: ${{ matrix.platform.runner }}
|
||||
strategy:
|
||||
matrix:
|
||||
platform:
|
||||
- runner: ubuntu-22.04
|
||||
target: x86_64
|
||||
- runner: ubuntu-22.04
|
||||
target: x86
|
||||
- runner: ubuntu-22.04
|
||||
target: aarch64
|
||||
- runner: ubuntu-22.04
|
||||
target: armv7
|
||||
- runner: ubuntu-22.04
|
||||
target: s390x
|
||||
- runner: ubuntu-22.04
|
||||
target: ppc64le
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: 3.x
|
||||
- name: Build wheels
|
||||
uses: PyO3/maturin-action@v1
|
||||
with:
|
||||
target: ${{ matrix.platform.target }}
|
||||
args: --release --out dist --find-interpreter
|
||||
sccache: ${{ !startsWith(github.ref, 'refs/tags/') }}
|
||||
manylinux: auto
|
||||
- name: Upload wheels
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: wheels-linux-${{ matrix.platform.target }}
|
||||
path: dist
|
||||
|
||||
musllinux:
|
||||
runs-on: ${{ matrix.platform.runner }}
|
||||
strategy:
|
||||
matrix:
|
||||
platform:
|
||||
- runner: ubuntu-22.04
|
||||
target: x86_64
|
||||
- runner: ubuntu-22.04
|
||||
target: x86
|
||||
- runner: ubuntu-22.04
|
||||
target: aarch64
|
||||
- runner: ubuntu-22.04
|
||||
target: armv7
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: 3.x
|
||||
- name: Build wheels
|
||||
uses: PyO3/maturin-action@v1
|
||||
with:
|
||||
target: ${{ matrix.platform.target }}
|
||||
args: --release --out dist --find-interpreter
|
||||
sccache: ${{ !startsWith(github.ref, 'refs/tags/') }}
|
||||
manylinux: musllinux_1_2
|
||||
- name: Upload wheels
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: wheels-musllinux-${{ matrix.platform.target }}
|
||||
path: dist
|
||||
|
||||
windows:
|
||||
runs-on: ${{ matrix.platform.runner }}
|
||||
strategy:
|
||||
matrix:
|
||||
platform:
|
||||
- runner: windows-latest
|
||||
target: x64
|
||||
- runner: windows-latest
|
||||
target: x86
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: 3.x
|
||||
architecture: ${{ matrix.platform.target }}
|
||||
- name: Build wheels
|
||||
uses: PyO3/maturin-action@v1
|
||||
with:
|
||||
target: ${{ matrix.platform.target }}
|
||||
args: --release --out dist --find-interpreter
|
||||
sccache: ${{ !startsWith(github.ref, 'refs/tags/') }}
|
||||
- name: Upload wheels
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: wheels-windows-${{ matrix.platform.target }}
|
||||
path: dist
|
||||
|
||||
macos:
|
||||
runs-on: ${{ matrix.platform.runner }}
|
||||
strategy:
|
||||
matrix:
|
||||
platform:
|
||||
- runner: macos-13
|
||||
target: x86_64
|
||||
- runner: macos-14
|
||||
target: aarch64
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: 3.x
|
||||
- name: Build wheels
|
||||
uses: PyO3/maturin-action@v1
|
||||
with:
|
||||
target: ${{ matrix.platform.target }}
|
||||
args: --release --out dist --find-interpreter
|
||||
sccache: ${{ !startsWith(github.ref, 'refs/tags/') }}
|
||||
- name: Upload wheels
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: wheels-macos-${{ matrix.platform.target }}
|
||||
path: dist
|
||||
|
||||
sdist:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Build sdist
|
||||
uses: PyO3/maturin-action@v1
|
||||
with:
|
||||
command: sdist
|
||||
args: --out dist
|
||||
- name: Upload sdist
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: wheels-sdist
|
||||
path: dist
|
||||
|
||||
release:
|
||||
name: Release
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch' }}
|
||||
needs: [linux, musllinux, windows, macos, sdist]
|
||||
permissions:
|
||||
# Use to sign the release artifacts
|
||||
id-token: write
|
||||
# Used to upload release artifacts
|
||||
contents: write
|
||||
# Used to generate artifact attestation
|
||||
attestations: write
|
||||
steps:
|
||||
- uses: actions/download-artifact@v4
|
||||
- name: Generate artifact attestation
|
||||
uses: actions/attest-build-provenance@v2
|
||||
with:
|
||||
subject-path: 'wheels-*/*'
|
||||
- name: Publish to PyPI
|
||||
if: ${{ startsWith(github.ref, 'refs/tags/') }}
|
||||
uses: PyO3/maturin-action@v1
|
||||
env:
|
||||
MATURIN_PYPI_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
|
||||
with:
|
||||
command: upload
|
||||
args: --non-interactive --skip-existing wheels-*/*
|
||||
@@ -0,0 +1,72 @@
|
||||
/target
|
||||
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
.pytest_cache/
|
||||
*.py[cod]
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
.venv/
|
||||
env/
|
||||
bin/
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
include/
|
||||
man/
|
||||
venv/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
pip-selfcheck.json
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.coverage
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
|
||||
# Mr Developer
|
||||
.mr.developer.cfg
|
||||
.project
|
||||
.pydevproject
|
||||
|
||||
# Rope
|
||||
.ropeproject
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
*.pot
|
||||
|
||||
.DS_Store
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyCharm
|
||||
.idea/
|
||||
|
||||
# VSCode
|
||||
.vscode/
|
||||
|
||||
# Pyenv
|
||||
.python-version
|
||||
@@ -0,0 +1,15 @@
|
||||
[package]
|
||||
name = "libloot_python"
|
||||
version = "0.26.0"
|
||||
edition = "2024"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
[lib]
|
||||
name = "loot"
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
libloot = { path = ".." }
|
||||
libloot-ffi-errors = { path = "../ffi-errors" }
|
||||
pyo3 = "0.24.0"
|
||||
pyo3-log = "0.12.2"
|
||||
@@ -0,0 +1,42 @@
|
||||
# libloot-python
|
||||
|
||||
An **experimental** Python wrapper around the libloot Rust implementation, built using [PyO3](https://pyo3.rs).
|
||||
|
||||
## Build
|
||||
|
||||
To build, first set up a Python virtual environment and install [maturin](https://github.com/PyO3/maturin):
|
||||
|
||||
```powershell
|
||||
python -m venv .venv
|
||||
.\.venv\Scripts\activate
|
||||
pip install maturin
|
||||
```
|
||||
|
||||
or in a POSIX shell:
|
||||
|
||||
```sh
|
||||
python -m venv .venv
|
||||
. .venv/bin/activate
|
||||
pip install maturin
|
||||
```
|
||||
|
||||
Then build the library in the virtual environment:
|
||||
|
||||
```
|
||||
maturin develop
|
||||
```
|
||||
|
||||
The library can then be imported in Python:
|
||||
|
||||
```
|
||||
python
|
||||
> 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.
|
||||
- The `LogLevel` enum and `set_logging_callback()` and `set_log_level()` functions are not exposed because the logging is integrated with Python's `logging` module instead.
|
||||
@@ -0,0 +1,16 @@
|
||||
[build-system]
|
||||
requires = ["maturin>=1.8,<2.0"]
|
||||
build-backend = "maturin"
|
||||
|
||||
[project]
|
||||
name = "libloot"
|
||||
requires-python = ">=3.8"
|
||||
classifiers = [
|
||||
"Programming Language :: Rust",
|
||||
"Programming Language :: Python :: Implementation :: CPython",
|
||||
"Programming Language :: Python :: Implementation :: PyPy",
|
||||
]
|
||||
dynamic = ["version"]
|
||||
|
||||
[tool.maturin]
|
||||
features = ["pyo3/extension-module"]
|
||||
@@ -0,0 +1,370 @@
|
||||
use std::{
|
||||
hash::{DefaultHasher, Hash, Hasher},
|
||||
path::PathBuf,
|
||||
sync::{Arc, RwLock},
|
||||
};
|
||||
|
||||
use libloot::{WriteMode, error::DatabaseLockPoisonError};
|
||||
use libloot_ffi_errors::UnsupportedEnumValueError;
|
||||
use pyo3::{
|
||||
Bound, PyResult, pyclass, pymethods,
|
||||
types::{PyAnyMethods, PyTypeMethods},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
error::VerboseError,
|
||||
metadata::{Group, Message, NONE_REPR, PluginMetadata},
|
||||
};
|
||||
|
||||
#[pyclass]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Database(Arc<RwLock<libloot::Database>>);
|
||||
|
||||
#[pymethods]
|
||||
impl Database {
|
||||
#[expect(clippy::needless_pass_by_value, reason = "Required by PyO3")]
|
||||
pub fn load_masterlist(&self, path: PathBuf) -> Result<(), VerboseError> {
|
||||
self.0
|
||||
.write()
|
||||
.map_err(DatabaseLockPoisonError::from)?
|
||||
.load_masterlist(&path)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
#[expect(clippy::needless_pass_by_value, reason = "Required by PyO3")]
|
||||
pub fn load_masterlist_with_prelude(
|
||||
&self,
|
||||
masterlist_path: PathBuf,
|
||||
prelude_path: PathBuf,
|
||||
) -> Result<(), VerboseError> {
|
||||
self.0
|
||||
.write()
|
||||
.map_err(DatabaseLockPoisonError::from)?
|
||||
.load_masterlist_with_prelude(&masterlist_path, &prelude_path)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
#[expect(clippy::needless_pass_by_value, reason = "Required by PyO3")]
|
||||
pub fn load_userlist(&self, path: PathBuf) -> Result<(), VerboseError> {
|
||||
self.0
|
||||
.write()
|
||||
.map_err(DatabaseLockPoisonError::from)?
|
||||
.load_userlist(&path)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
#[expect(clippy::needless_pass_by_value, reason = "Required by PyO3")]
|
||||
pub fn write_user_metadata(
|
||||
&self,
|
||||
output_path: PathBuf,
|
||||
overwrite: bool,
|
||||
) -> Result<(), VerboseError> {
|
||||
let write_mode = if overwrite {
|
||||
WriteMode::CreateOrTruncate
|
||||
} else {
|
||||
WriteMode::Create
|
||||
};
|
||||
|
||||
self.0
|
||||
.read()
|
||||
.map_err(DatabaseLockPoisonError::from)?
|
||||
.write_user_metadata(&output_path, write_mode)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
#[expect(clippy::needless_pass_by_value, reason = "Required by PyO3")]
|
||||
pub fn write_minimal_list(
|
||||
&self,
|
||||
output_path: PathBuf,
|
||||
overwrite: bool,
|
||||
) -> Result<(), VerboseError> {
|
||||
let write_mode = if overwrite {
|
||||
WriteMode::CreateOrTruncate
|
||||
} else {
|
||||
WriteMode::Create
|
||||
};
|
||||
|
||||
self.0
|
||||
.read()
|
||||
.map_err(DatabaseLockPoisonError::from)?
|
||||
.write_minimal_list(&output_path, write_mode)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
pub fn evaluate(&self, condition: &str) -> Result<bool, VerboseError> {
|
||||
self.0
|
||||
.read()
|
||||
.map_err(DatabaseLockPoisonError::from)?
|
||||
.evaluate(condition)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
pub fn known_bash_tags(&self) -> Result<Vec<String>, VerboseError> {
|
||||
Ok(self
|
||||
.0
|
||||
.read()
|
||||
.map_err(DatabaseLockPoisonError::from)?
|
||||
.known_bash_tags())
|
||||
}
|
||||
|
||||
pub fn general_messages(
|
||||
&self,
|
||||
evaluate_conditions: bool,
|
||||
) -> Result<Vec<Message>, VerboseError> {
|
||||
self.0
|
||||
.write()
|
||||
.map_err(DatabaseLockPoisonError::from)?
|
||||
.general_messages(evaluate_conditions)
|
||||
.map(|v| v.into_iter().map(Into::into).collect())
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
pub fn groups(&self, include_user_metadata: bool) -> Result<Vec<Group>, VerboseError> {
|
||||
Ok(self
|
||||
.0
|
||||
.read()
|
||||
.map_err(DatabaseLockPoisonError::from)?
|
||||
.groups(include_user_metadata)
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect())
|
||||
}
|
||||
|
||||
fn user_groups(&self) -> Result<Vec<Group>, VerboseError> {
|
||||
Ok(self
|
||||
.0
|
||||
.read()
|
||||
.map_err(DatabaseLockPoisonError::from)?
|
||||
.user_groups()
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(Into::into)
|
||||
.collect())
|
||||
}
|
||||
|
||||
pub fn set_user_groups(&self, groups: Vec<Group>) -> Result<(), VerboseError> {
|
||||
let groups = groups.into_iter().map(Into::into).collect();
|
||||
self.0
|
||||
.write()
|
||||
.map_err(DatabaseLockPoisonError::from)?
|
||||
.set_user_groups(groups);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn groups_path(
|
||||
&self,
|
||||
from_group_name: &str,
|
||||
to_group_name: &str,
|
||||
) -> Result<Vec<Vertex>, VerboseError> {
|
||||
self.0
|
||||
.read()
|
||||
.map_err(DatabaseLockPoisonError::from)?
|
||||
.groups_path(from_group_name, to_group_name)
|
||||
.map(|v| v.into_iter().map(Into::into).collect())
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
pub fn plugin_metadata(
|
||||
&self,
|
||||
plugin_name: &str,
|
||||
include_user_metadata: bool,
|
||||
evaluate_conditions: bool,
|
||||
) -> Result<Option<PluginMetadata>, VerboseError> {
|
||||
self.0
|
||||
.read()
|
||||
.map_err(DatabaseLockPoisonError::from)?
|
||||
.plugin_metadata(plugin_name, include_user_metadata, evaluate_conditions)
|
||||
.map(|p| p.map(Into::into))
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
pub fn plugin_user_metadata(
|
||||
&self,
|
||||
plugin_name: &str,
|
||||
evaluate_conditions: bool,
|
||||
) -> Result<Option<PluginMetadata>, VerboseError> {
|
||||
self.0
|
||||
.read()
|
||||
.map_err(DatabaseLockPoisonError::from)?
|
||||
.plugin_user_metadata(plugin_name, evaluate_conditions)
|
||||
.map(|p| p.map(Into::into))
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
pub fn set_plugin_user_metadata(
|
||||
&mut self,
|
||||
plugin_metadata: PluginMetadata,
|
||||
) -> Result<(), VerboseError> {
|
||||
self.0
|
||||
.write()
|
||||
.map_err(DatabaseLockPoisonError::from)?
|
||||
.set_plugin_user_metadata(plugin_metadata.into());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn discard_plugin_user_metadata(&self, plugin: &str) -> Result<(), VerboseError> {
|
||||
self.0
|
||||
.write()
|
||||
.map_err(DatabaseLockPoisonError::from)?
|
||||
.discard_plugin_user_metadata(plugin);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn discard_all_user_metadata(&self) -> Result<(), VerboseError> {
|
||||
self.0
|
||||
.write()
|
||||
.map_err(DatabaseLockPoisonError::from)?
|
||||
.discard_all_user_metadata();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Arc<RwLock<libloot::Database>>> for Database {
|
||||
fn from(value: Arc<RwLock<libloot::Database>>) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
#[pyclass(eq, ord, str = "{0:?}")]
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
#[repr(transparent)]
|
||||
pub struct Vertex(libloot::Vertex);
|
||||
|
||||
#[pymethods]
|
||||
impl Vertex {
|
||||
#[new]
|
||||
fn new(name: String) -> Self {
|
||||
Self(libloot::Vertex::new(name))
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn name(&self) -> &str {
|
||||
self.0.name()
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn out_edge_type(&self) -> Result<Option<EdgeType>, VerboseError> {
|
||||
self.0
|
||||
.out_edge_type()
|
||||
.map(|e| e.try_into().map_err(Into::into))
|
||||
.transpose()
|
||||
}
|
||||
|
||||
#[setter]
|
||||
fn set_out_edge_type(&mut self, out_edge_type: EdgeType) -> Result<(), VerboseError> {
|
||||
let out_edge_type = out_edge_type.try_into()?;
|
||||
self.0.set_out_edge_type(out_edge_type);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn __repr__(slf: &Bound<'_, Self>) -> PyResult<String> {
|
||||
let class_name = slf.get_type().qualname()?;
|
||||
let inner = &slf.borrow().0;
|
||||
Ok(format!(
|
||||
"{}({}, {})",
|
||||
class_name,
|
||||
inner.name(),
|
||||
inner.out_edge_type().map_or(NONE_REPR, repr_edge_type),
|
||||
))
|
||||
}
|
||||
|
||||
fn __hash__(&self) -> u64 {
|
||||
let mut hasher = DefaultHasher::new();
|
||||
self.0.hash(&mut hasher);
|
||||
hasher.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<libloot::Vertex> for Vertex {
|
||||
fn from(value: libloot::Vertex) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vertex> for libloot::Vertex {
|
||||
fn from(value: Vertex) -> Self {
|
||||
value.0
|
||||
}
|
||||
}
|
||||
|
||||
#[pyclass(eq, frozen, hash, ord)]
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
pub enum EdgeType {
|
||||
Hardcoded,
|
||||
MasterFlag,
|
||||
Master,
|
||||
MasterlistRequirement,
|
||||
UserRequirement,
|
||||
MasterlistLoadAfter,
|
||||
UserLoadAfter,
|
||||
MasterlistGroup,
|
||||
UserGroup,
|
||||
RecordOverlap,
|
||||
AssetOverlap,
|
||||
TieBreak,
|
||||
BlueprintMaster,
|
||||
}
|
||||
|
||||
impl TryFrom<libloot::EdgeType> for EdgeType {
|
||||
type Error = UnsupportedEnumValueError;
|
||||
|
||||
fn try_from(value: libloot::EdgeType) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
libloot::EdgeType::Hardcoded => Ok(EdgeType::Hardcoded),
|
||||
libloot::EdgeType::MasterFlag => Ok(EdgeType::MasterFlag),
|
||||
libloot::EdgeType::Master => Ok(EdgeType::Master),
|
||||
libloot::EdgeType::MasterlistRequirement => Ok(EdgeType::MasterlistRequirement),
|
||||
libloot::EdgeType::UserRequirement => Ok(EdgeType::UserRequirement),
|
||||
libloot::EdgeType::MasterlistLoadAfter => Ok(EdgeType::MasterlistLoadAfter),
|
||||
libloot::EdgeType::UserLoadAfter => Ok(EdgeType::UserLoadAfter),
|
||||
libloot::EdgeType::MasterlistGroup => Ok(EdgeType::MasterlistGroup),
|
||||
libloot::EdgeType::UserGroup => Ok(EdgeType::UserGroup),
|
||||
libloot::EdgeType::RecordOverlap => Ok(EdgeType::RecordOverlap),
|
||||
libloot::EdgeType::AssetOverlap => Ok(EdgeType::AssetOverlap),
|
||||
libloot::EdgeType::TieBreak => Ok(EdgeType::TieBreak),
|
||||
libloot::EdgeType::BlueprintMaster => Ok(EdgeType::BlueprintMaster),
|
||||
_ => Err(UnsupportedEnumValueError),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<EdgeType> for libloot::EdgeType {
|
||||
type Error = UnsupportedEnumValueError;
|
||||
|
||||
fn try_from(value: EdgeType) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
EdgeType::Hardcoded => Ok(libloot::EdgeType::Hardcoded),
|
||||
EdgeType::MasterFlag => Ok(libloot::EdgeType::MasterFlag),
|
||||
EdgeType::Master => Ok(libloot::EdgeType::Master),
|
||||
EdgeType::MasterlistRequirement => Ok(libloot::EdgeType::MasterlistRequirement),
|
||||
EdgeType::UserRequirement => Ok(libloot::EdgeType::UserRequirement),
|
||||
EdgeType::MasterlistLoadAfter => Ok(libloot::EdgeType::MasterlistLoadAfter),
|
||||
EdgeType::UserLoadAfter => Ok(libloot::EdgeType::UserLoadAfter),
|
||||
EdgeType::MasterlistGroup => Ok(libloot::EdgeType::MasterlistGroup),
|
||||
EdgeType::UserGroup => Ok(libloot::EdgeType::UserGroup),
|
||||
EdgeType::RecordOverlap => Ok(libloot::EdgeType::RecordOverlap),
|
||||
EdgeType::AssetOverlap => Ok(libloot::EdgeType::AssetOverlap),
|
||||
EdgeType::TieBreak => Ok(libloot::EdgeType::TieBreak),
|
||||
EdgeType::BlueprintMaster => Ok(libloot::EdgeType::BlueprintMaster),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn repr_edge_type(value: libloot::EdgeType) -> &'static str {
|
||||
match value {
|
||||
libloot::EdgeType::Hardcoded => "EdgeType.Hardcoded",
|
||||
libloot::EdgeType::MasterFlag => "EdgeType.MasterFlag",
|
||||
libloot::EdgeType::Master => "EdgeType.Master",
|
||||
libloot::EdgeType::MasterlistRequirement => "EdgeType.MasterlistRequirement",
|
||||
libloot::EdgeType::UserRequirement => "EdgeType.UserRequirement",
|
||||
libloot::EdgeType::MasterlistLoadAfter => "EdgeType.MasterlistLoadAfter",
|
||||
libloot::EdgeType::UserLoadAfter => "EdgeType.UserLoadAfter",
|
||||
libloot::EdgeType::MasterlistGroup => "EdgeType.MasterlistGroup",
|
||||
libloot::EdgeType::UserGroup => "EdgeType.UserGroup",
|
||||
libloot::EdgeType::RecordOverlap => "EdgeType.RecordOverlap",
|
||||
libloot::EdgeType::AssetOverlap => "EdgeType.AssetOverlap",
|
||||
libloot::EdgeType::TieBreak => "EdgeType.TieBreak",
|
||||
libloot::EdgeType::BlueprintMaster => "EdgeType.BlueprintMaster",
|
||||
_ => "<unknown EdgeType>",
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
use libloot::{
|
||||
error::{
|
||||
ConditionEvaluationError, DatabaseLockPoisonError, GameHandleCreationError,
|
||||
GroupsPathError, LoadOrderError, LoadOrderStateError, LoadPluginsError,
|
||||
MetadataRetrievalError, PluginDataError, SortPluginsError,
|
||||
},
|
||||
metadata::error::{
|
||||
LoadMetadataError, MultilingualMessageContentsError, RegexError, WriteMetadataError,
|
||||
},
|
||||
};
|
||||
use libloot_ffi_errors::{
|
||||
SystemError, UnsupportedEnumValueError, fmt_error_chain, variant_box_from_error,
|
||||
};
|
||||
use pyo3::{PyErr, exceptions::PyValueError};
|
||||
|
||||
use crate::{CyclicInteractionError, EspluginError, UndefinedGroupError, database::Vertex};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum VerboseError {
|
||||
CyclicInteractionError(Vec<libloot::Vertex>),
|
||||
UndefinedGroupError(String),
|
||||
EspluginError(SystemError),
|
||||
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) => SortPluginsError::CycleFound(c.clone()).fmt(f),
|
||||
Self::UndefinedGroupError(g) => SortPluginsError::UndefinedGroup(g.clone()).fmt(f),
|
||||
Self::EspluginError(e) => e.message().fmt(f),
|
||||
Self::Other(e) => fmt_error_chain(e.as_ref(), f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
variant_box_from_error!(UnsupportedEnumValueError, VerboseError::Other);
|
||||
variant_box_from_error!(DatabaseLockPoisonError, VerboseError::Other);
|
||||
variant_box_from_error!(LoadPluginsError, VerboseError::Other);
|
||||
variant_box_from_error!(LoadOrderError, VerboseError::Other);
|
||||
variant_box_from_error!(LoadMetadataError, VerboseError::Other);
|
||||
variant_box_from_error!(WriteMetadataError, VerboseError::Other);
|
||||
variant_box_from_error!(ConditionEvaluationError, VerboseError::Other);
|
||||
variant_box_from_error!(MultilingualMessageContentsError, VerboseError::Other);
|
||||
variant_box_from_error!(RegexError, VerboseError::Other);
|
||||
|
||||
impl From<GameHandleCreationError> for VerboseError {
|
||||
fn from(value: GameHandleCreationError) -> Self {
|
||||
match value {
|
||||
GameHandleCreationError::LoadOrderError(e) => e.into(),
|
||||
GameHandleCreationError::NotADirectory(_) | _ => 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(),
|
||||
SortPluginsError::DatabaseLockPoisoned
|
||||
| SortPluginsError::PluginNotLoaded(_)
|
||||
| SortPluginsError::CycleFoundInvolving(_)
|
||||
| SortPluginsError::PathfindingError(_)
|
||||
| _ => Self::Other(Box::new(value)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LoadOrderStateError> for VerboseError {
|
||||
fn from(value: LoadOrderStateError) -> Self {
|
||||
match value {
|
||||
LoadOrderStateError::LoadOrderError(e) => e.into(),
|
||||
LoadOrderStateError::DatabaseLockPoisoned | _ => 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),
|
||||
GroupsPathError::PathfindingError(_) => Self::Other(Box::new(value)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MetadataRetrievalError> for VerboseError {
|
||||
fn from(value: MetadataRetrievalError) -> Self {
|
||||
match value {
|
||||
MetadataRetrievalError::ConditionEvaluationError(e) => e.into(),
|
||||
MetadataRetrievalError::RegexError(_) => Self::Other(Box::new(value)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PluginDataError> for VerboseError {
|
||||
fn from(value: PluginDataError) -> Self {
|
||||
Self::EspluginError(SystemError::from(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(e) => {
|
||||
PyErr::new::<EspluginError, _>((e.code(), e.message().to_owned()))
|
||||
}
|
||||
VerboseError::Other(_) => PyValueError::new_err(message),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use libloot_ffi_errors::UnsupportedEnumValueError;
|
||||
use pyo3::{pyclass, pymethods};
|
||||
|
||||
use crate::{database::Database, error::VerboseError, plugin::Plugin};
|
||||
|
||||
#[pyclass(eq, frozen, hash, ord)]
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
pub enum GameType {
|
||||
Oblivion,
|
||||
Skyrim,
|
||||
Fallout3,
|
||||
FalloutNV,
|
||||
Fallout4,
|
||||
SkyrimSE,
|
||||
Fallout4VR,
|
||||
SkyrimVR,
|
||||
Morrowind,
|
||||
Starfield,
|
||||
OpenMW,
|
||||
}
|
||||
|
||||
impl TryFrom<libloot::GameType> for GameType {
|
||||
type Error = UnsupportedEnumValueError;
|
||||
|
||||
fn try_from(value: libloot::GameType) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
libloot::GameType::Oblivion => Ok(GameType::Oblivion),
|
||||
libloot::GameType::Skyrim => Ok(GameType::Skyrim),
|
||||
libloot::GameType::Fallout3 => Ok(GameType::Fallout3),
|
||||
libloot::GameType::FalloutNV => Ok(GameType::FalloutNV),
|
||||
libloot::GameType::Fallout4 => Ok(GameType::Fallout4),
|
||||
libloot::GameType::SkyrimSE => Ok(GameType::SkyrimSE),
|
||||
libloot::GameType::Fallout4VR => Ok(GameType::Fallout4VR),
|
||||
libloot::GameType::SkyrimVR => Ok(GameType::SkyrimVR),
|
||||
libloot::GameType::Morrowind => Ok(GameType::Morrowind),
|
||||
libloot::GameType::Starfield => Ok(GameType::Starfield),
|
||||
libloot::GameType::OpenMW => Ok(GameType::OpenMW),
|
||||
_ => Err(UnsupportedEnumValueError),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<GameType> for libloot::GameType {
|
||||
type Error = UnsupportedEnumValueError;
|
||||
|
||||
fn try_from(value: GameType) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
GameType::Oblivion => Ok(libloot::GameType::Oblivion),
|
||||
GameType::Skyrim => Ok(libloot::GameType::Skyrim),
|
||||
GameType::Fallout3 => Ok(libloot::GameType::Fallout3),
|
||||
GameType::FalloutNV => Ok(libloot::GameType::FalloutNV),
|
||||
GameType::Fallout4 => Ok(libloot::GameType::Fallout4),
|
||||
GameType::SkyrimSE => Ok(libloot::GameType::SkyrimSE),
|
||||
GameType::Fallout4VR => Ok(libloot::GameType::Fallout4VR),
|
||||
GameType::SkyrimVR => Ok(libloot::GameType::SkyrimVR),
|
||||
GameType::Morrowind => Ok(libloot::GameType::Morrowind),
|
||||
GameType::Starfield => Ok(libloot::GameType::Starfield),
|
||||
GameType::OpenMW => Ok(libloot::GameType::OpenMW),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[pyclass]
|
||||
#[derive(Debug)]
|
||||
pub struct Game(libloot::Game);
|
||||
|
||||
#[pymethods]
|
||||
impl Game {
|
||||
#[new]
|
||||
#[pyo3(signature = (game_type, game_path, local_path = None))]
|
||||
#[expect(clippy::needless_pass_by_value, reason = "Required by PyO3")]
|
||||
fn new(
|
||||
game_type: GameType,
|
||||
game_path: PathBuf,
|
||||
local_path: Option<PathBuf>,
|
||||
) -> Result<Self, VerboseError> {
|
||||
match local_path {
|
||||
Some(local_path) => Ok(Game(libloot::Game::with_local_path(
|
||||
game_type.try_into()?,
|
||||
&game_path,
|
||||
&local_path,
|
||||
)?)),
|
||||
None => Ok(Game(libloot::Game::new(game_type.try_into()?, &game_path)?)),
|
||||
}
|
||||
}
|
||||
|
||||
fn game_type(&self) -> Result<GameType, VerboseError> {
|
||||
self.0.game_type().try_into().map_err(Into::into)
|
||||
}
|
||||
|
||||
fn additional_data_paths(&self) -> &[PathBuf] {
|
||||
self.0.additional_data_paths()
|
||||
}
|
||||
|
||||
#[expect(clippy::needless_pass_by_value, reason = "Required by PyO3")]
|
||||
fn set_additional_data_paths(&mut self, paths: Vec<PathBuf>) -> Result<(), VerboseError> {
|
||||
self.0.set_additional_data_paths(&as_paths(&paths))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn database(&self) -> Database {
|
||||
self.0.database().into()
|
||||
}
|
||||
|
||||
#[expect(clippy::needless_pass_by_value, reason = "Required by PyO3")]
|
||||
fn is_valid_plugin(&self, plugin_path: PathBuf) -> bool {
|
||||
self.0.is_valid_plugin(&plugin_path)
|
||||
}
|
||||
|
||||
#[expect(clippy::needless_pass_by_value, reason = "Required by PyO3")]
|
||||
fn load_plugins(&mut self, plugin_paths: Vec<PathBuf>) -> Result<(), VerboseError> {
|
||||
self.0.load_plugins(&as_paths(&plugin_paths))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[expect(clippy::needless_pass_by_value, reason = "Required by PyO3")]
|
||||
fn load_plugin_headers(&mut self, plugin_paths: Vec<PathBuf>) -> Result<(), VerboseError> {
|
||||
self.0.load_plugin_headers(&as_paths(&plugin_paths))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn clear_loaded_plugins(&mut self) {
|
||||
self.0.clear_loaded_plugins();
|
||||
}
|
||||
|
||||
fn plugin(&self, plugin_name: &str) -> Option<Plugin> {
|
||||
self.0.plugin(plugin_name).map(Into::into)
|
||||
}
|
||||
|
||||
fn loaded_plugins(&self) -> Vec<Plugin> {
|
||||
self.0
|
||||
.loaded_plugins()
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[expect(clippy::needless_pass_by_value, reason = "Required by PyO3")]
|
||||
fn sort_plugins(&self, plugin_names: Vec<String>) -> Result<Vec<String>, VerboseError> {
|
||||
Ok(self.0.sort_plugins(&as_strs(&plugin_names))?)
|
||||
}
|
||||
|
||||
fn load_current_load_order_state(&mut self) -> Result<(), VerboseError> {
|
||||
self.0.load_current_load_order_state()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn is_load_order_ambiguous(&self) -> Result<bool, VerboseError> {
|
||||
Ok(self.0.is_load_order_ambiguous()?)
|
||||
}
|
||||
|
||||
fn active_plugins_file_path(&self) -> &PathBuf {
|
||||
self.0.active_plugins_file_path()
|
||||
}
|
||||
|
||||
fn is_plugin_active(&self, plugin_name: &str) -> bool {
|
||||
self.0.is_plugin_active(plugin_name)
|
||||
}
|
||||
|
||||
fn load_order(&self) -> Vec<&str> {
|
||||
self.0.load_order()
|
||||
}
|
||||
|
||||
#[expect(clippy::needless_pass_by_value, reason = "Required by PyO3")]
|
||||
fn set_load_order(&mut self, load_order: Vec<String>) -> Result<(), VerboseError> {
|
||||
self.0.set_load_order(&as_strs(&load_order))?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn as_paths(pathbufs: &[PathBuf]) -> Vec<&Path> {
|
||||
pathbufs.iter().map(PathBuf::as_ref).collect()
|
||||
}
|
||||
|
||||
fn as_strs(strings: &[String]) -> Vec<&str> {
|
||||
strings.iter().map(String::as_ref).collect()
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
// Deny some rustc lints that are allow-by-default.
|
||||
#![deny(
|
||||
ambiguous_negative_literals,
|
||||
impl_trait_overcaptures,
|
||||
let_underscore_drop,
|
||||
missing_copy_implementations,
|
||||
missing_debug_implementations,
|
||||
non_ascii_idents,
|
||||
redundant_imports,
|
||||
redundant_lifetimes,
|
||||
trivial_casts,
|
||||
trivial_numeric_casts,
|
||||
unit_bindings,
|
||||
unreachable_pub,
|
||||
unsafe_code
|
||||
)]
|
||||
#![deny(clippy::pedantic)]
|
||||
// Selectively deny clippy restriction lints.
|
||||
#![deny(
|
||||
clippy::allow_attributes,
|
||||
clippy::as_conversions,
|
||||
clippy::as_underscore,
|
||||
clippy::assertions_on_result_states,
|
||||
clippy::big_endian_bytes,
|
||||
clippy::cfg_not_test,
|
||||
clippy::clone_on_ref_ptr,
|
||||
clippy::create_dir,
|
||||
clippy::dbg_macro,
|
||||
clippy::decimal_literal_representation,
|
||||
clippy::default_numeric_fallback,
|
||||
clippy::doc_include_without_cfg,
|
||||
clippy::empty_drop,
|
||||
clippy::error_impl_error,
|
||||
clippy::exit,
|
||||
clippy::exhaustive_enums,
|
||||
clippy::expect_used,
|
||||
clippy::filetype_is_file,
|
||||
clippy::float_cmp_const,
|
||||
clippy::fn_to_numeric_cast_any,
|
||||
clippy::get_unwrap,
|
||||
clippy::host_endian_bytes,
|
||||
clippy::if_then_some_else_none,
|
||||
clippy::indexing_slicing,
|
||||
clippy::infinite_loop,
|
||||
clippy::integer_division,
|
||||
clippy::integer_division_remainder_used,
|
||||
clippy::iter_over_hash_type,
|
||||
clippy::let_underscore_must_use,
|
||||
clippy::lossy_float_literal,
|
||||
clippy::map_err_ignore,
|
||||
clippy::map_with_unused_argument_over_ranges,
|
||||
clippy::mem_forget,
|
||||
clippy::missing_assert_message,
|
||||
clippy::missing_asserts_for_indexing,
|
||||
clippy::mixed_read_write_in_expression,
|
||||
clippy::multiple_inherent_impl,
|
||||
clippy::multiple_unsafe_ops_per_block,
|
||||
clippy::mutex_atomic,
|
||||
clippy::mutex_integer,
|
||||
clippy::needless_raw_strings,
|
||||
clippy::non_ascii_literal,
|
||||
clippy::non_zero_suggestions,
|
||||
clippy::panic,
|
||||
clippy::panic_in_result_fn,
|
||||
clippy::partial_pub_fields,
|
||||
clippy::pathbuf_init_then_push,
|
||||
clippy::precedence_bits,
|
||||
clippy::print_stderr,
|
||||
clippy::print_stdout,
|
||||
clippy::rc_buffer,
|
||||
clippy::rc_mutex,
|
||||
clippy::redundant_type_annotations,
|
||||
clippy::ref_patterns,
|
||||
clippy::rest_pat_in_fully_bound_structs,
|
||||
clippy::str_to_string,
|
||||
clippy::string_lit_chars_any,
|
||||
clippy::string_slice,
|
||||
clippy::string_to_string,
|
||||
clippy::suspicious_xor_used_as_pow,
|
||||
clippy::tests_outside_test_module,
|
||||
clippy::todo,
|
||||
clippy::try_err,
|
||||
clippy::undocumented_unsafe_blocks,
|
||||
clippy::unimplemented,
|
||||
clippy::unnecessary_safety_comment,
|
||||
clippy::unneeded_field_pattern,
|
||||
clippy::unreachable,
|
||||
clippy::unused_result_ok,
|
||||
clippy::unwrap_in_result,
|
||||
clippy::unwrap_used,
|
||||
clippy::use_debug,
|
||||
clippy::verbose_file_reads,
|
||||
clippy::wildcard_enum_match_arm
|
||||
)]
|
||||
|
||||
mod database;
|
||||
mod error;
|
||||
mod game;
|
||||
mod metadata;
|
||||
mod plugin;
|
||||
|
||||
use database::{Database, EdgeType, Vertex};
|
||||
use game::{Game, GameType};
|
||||
use metadata::{
|
||||
File, Filename, Group, Location, Message, MessageContent, MessageType, PluginCleaningData,
|
||||
PluginMetadata, Tag, TagSuggestion, select_message_content,
|
||||
};
|
||||
use plugin::Plugin;
|
||||
use pyo3::{create_exception, exceptions::PyException, prelude::*};
|
||||
|
||||
#[pyfunction]
|
||||
fn is_compatible(major: u32, minor: u32, patch: u32) -> bool {
|
||||
libloot::is_compatible(major, minor, patch)
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn libloot_revision() -> String {
|
||||
libloot::libloot_revision()
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn libloot_version() -> String {
|
||||
libloot::libloot_version()
|
||||
}
|
||||
|
||||
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(py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
|
||||
pyo3_log::init();
|
||||
|
||||
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)?;
|
||||
|
||||
m.add_function(wrap_pyfunction!(is_compatible, m)?)?;
|
||||
m.add_function(wrap_pyfunction!(libloot_revision, m)?)?;
|
||||
m.add_function(wrap_pyfunction!(libloot_version, m)?)?;
|
||||
|
||||
m.add_function(wrap_pyfunction!(select_message_content, m)?)?;
|
||||
|
||||
m.add_class::<Vertex>()?;
|
||||
m.add_class::<EdgeType>()?;
|
||||
m.add_class::<GameType>()?;
|
||||
m.add_class::<Game>()?;
|
||||
m.add_class::<Database>()?;
|
||||
m.add_class::<Plugin>()?;
|
||||
m.add_class::<Group>()?;
|
||||
m.add_class::<MessageContent>()?;
|
||||
m.add_class::<MessageType>()?;
|
||||
m.add_class::<Message>()?;
|
||||
m.add_class::<File>()?;
|
||||
m.add_class::<Filename>()?;
|
||||
m.add_class::<PluginCleaningData>()?;
|
||||
m.add_class::<Tag>()?;
|
||||
m.add_class::<TagSuggestion>()?;
|
||||
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(())
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,87 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use pyo3::{pyclass, pymethods};
|
||||
|
||||
use crate::error::VerboseError;
|
||||
|
||||
#[pyclass(eq, frozen)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
#[repr(transparent)]
|
||||
pub struct Plugin(Arc<libloot::Plugin>);
|
||||
|
||||
#[pymethods]
|
||||
impl Plugin {
|
||||
fn name(&self) -> &str {
|
||||
self.0.name()
|
||||
}
|
||||
|
||||
fn header_version(&self) -> Option<f32> {
|
||||
self.0.header_version()
|
||||
}
|
||||
|
||||
fn version(&self) -> Option<&str> {
|
||||
self.0.version()
|
||||
}
|
||||
|
||||
fn masters(&self) -> Result<Vec<String>, VerboseError> {
|
||||
Ok(self.0.masters()?)
|
||||
}
|
||||
|
||||
fn bash_tags(&self) -> &[String] {
|
||||
self.0.bash_tags()
|
||||
}
|
||||
|
||||
fn crc(&self) -> Option<u32> {
|
||||
self.0.crc()
|
||||
}
|
||||
|
||||
fn is_master(&self) -> bool {
|
||||
self.0.is_master()
|
||||
}
|
||||
|
||||
fn is_light_plugin(&self) -> bool {
|
||||
self.0.is_light_plugin()
|
||||
}
|
||||
|
||||
fn is_medium_plugin(&self) -> bool {
|
||||
self.0.is_medium_plugin()
|
||||
}
|
||||
|
||||
fn is_update_plugin(&self) -> bool {
|
||||
self.0.is_update_plugin()
|
||||
}
|
||||
|
||||
fn is_blueprint_plugin(&self) -> bool {
|
||||
self.0.is_blueprint_plugin()
|
||||
}
|
||||
|
||||
fn is_valid_as_light_plugin(&self) -> Result<bool, VerboseError> {
|
||||
Ok(self.0.is_valid_as_light_plugin()?)
|
||||
}
|
||||
|
||||
fn is_valid_as_medium_plugin(&self) -> Result<bool, VerboseError> {
|
||||
Ok(self.0.is_valid_as_medium_plugin()?)
|
||||
}
|
||||
|
||||
fn is_valid_as_update_plugin(&self) -> Result<bool, VerboseError> {
|
||||
Ok(self.0.is_valid_as_update_plugin()?)
|
||||
}
|
||||
|
||||
fn is_empty(&self) -> bool {
|
||||
self.0.is_empty()
|
||||
}
|
||||
|
||||
fn loads_archive(&self) -> bool {
|
||||
self.0.loads_archive()
|
||||
}
|
||||
|
||||
fn do_records_overlap(&self, plugin: &Self) -> Result<bool, VerboseError> {
|
||||
Ok(self.0.do_records_overlap(&plugin.0)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Arc<libloot::Plugin>> for Plugin {
|
||||
fn from(value: Arc<libloot::Plugin>) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user