From 96b2ed23730361591800ca97069e468183e42c26 Mon Sep 17 00:00:00 2001 From: Oliver Hamlet Date: Tue, 30 Dec 2025 11:12:10 +0000 Subject: [PATCH] Add include_user_metadata parameter to Database::general_messages() The C++ wrapper defaults it to true, so the behaviour hasn't changed for existing callers that don't pass a value for the evaluateConditions parameter, i.e. that call GetGeneralMessages(). Since the new parameter comes before evaluateConditions, existing GetGeneralMessages(true) and GetGeneralMessages(false) calls need to be replaced with GetGeneralMessages(true, true) and GetGeneralMessages(true, false) to retain their existing behaviour. The parameter order was chosen to match that of GetPluginMetadata(), at the cost of this backwards incompatibility. --- cpp/include/loot/database_interface.h | 5 ++ cpp/src/api/database.cpp | 4 +- cpp/src/api/database.h | 1 + cpp/src/database.rs | 6 +- cpp/src/lib.rs | 6 +- .../api/interface/database_interface_test.h | 20 ++++++- nodejs/src/database.rs | 3 +- python/src/database.rs | 6 +- src/database/mod.rs | 58 +++++++++++++++---- 9 files changed, 92 insertions(+), 17 deletions(-) diff --git a/cpp/include/loot/database_interface.h b/cpp/include/loot/database_interface.h index 05502b9f..3a8810ca 100644 --- a/cpp/include/loot/database_interface.h +++ b/cpp/include/loot/database_interface.h @@ -167,6 +167,10 @@ public: /** * @brief Get all general messages listed in the loaded metadata lists. + * @param includeUserMetadata + * If true, any general messages present in the userlist are included + * in the returned metadata, otherwise the metadata returned only + * includes metadata from the masterlist. * @param evaluateConditions * If true, any metadata conditions are evaluated before the metadata * is returned, otherwise unevaluated metadata is returned. Evaluating @@ -176,6 +180,7 @@ public: * to any particular plugin. */ virtual std::vector GetGeneralMessages( + bool includeUserMetadata = true, bool evaluateConditions = false) const = 0; /** diff --git a/cpp/src/api/database.cpp b/cpp/src/api/database.cpp index c930d2b7..34f379c1 100644 --- a/cpp/src/api/database.cpp +++ b/cpp/src/api/database.cpp @@ -87,9 +87,11 @@ void Database::SetUserKnownBashTags(const std::vector& bashTags) { } std::vector Database::GetGeneralMessages( + bool includeUserMetadata, bool evaluateConditions) const { try { - return convert(database_->general_messages(evaluateConditions)); + return convert( + database_->general_messages(includeUserMetadata, evaluateConditions)); } catch (const ::rust::Error& e) { std::rethrow_exception(mapError(e)); } diff --git a/cpp/src/api/database.h b/cpp/src/api/database.h index 37d3229c..9ca97f48 100644 --- a/cpp/src/api/database.h +++ b/cpp/src/api/database.h @@ -36,6 +36,7 @@ public: void SetUserKnownBashTags(const std::vector& bashTags) override; std::vector GetGeneralMessages( + bool includeUserMetadata = true, bool evaluateConditions = false) const override; std::vector GetUserGeneralMessages( diff --git a/cpp/src/database.rs b/cpp/src/database.rs index 3825048b..0bffed41 100644 --- a/cpp/src/database.rs +++ b/cpp/src/database.rs @@ -134,12 +134,16 @@ impl Database { pub fn general_messages( &self, + include_user_metadata: bool, evaluate_conditions: bool, ) -> Result, VerboseError> { self.0 .read() .map_err(DatabaseLockPoisonError::from)? - .general_messages(to_eval_mode(evaluate_conditions)) + .general_messages( + to_merge_mode(include_user_metadata), + to_eval_mode(evaluate_conditions), + ) .map(|v| v.into_iter().map(Into::into).collect()) .map_err(Into::into) } diff --git a/cpp/src/lib.rs b/cpp/src/lib.rs index 5baa8aa4..88859e66 100644 --- a/cpp/src/lib.rs +++ b/cpp/src/lib.rs @@ -274,7 +274,11 @@ mod ffi { pub fn set_user_known_bash_tags(&self, bash_tags: Vec) -> Result<()>; - pub fn general_messages(&self, evaluate_conditions: bool) -> Result>; + pub fn general_messages( + &self, + include_user_metadata: bool, + evaluate_conditions: bool, + ) -> Result>; pub fn user_general_messages(&self, evaluate_conditions: bool) -> Result>; diff --git a/cpp/src/tests/api/interface/database_interface_test.h b/cpp/src/tests/api/interface/database_interface_test.h index 95a14863..2cadb6ae 100644 --- a/cpp/src/tests/api/interface/database_interface_test.h +++ b/cpp/src/tests/api/interface/database_interface_test.h @@ -555,13 +555,31 @@ TEST_P(DatabaseInterfaceTest, EXPECT_EQ(expectedMessages, messages); } +TEST_P( + DatabaseInterfaceTest, + getGeneralMessagesShouldGetGeneralMessagesFromTheMasterlistOnlyWhenFirstParamIsFalse) { + ASSERT_NO_THROW(GenerateMasterlist()); + ASSERT_NO_THROW(GenerateUserlist()); + ASSERT_NO_THROW(handle_->GetDatabase().LoadMasterlist(masterlistPath)); + ASSERT_NO_THROW(handle_->GetDatabase().LoadUserlist(userlistPath_)); + + auto messages = handle_->GetDatabase().GetGeneralMessages(false); + + std::vector expectedMessages({ + Message(MessageType::say, + generalMasterlistMessage, + "file(\"" + missingEsp + "\")"), + }); + EXPECT_EQ(expectedMessages, messages); +} + TEST_P( DatabaseInterfaceTest, getGeneralMessagesShouldReturnOnlyValidMessagesIfConditionsAreEvaluated) { ASSERT_NO_THROW(GenerateMasterlist()); ASSERT_NO_THROW(handle_->GetDatabase().LoadMasterlist(masterlistPath)); - auto messages = handle_->GetDatabase().GetGeneralMessages(true); + auto messages = handle_->GetDatabase().GetGeneralMessages(true, true); EXPECT_TRUE(messages.empty()); } diff --git a/nodejs/src/database.rs b/nodejs/src/database.rs index 2c43f7bc..f7993914 100644 --- a/nodejs/src/database.rs +++ b/nodejs/src/database.rs @@ -173,12 +173,13 @@ impl Database { #[napi] pub fn general_messages( &self, + include_user_metadata: MergeMode, evaluate_conditions: EvalMode, ) -> Result, VerboseError> { self.0 .read() .map_err(DatabaseLockPoisonError::from)? - .general_messages(evaluate_conditions.into()) + .general_messages(include_user_metadata.into(), evaluate_conditions.into()) .map(|v| v.into_iter().map(Into::into).collect()) .map_err(Into::into) } diff --git a/python/src/database.rs b/python/src/database.rs index 5e3ce5a3..41495635 100644 --- a/python/src/database.rs +++ b/python/src/database.rs @@ -138,12 +138,16 @@ impl Database { pub fn general_messages( &self, + include_user_metadata: bool, evaluate_conditions: bool, ) -> Result, VerboseError> { self.0 .read() .map_err(DatabaseLockPoisonError::from)? - .general_messages(to_eval_mode(evaluate_conditions)) + .general_messages( + to_merge_mode(include_user_metadata), + to_eval_mode(evaluate_conditions), + ) .map(|v| v.into_iter().map(Into::into).collect()) .map_err(Into::into) } diff --git a/src/database/mod.rs b/src/database/mod.rs index d7db1c1f..5641ffff 100644 --- a/src/database/mod.rs +++ b/src/database/mod.rs @@ -192,16 +192,24 @@ impl Database { /// Get all general messages listed in the loaded metadata lists. pub fn general_messages( &self, + include_user_metadata: MergeMode, evaluate_conditions: EvalMode, ) -> Result, ConditionEvaluationError> { - process_messages( - self.masterlist - .messages() - .iter() - .chain(self.userlist.messages()), - &self.condition_evaluator_state, - evaluate_conditions, - ) + let messages_iter = self.masterlist.messages().iter(); + + if include_user_metadata == MergeMode::WithUserMetadata { + process_messages( + messages_iter.chain(self.userlist.messages()), + &self.condition_evaluator_state, + evaluate_conditions, + ) + } else { + process_messages( + messages_iter, + &self.condition_evaluator_state, + evaluate_conditions, + ) + } } /// Get all general messages listed in the loaded userlist. @@ -1010,7 +1018,35 @@ plugins: use super::*; #[test] - fn should_append_userlist_messages_to_masterlist_messages() { + fn should_not_append_userlist_messages_to_masterlist_messages_if_merge_mode_is_without_user_metadata() + { + let fixture = Fixture::new(GameType::Oblivion); + let mut database = fixture.database(); + + database.load_masterlist(&fixture.metadata_path).unwrap(); + + let userlist_path = fixture.inner.local_path.join("userlist.yaml"); + std::fs::write( + &userlist_path, + "globals: [{type: say, content: 'A user message'}]", + ) + .unwrap(); + + database.load_userlist(&userlist_path).unwrap(); + + assert_eq!( + &[Message::new(MessageType::Say, "A general message".into()) + .with_condition("file(\"missing.esp\")".into()),], + database + .general_messages(MergeMode::WithoutUserMetadata, EvalMode::DoNotEvaluate) + .unwrap() + .as_slice() + ); + } + + #[test] + fn should_append_userlist_messages_to_masterlist_messages_if_merge_mode_is_with_user_metadata() + { let fixture = Fixture::new(GameType::Oblivion); let mut database = fixture.database(); @@ -1032,7 +1068,7 @@ plugins: Message::new(MessageType::Say, "A user message".into()) ], database - .general_messages(EvalMode::DoNotEvaluate) + .general_messages(MergeMode::WithUserMetadata, EvalMode::DoNotEvaluate) .unwrap() .as_slice() ); @@ -1057,7 +1093,7 @@ plugins: assert_eq!( &[Message::new(MessageType::Say, "A user message".into())], database - .general_messages(EvalMode::Evaluate) + .general_messages(MergeMode::WithUserMetadata, EvalMode::Evaluate) .unwrap() .as_slice() );