diff --git a/pyo3/src/database.rs b/pyo3/src/database.rs index 99dc5ee4..2bd7d8a9 100644 --- a/pyo3/src/database.rs +++ b/pyo3/src/database.rs @@ -22,14 +22,16 @@ pub struct Database(Arc>); #[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 { 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, 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, 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, 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, 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), )) } diff --git a/pyo3/src/error.rs b/pyo3/src/error.rs index ff48b6b9..b8d0b0ab 100644 --- a/pyo3/src/error.rs +++ b/pyo3/src/error.rs @@ -48,7 +48,7 @@ impl From 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 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 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 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 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)), } } } diff --git a/pyo3/src/game.rs b/pyo3/src/game.rs index 484ca47d..462e53fe 100644 --- a/pyo3/src/game.rs +++ b/pyo3/src/game.rs @@ -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 for GameType { @@ -27,17 +26,17 @@ impl TryFrom for GameType { fn try_from(value: libloot::GameType) -> Result { 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 for libloot::GameType { fn try_from(value: GameType) -> Result { 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) -> 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) -> 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) -> 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) -> Result, 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) -> Result<(), VerboseError> { self.0.set_load_order(&as_strs(&load_order))?; Ok(()) diff --git a/pyo3/src/lib.rs b/pyo3/src/lib.rs index ab6ef8a6..27c25a50 100644 --- a/pyo3/src/lib.rs +++ b/pyo3/src/lib.rs @@ -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; diff --git a/pyo3/src/metadata.rs b/pyo3/src/metadata.rs index 5a55b9c2..c5b2539e 100644 --- a/pyo3/src/metadata.rs +++ b/pyo3/src/metadata.rs @@ -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, @@ -745,8 +749,8 @@ pub struct PluginMetadata(libloot::metadata::PluginMetadata); #[pymethods] impl PluginMetadata { #[new] - fn new(name: String) -> Result { - Ok(Self(libloot::metadata::PluginMetadata::new(&name)?)) + fn new(name: &str) -> Result { + 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); }