Deny various rustc and clippy lints in Python wrapper

This commit is contained in:
Oliver Hamlet
2025-04-23 00:16:09 +01:00
parent 0fd0f94c2a
commit b93a83aa3d
5 changed files with 176 additions and 64 deletions
+23 -21
View File
@@ -22,14 +22,16 @@ 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)?
.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,
@@ -37,19 +39,21 @@ impl Database {
) -> Result<(), VerboseError> {
self.0
.write()
.map_err(|_| DatabaseLockPoisonError)?
.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)?
.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,
@@ -63,11 +67,12 @@ impl Database {
self.0
.read()
.map_err(|_| DatabaseLockPoisonError)?
.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,
@@ -81,7 +86,7 @@ impl Database {
self.0
.read()
.map_err(|_| DatabaseLockPoisonError)?
.map_err(DatabaseLockPoisonError::from)?
.write_minimal_list(&output_path, write_mode)
.map_err(Into::into)
}
@@ -89,7 +94,7 @@ impl Database {
pub fn evaluate(&self, condition: &str) -> Result<bool, VerboseError> {
self.0
.read()
.map_err(|_| DatabaseLockPoisonError)?
.map_err(DatabaseLockPoisonError::from)?
.evaluate(condition)
.map_err(Into::into)
}
@@ -98,7 +103,7 @@ impl Database {
Ok(self
.0
.read()
.map_err(|_| DatabaseLockPoisonError)?
.map_err(DatabaseLockPoisonError::from)?
.known_bash_tags())
}
@@ -108,7 +113,7 @@ impl Database {
) -> Result<Vec<Message>, VerboseError> {
self.0
.write()
.map_err(|_| DatabaseLockPoisonError)?
.map_err(DatabaseLockPoisonError::from)?
.general_messages(evaluate_conditions)
.map(|v| v.into_iter().map(Into::into).collect())
.map_err(Into::into)
@@ -118,7 +123,7 @@ impl Database {
Ok(self
.0
.read()
.map_err(|_| DatabaseLockPoisonError)?
.map_err(DatabaseLockPoisonError::from)?
.groups(include_user_metadata)
.into_iter()
.map(Into::into)
@@ -129,7 +134,7 @@ impl Database {
Ok(self
.0
.read()
.map_err(|_| DatabaseLockPoisonError)?
.map_err(DatabaseLockPoisonError::from)?
.user_groups()
.iter()
.cloned()
@@ -141,7 +146,7 @@ impl Database {
let groups = groups.into_iter().map(Into::into).collect();
self.0
.write()
.map_err(|_| DatabaseLockPoisonError)?
.map_err(DatabaseLockPoisonError::from)?
.set_user_groups(groups);
Ok(())
}
@@ -153,7 +158,7 @@ impl Database {
) -> Result<Vec<Vertex>, VerboseError> {
self.0
.read()
.map_err(|_| DatabaseLockPoisonError)?
.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)
@@ -167,7 +172,7 @@ impl Database {
) -> Result<Option<PluginMetadata>, VerboseError> {
self.0
.read()
.map_err(|_| DatabaseLockPoisonError)?
.map_err(DatabaseLockPoisonError::from)?
.plugin_metadata(plugin_name, include_user_metadata, evaluate_conditions)
.map(|p| p.map(Into::into))
.map_err(Into::into)
@@ -180,7 +185,7 @@ impl Database {
) -> Result<Option<PluginMetadata>, VerboseError> {
self.0
.read()
.map_err(|_| DatabaseLockPoisonError)?
.map_err(DatabaseLockPoisonError::from)?
.plugin_user_metadata(plugin_name, evaluate_conditions)
.map(|p| p.map(Into::into))
.map_err(Into::into)
@@ -192,7 +197,7 @@ impl Database {
) -> Result<(), VerboseError> {
self.0
.write()
.map_err(|_| DatabaseLockPoisonError)?
.map_err(DatabaseLockPoisonError::from)?
.set_plugin_user_metadata(plugin_metadata.into());
Ok(())
}
@@ -200,7 +205,7 @@ impl Database {
pub fn discard_plugin_user_metadata(&self, plugin: &str) -> Result<(), VerboseError> {
self.0
.write()
.map_err(|_| DatabaseLockPoisonError)?
.map_err(DatabaseLockPoisonError::from)?
.discard_plugin_user_metadata(plugin);
Ok(())
}
@@ -208,7 +213,7 @@ impl Database {
pub fn discard_all_user_metadata(&self) -> Result<(), VerboseError> {
self.0
.write()
.map_err(|_| DatabaseLockPoisonError)?
.map_err(DatabaseLockPoisonError::from)?
.discard_all_user_metadata();
Ok(())
}
@@ -259,10 +264,7 @@ impl Vertex {
"{}({}, {})",
class_name,
inner.name(),
inner
.out_edge_type()
.map(repr_edge_type)
.unwrap_or(NONE_REPR),
inner.out_edge_type().map_or(NONE_REPR, repr_edge_type),
))
}
+9 -5
View File
@@ -48,7 +48,7 @@ impl From<GameHandleCreationError> for VerboseError {
fn from(value: GameHandleCreationError) -> Self {
match value {
GameHandleCreationError::LoadOrderError(e) => e.into(),
_ => Self::Other(Box::new(value)),
GameHandleCreationError::NotADirectory(_) | _ => Self::Other(Box::new(value)),
}
}
}
@@ -60,7 +60,11 @@ impl From<SortPluginsError> for VerboseError {
SortPluginsError::UndefinedGroup(g) => Self::UndefinedGroupError(g),
SortPluginsError::CycleFound(cycle) => Self::CyclicInteractionError(cycle),
SortPluginsError::PluginDataError(e) => e.into(),
_ => Self::Other(Box::new(value)),
SortPluginsError::DatabaseLockPoisoned
| SortPluginsError::PluginNotLoaded(_)
| SortPluginsError::CycleFoundInvolving(_)
| SortPluginsError::PathfindingError(_)
| _ => Self::Other(Box::new(value)),
}
}
}
@@ -69,7 +73,7 @@ impl From<LoadOrderStateError> for VerboseError {
fn from(value: LoadOrderStateError) -> Self {
match value {
LoadOrderStateError::LoadOrderError(e) => e.into(),
_ => Self::Other(Box::new(value)),
LoadOrderStateError::DatabaseLockPoisoned | _ => Self::Other(Box::new(value)),
}
}
}
@@ -79,7 +83,7 @@ impl From<GroupsPathError> for VerboseError {
match value {
GroupsPathError::UndefinedGroup(g) => Self::UndefinedGroupError(g),
GroupsPathError::CycleFound(cycle) => Self::CyclicInteractionError(cycle),
_ => Self::Other(Box::new(value)),
GroupsPathError::PathfindingError(_) => Self::Other(Box::new(value)),
}
}
}
@@ -88,7 +92,7 @@ impl From<MetadataRetrievalError> for VerboseError {
fn from(value: MetadataRetrievalError) -> Self {
match value {
MetadataRetrievalError::ConditionEvaluationError(e) => e.into(),
_ => Self::Other(Box::new(value)),
MetadataRetrievalError::RegexError(_) => Self::Other(Box::new(value)),
}
}
}
+40 -34
View File
@@ -5,21 +5,20 @@ use pyo3::{pyclass, pymethods};
use crate::{database::Database, error::VerboseError, plugin::Plugin};
#[allow(non_camel_case_types)]
#[pyclass(eq, frozen, hash, ord)]
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum GameType {
tes4,
tes5,
fo3,
fonv,
fo4,
tes5se,
fo4vr,
tes5vr,
tes3,
starfield,
openmw,
Oblivion,
Skyrim,
Fallout3,
FalloutNV,
Fallout4,
SkyrimSE,
Fallout4VR,
SkyrimVR,
Morrowind,
Starfield,
OpenMW,
}
impl TryFrom<libloot::GameType> for GameType {
@@ -27,17 +26,17 @@ impl TryFrom<libloot::GameType> for GameType {
fn try_from(value: libloot::GameType) -> Result<Self, Self::Error> {
match value {
libloot::GameType::TES4 => Ok(GameType::tes4),
libloot::GameType::TES5 => Ok(GameType::tes5),
libloot::GameType::FO3 => Ok(GameType::fo3),
libloot::GameType::FONV => Ok(GameType::fonv),
libloot::GameType::FO4 => Ok(GameType::fo4),
libloot::GameType::TES5SE => Ok(GameType::tes5se),
libloot::GameType::FO4VR => Ok(GameType::fo4vr),
libloot::GameType::TES5VR => Ok(GameType::tes5vr),
libloot::GameType::TES3 => Ok(GameType::tes3),
libloot::GameType::Starfield => Ok(GameType::starfield),
libloot::GameType::OpenMW => Ok(GameType::openmw),
libloot::GameType::TES4 => Ok(GameType::Oblivion),
libloot::GameType::TES5 => Ok(GameType::Skyrim),
libloot::GameType::FO3 => Ok(GameType::Fallout3),
libloot::GameType::FONV => Ok(GameType::FalloutNV),
libloot::GameType::FO4 => Ok(GameType::Fallout4),
libloot::GameType::TES5SE => Ok(GameType::SkyrimSE),
libloot::GameType::FO4VR => Ok(GameType::Fallout4VR),
libloot::GameType::TES5VR => Ok(GameType::SkyrimVR),
libloot::GameType::TES3 => Ok(GameType::Morrowind),
libloot::GameType::Starfield => Ok(GameType::Starfield),
libloot::GameType::OpenMW => Ok(GameType::OpenMW),
_ => Err(UnsupportedEnumValueError),
}
}
@@ -48,17 +47,17 @@ impl TryFrom<GameType> for libloot::GameType {
fn try_from(value: GameType) -> Result<Self, Self::Error> {
match value {
GameType::tes4 => Ok(libloot::GameType::TES4),
GameType::tes5 => Ok(libloot::GameType::TES5),
GameType::fo3 => Ok(libloot::GameType::FO3),
GameType::fonv => Ok(libloot::GameType::FONV),
GameType::fo4 => Ok(libloot::GameType::FO4),
GameType::tes5se => Ok(libloot::GameType::TES5SE),
GameType::fo4vr => Ok(libloot::GameType::FO4VR),
GameType::tes5vr => Ok(libloot::GameType::TES5VR),
GameType::tes3 => Ok(libloot::GameType::TES3),
GameType::starfield => Ok(libloot::GameType::Starfield),
GameType::openmw => Ok(libloot::GameType::OpenMW),
GameType::Oblivion => Ok(libloot::GameType::TES4),
GameType::Skyrim => Ok(libloot::GameType::TES5),
GameType::Fallout3 => Ok(libloot::GameType::FO3),
GameType::FalloutNV => Ok(libloot::GameType::FONV),
GameType::Fallout4 => Ok(libloot::GameType::FO4),
GameType::SkyrimSE => Ok(libloot::GameType::TES5SE),
GameType::Fallout4VR => Ok(libloot::GameType::FO4VR),
GameType::SkyrimVR => Ok(libloot::GameType::TES5VR),
GameType::Morrowind => Ok(libloot::GameType::TES3),
GameType::Starfield => Ok(libloot::GameType::Starfield),
GameType::OpenMW => Ok(libloot::GameType::OpenMW),
}
}
}
@@ -71,6 +70,7 @@ pub struct Game(libloot::Game);
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,
@@ -94,6 +94,7 @@ impl Game {
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(())
@@ -103,15 +104,18 @@ impl Game {
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(())
@@ -133,6 +137,7 @@ impl Game {
.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))?)
}
@@ -158,6 +163,7 @@ impl Game {
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(())
+96
View File
@@ -1,3 +1,99 @@
// 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::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;
+8 -4
View File
@@ -8,7 +8,7 @@ use pyo3::{
use crate::error::VerboseError;
pub const NONE_REPR: &str = "None";
pub(crate) const NONE_REPR: &str = "None";
#[pyclass(eq, ord, str = "{0:?}")]
#[repr(transparent)]
@@ -171,6 +171,10 @@ fn repr_message_contents(contents: &[libloot::metadata::MessageContent]) -> Stri
.join(",")
}
#[expect(
unreachable_pub,
reason = "It's exported by PyO3, so while the pub isn't necessary, it's misleading to not have it"
)]
#[pyfunction]
pub fn select_message_content(
content: Vec<MessageContent>,
@@ -745,8 +749,8 @@ pub struct PluginMetadata(libloot::metadata::PluginMetadata);
#[pymethods]
impl PluginMetadata {
#[new]
fn new(name: String) -> Result<Self, VerboseError> {
Ok(Self(libloot::metadata::PluginMetadata::new(&name)?))
fn new(name: &str) -> Result<Self, VerboseError> {
Ok(Self(libloot::metadata::PluginMetadata::new(name)?))
}
#[getter]
@@ -880,7 +884,7 @@ impl PluginMetadata {
self.0.set_locations(value);
}
fn merge_metadata(&mut self, other: PluginMetadata) {
fn merge_metadata(&mut self, other: &PluginMetadata) {
self.0.merge_metadata(&other.0);
}