mirror of
https://github.com/loot/libloot.git
synced 2026-07-27 14:16:01 -07:00
Add condition field to plugin cleaning data metadata
This commit is contained in:
@@ -80,7 +80,8 @@ public:
|
||||
const std::vector<MessageContent>& detail,
|
||||
unsigned int itm,
|
||||
unsigned int ref,
|
||||
unsigned int nav);
|
||||
unsigned int nav,
|
||||
std::string_view condition);
|
||||
|
||||
/**
|
||||
* Get the CRC that identifies the plugin that the cleaning data is for.
|
||||
@@ -122,6 +123,12 @@ public:
|
||||
*/
|
||||
LOOT_API std::vector<MessageContent> GetDetail() const;
|
||||
|
||||
/**
|
||||
* Get the condition string.
|
||||
* @return The cleaning data's condition string.
|
||||
*/
|
||||
LOOT_API std::string GetCondition() const;
|
||||
|
||||
private:
|
||||
uint32_t crc_{0};
|
||||
unsigned int itm_{0};
|
||||
@@ -129,6 +136,7 @@ private:
|
||||
unsigned int nav_{0};
|
||||
std::string utility_;
|
||||
std::vector<MessageContent> detail_;
|
||||
std::string condition_;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -106,7 +106,8 @@ loot::PluginCleaningData convert(const loot::rust::PluginCleaningData& data) {
|
||||
convert<loot::MessageContent>(data.detail()),
|
||||
data.itm_count(),
|
||||
data.deleted_reference_count(),
|
||||
data.deleted_navmesh_count());
|
||||
data.deleted_navmesh_count(),
|
||||
convert(data.condition()));
|
||||
}
|
||||
|
||||
loot::Location convert(const loot::rust::Location& location) {
|
||||
@@ -221,7 +222,8 @@ loot::rust::MessageType convert(loot::MessageType messageType) {
|
||||
convert<loot::rust::MessageContent>(data.GetDetail()),
|
||||
data.GetITMCount(),
|
||||
data.GetDeletedReferenceCount(),
|
||||
data.GetDeletedNavmeshCount());
|
||||
data.GetDeletedNavmeshCount(),
|
||||
data.GetCondition());
|
||||
} catch (const ::rust::Error& e) {
|
||||
std::rethrow_exception(mapError(e));
|
||||
}
|
||||
|
||||
@@ -34,13 +34,15 @@ PluginCleaningData::PluginCleaningData(
|
||||
const std::vector<MessageContent>& detail,
|
||||
unsigned int itm,
|
||||
unsigned int ref,
|
||||
unsigned int nav) :
|
||||
unsigned int nav,
|
||||
std::string_view condition) :
|
||||
crc_(crc),
|
||||
itm_(itm),
|
||||
ref_(ref),
|
||||
nav_(nav),
|
||||
utility_(utility),
|
||||
detail_(detail) {}
|
||||
detail_(detail),
|
||||
condition_(condition) {}
|
||||
|
||||
uint32_t PluginCleaningData::GetCRC() const { return crc_; }
|
||||
|
||||
@@ -58,12 +60,15 @@ std::vector<MessageContent> PluginCleaningData::GetDetail() const {
|
||||
return detail_;
|
||||
}
|
||||
|
||||
std::string PluginCleaningData::GetCondition() const { return condition_; }
|
||||
|
||||
bool operator==(const PluginCleaningData& lhs, const PluginCleaningData& rhs) {
|
||||
return lhs.GetCRC() == rhs.GetCRC() &&
|
||||
lhs.GetITMCount() == rhs.GetITMCount() &&
|
||||
lhs.GetDeletedReferenceCount() == rhs.GetDeletedReferenceCount() &&
|
||||
lhs.GetDeletedNavmeshCount() == rhs.GetDeletedNavmeshCount() &&
|
||||
lhs.GetCleaningUtility() == rhs.GetCleaningUtility() &&
|
||||
lhs.GetCondition() == rhs.GetCondition() &&
|
||||
lhs.GetDetail() == rhs.GetDetail();
|
||||
}
|
||||
|
||||
@@ -112,6 +117,14 @@ bool operator<(const PluginCleaningData& lhs, const PluginCleaningData& rhs) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (lhs.GetCondition() < rhs.GetCondition()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (rhs.GetCondition() < lhs.GetCondition()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return lhs.GetDetail() < rhs.GetDetail();
|
||||
}
|
||||
|
||||
|
||||
@@ -612,6 +612,7 @@ mod ffi {
|
||||
itm_count: u32,
|
||||
deleted_reference_count: u32,
|
||||
deleted_navmesh_count: u32,
|
||||
condition: String,
|
||||
) -> Result<Box<PluginCleaningData>>;
|
||||
|
||||
pub fn crc(&self) -> u32;
|
||||
@@ -626,6 +627,8 @@ mod ffi {
|
||||
|
||||
pub fn detail(&self) -> &[MessageContent];
|
||||
|
||||
pub fn condition(&self) -> &str;
|
||||
|
||||
pub fn boxed_clone(&self) -> Box<PluginCleaningData>;
|
||||
}
|
||||
|
||||
|
||||
@@ -585,6 +585,7 @@ pub fn new_plugin_cleaning_data(
|
||||
itm_count: u32,
|
||||
deleted_reference_count: u32,
|
||||
deleted_navmesh_count: u32,
|
||||
condition: String,
|
||||
) -> Result<Box<PluginCleaningData>, VerboseError> {
|
||||
let mut data = libloot::metadata::PluginCleaningData::new(crc, cleaning_utility)
|
||||
.with_itm_count(itm_count)
|
||||
@@ -595,6 +596,10 @@ pub fn new_plugin_cleaning_data(
|
||||
data = data.with_detail(to_vec_of_unwrapped(detail))?;
|
||||
}
|
||||
|
||||
if !condition.is_empty() {
|
||||
data = data.with_condition(condition);
|
||||
}
|
||||
|
||||
Ok(Box::new(PluginCleaningData(data)))
|
||||
}
|
||||
|
||||
@@ -603,6 +608,10 @@ impl PluginCleaningData {
|
||||
MessageContent::wrap_slice(self.0.detail())
|
||||
}
|
||||
|
||||
pub fn condition(&self) -> &str {
|
||||
self.0.condition().unwrap_or("")
|
||||
}
|
||||
|
||||
pub fn boxed_clone(&self) -> Box<Self> {
|
||||
Box::new(Self(self.0.clone()))
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -205,8 +205,8 @@ TEST_F(PluginMetadataTest, mergeMetadataShouldMergeTags) {
|
||||
TEST_F(PluginMetadataTest, mergeMetadataShouldMergeDirtyInfoData) {
|
||||
PluginMetadata plugin1;
|
||||
PluginMetadata plugin2;
|
||||
PluginCleaningData info1(0x5, "utility", info_, 1, 2, 3);
|
||||
PluginCleaningData info2(0xA, "utility", info_, 1, 2, 3);
|
||||
PluginCleaningData info1(0x5, "utility", info_, 1, 2, 3, "condition");
|
||||
PluginCleaningData info2(0xA, "utility", info_, 1, 2, 3, "condition");
|
||||
|
||||
plugin1.SetDirtyInfo({info1});
|
||||
plugin2.SetDirtyInfo({info1, info2});
|
||||
@@ -314,7 +314,8 @@ TEST_F(PluginMetadataTest, hasNameOnlyShouldBeFalseIfTagsExist) {
|
||||
|
||||
TEST_F(PluginMetadataTest, hasNameOnlyShouldBeFalseIfDirtyInfoExists) {
|
||||
PluginMetadata plugin(blankEsp);
|
||||
plugin.SetDirtyInfo({PluginCleaningData(5, "utility", info_, 0, 1, 2)});
|
||||
plugin.SetDirtyInfo(
|
||||
{PluginCleaningData(5, "utility", info_, 0, 1, 2, "condition")});
|
||||
|
||||
EXPECT_FALSE(plugin.HasNameOnly());
|
||||
}
|
||||
|
||||
@@ -317,6 +317,7 @@ impl PluginCleaningData {
|
||||
deleted_reference_count: Option<u32>,
|
||||
deleted_navmesh_count: Option<u32>,
|
||||
detail: Option<Vec<&MessageContent>>,
|
||||
condition: Option<String>,
|
||||
) -> Result<Self, VerboseError> {
|
||||
let mut data = libloot::metadata::PluginCleaningData::new(crc, cleaning_utility);
|
||||
|
||||
@@ -337,6 +338,10 @@ impl PluginCleaningData {
|
||||
data = data.with_detail(detail)?;
|
||||
}
|
||||
|
||||
if let Some(condition) = condition {
|
||||
data = data.with_condition(condition);
|
||||
}
|
||||
|
||||
Ok(Self(data))
|
||||
}
|
||||
|
||||
@@ -369,6 +374,11 @@ impl PluginCleaningData {
|
||||
pub fn detail(&self) -> Vec<MessageContent> {
|
||||
self.0.detail().iter().cloned().map(Into::into).collect()
|
||||
}
|
||||
|
||||
#[napi(getter)]
|
||||
pub fn condition(&self) -> Option<&str> {
|
||||
self.0.condition()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<libloot::metadata::PluginCleaningData> for PluginCleaningData {
|
||||
|
||||
+14
-3
@@ -391,7 +391,7 @@ pub struct PluginCleaningData(libloot::metadata::PluginCleaningData);
|
||||
#[pymethods]
|
||||
impl PluginCleaningData {
|
||||
#[new]
|
||||
#[pyo3(signature = (crc, cleaning_utility, itm_count = None, deleted_reference_count = None, deleted_navmesh_count = None, detail = None))]
|
||||
#[pyo3(signature = (crc, cleaning_utility, itm_count = None, deleted_reference_count = None, deleted_navmesh_count = None, detail = None, condition = None))]
|
||||
fn new(
|
||||
crc: u32,
|
||||
cleaning_utility: String,
|
||||
@@ -399,6 +399,7 @@ impl PluginCleaningData {
|
||||
deleted_reference_count: Option<u32>,
|
||||
deleted_navmesh_count: Option<u32>,
|
||||
detail: Option<Vec<MessageContent>>,
|
||||
condition: Option<String>,
|
||||
) -> Result<Self, VerboseError> {
|
||||
let mut data = libloot::metadata::PluginCleaningData::new(crc, cleaning_utility);
|
||||
|
||||
@@ -419,6 +420,10 @@ impl PluginCleaningData {
|
||||
data = data.with_detail(detail)?;
|
||||
}
|
||||
|
||||
if let Some(condition) = condition {
|
||||
data = data.with_condition(condition);
|
||||
}
|
||||
|
||||
Ok(Self(data))
|
||||
}
|
||||
|
||||
@@ -452,18 +457,24 @@ impl PluginCleaningData {
|
||||
self.0.detail().iter().cloned().map(Into::into).collect()
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn condition(&self) -> Option<&str> {
|
||||
self.0.condition()
|
||||
}
|
||||
|
||||
fn __repr__(slf: &Bound<'_, Self>) -> PyResult<String> {
|
||||
let class_name = slf.get_type().qualname()?;
|
||||
let inner = &slf.borrow().0;
|
||||
Ok(format!(
|
||||
"{}({}, {}, {}, {}, {}, {})",
|
||||
"{}({}, {}, {}, {}, {}, {}, {})",
|
||||
class_name,
|
||||
inner.crc(),
|
||||
inner.cleaning_utility(),
|
||||
inner.itm_count(),
|
||||
inner.deleted_reference_count(),
|
||||
inner.deleted_navmesh_count(),
|
||||
repr_message_contents(inner.detail())
|
||||
repr_message_contents(inner.detail()),
|
||||
inner.condition().unwrap_or(NONE_REPR)
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,7 +107,11 @@ fn filter_cleaning_data_on_conditions(
|
||||
cleaning_info
|
||||
.iter()
|
||||
.filter_map(|i| {
|
||||
let condition = format!("checksum(\"{}\", {:08X})", plugin_name, i.crc());
|
||||
let condition = if let Some(c) = i.condition() {
|
||||
format!("checksum(\"{}\", {:08X}) and ({})", plugin_name, i.crc(), c)
|
||||
} else {
|
||||
format!("checksum(\"{}\", {:08X})", plugin_name, i.crc())
|
||||
};
|
||||
|
||||
filter_map_on_condition(i, Some(condition.as_str()), state)
|
||||
})
|
||||
@@ -152,7 +156,9 @@ mod tests {
|
||||
|
||||
let info1 = PluginCleaningData::new(0x374E_2A6F, "utility1".into());
|
||||
let info2 = PluginCleaningData::new(0xDEAD_BEEF, "utility2".into());
|
||||
plugin.set_dirty_info(vec![info1.clone(), info2.clone()]);
|
||||
let info3 = PluginCleaningData::new(0x374E_2A6F, "utility3".into())
|
||||
.with_condition(condition.clone());
|
||||
plugin.set_dirty_info(vec![info1.clone(), info2.clone(), info3.clone()]);
|
||||
plugin.set_clean_info(vec![info1.clone(), info2.clone()]);
|
||||
|
||||
let state = loot_condition_interpreter::State::new(
|
||||
|
||||
@@ -632,12 +632,20 @@ fn get_common_metadata_values<'a>(
|
||||
if !info.detail().is_empty() {
|
||||
message_contents_counts.increment_for(info.detail());
|
||||
}
|
||||
|
||||
if let Some(condition) = info.condition() {
|
||||
condition_counts.increment_for(condition);
|
||||
}
|
||||
}
|
||||
|
||||
for info in plugin.clean_info() {
|
||||
if !info.detail().is_empty() {
|
||||
message_contents_counts.increment_for(info.detail());
|
||||
}
|
||||
|
||||
if let Some(condition) = info.condition() {
|
||||
condition_counts.increment_for(condition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1468,6 +1476,7 @@ plugins:
|
||||
let condition1 = "file(\"test.txt\")";
|
||||
let condition2 = "file(\"other.txt\")";
|
||||
let condition3 = "file(\"third.txt\")";
|
||||
let condition4 = "file(\"fourth.txt\")";
|
||||
let contents1 = vec![MessageContent::new("message text 1".to_owned())];
|
||||
let contents2 = vec![
|
||||
MessageContent::new("message text 1".to_owned()).with_language("en".to_owned()),
|
||||
@@ -1502,10 +1511,12 @@ plugins:
|
||||
|
||||
let info1 = PluginCleaningData::new(0xDEAD_BEEF, "utility".to_owned())
|
||||
.with_detail(contents2.clone())
|
||||
.unwrap();
|
||||
.unwrap()
|
||||
.with_condition(condition4.to_owned());
|
||||
let info2 = PluginCleaningData::new(0xDEAD_BEEF, "utility".to_owned())
|
||||
.with_detail(contents3.clone())
|
||||
.unwrap();
|
||||
.unwrap()
|
||||
.with_condition(condition4.to_owned());
|
||||
|
||||
plugin.set_load_after_files(vec![file1.clone()]);
|
||||
plugin.set_requirements(vec![file1.clone(), file2.clone()]);
|
||||
@@ -1583,10 +1594,12 @@ plugins:
|
||||
- crc: 0xDEADBEEF
|
||||
util: 'utility'
|
||||
detail: *contents2
|
||||
condition: &condition4 'file(\"fourth.txt\")'
|
||||
clean:
|
||||
- crc: 0xDEADBEEF
|
||||
util: 'utility'
|
||||
detail: &contents3 'message text 3'
|
||||
condition: *condition4
|
||||
|
||||
- name: 'test2.esp'
|
||||
after:
|
||||
@@ -1641,6 +1654,8 @@ plugins:
|
||||
|
||||
- &condition3 'file(\"third.txt\")'
|
||||
|
||||
- &condition4 'file(\"fourth.txt\")'
|
||||
|
||||
- &message1
|
||||
type: say
|
||||
content: *contents2
|
||||
@@ -1682,10 +1697,12 @@ plugins:
|
||||
- crc: 0xDEADBEEF
|
||||
util: 'utility'
|
||||
detail: *contents2
|
||||
condition: *condition4
|
||||
clean:
|
||||
- crc: 0xDEADBEEF
|
||||
util: 'utility'
|
||||
detail: *contents3
|
||||
condition: *condition4
|
||||
|
||||
- name: 'test2.esp'
|
||||
after:
|
||||
@@ -1738,6 +1755,8 @@ plugins:
|
||||
|
||||
- &condition3 'file(\"third.txt\")'
|
||||
|
||||
- &condition4 'file(\"fourth.txt\")'
|
||||
|
||||
- &message1
|
||||
type: say
|
||||
content: *contents2
|
||||
@@ -1777,10 +1796,12 @@ plugins:
|
||||
- crc: 0xDEADBEEF
|
||||
util: 'utility'
|
||||
detail: *contents2
|
||||
condition: *condition4
|
||||
clean:
|
||||
- crc: 0xDEADBEEF
|
||||
util: 'utility'
|
||||
detail: *contents3
|
||||
condition: *condition4
|
||||
|
||||
- name: 'test2.esp'
|
||||
after:
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
use saphyr::MarkedYaml;
|
||||
|
||||
use crate::metadata::yaml::parse_condition;
|
||||
|
||||
use super::{
|
||||
error::{MultilingualMessageContentsError, ParseMetadataError},
|
||||
message::{
|
||||
@@ -22,6 +24,7 @@ pub struct PluginCleaningData {
|
||||
deleted_navmesh_count: u32,
|
||||
cleaning_utility: Box<str>,
|
||||
detail: Box<[MessageContent]>,
|
||||
condition: Option<Box<str>>,
|
||||
}
|
||||
|
||||
impl PluginCleaningData {
|
||||
@@ -69,6 +72,13 @@ impl PluginCleaningData {
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
/// Set the condition string.
|
||||
#[must_use]
|
||||
pub fn with_condition(mut self, condition: String) -> Self {
|
||||
self.condition = Some(condition.into_boxed_str());
|
||||
self
|
||||
}
|
||||
|
||||
/// Get the CRC that identifies the plugin that the cleaning data is for.
|
||||
pub fn crc(&self) -> u32 {
|
||||
self.crc
|
||||
@@ -104,6 +114,11 @@ impl PluginCleaningData {
|
||||
pub fn detail(&self) -> &[MessageContent] {
|
||||
&self.detail
|
||||
}
|
||||
|
||||
/// Get the condition string.
|
||||
pub fn condition(&self) -> Option<&str> {
|
||||
self.condition.as_deref()
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFromYaml for PluginCleaningData {
|
||||
@@ -136,6 +151,8 @@ impl TryFromYaml for PluginCleaningData {
|
||||
None => Box::default(),
|
||||
};
|
||||
|
||||
let condition = parse_condition(mapping, "condition", YamlObjectType::File)?;
|
||||
|
||||
Ok(PluginCleaningData {
|
||||
crc,
|
||||
itm_count: itm,
|
||||
@@ -143,6 +160,7 @@ impl TryFromYaml for PluginCleaningData {
|
||||
deleted_navmesh_count: nav,
|
||||
cleaning_utility: util.into(),
|
||||
detail,
|
||||
condition,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -177,6 +195,11 @@ impl EmitYaml for PluginCleaningData {
|
||||
emit_message_contents(&self.detail, emitter);
|
||||
}
|
||||
|
||||
if let Some(condition) = &self.condition {
|
||||
emitter.write_map_key("condition");
|
||||
emitter.write_condition(condition);
|
||||
}
|
||||
|
||||
emitter.end_map();
|
||||
}
|
||||
}
|
||||
@@ -250,8 +273,9 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn should_set_all_given_fields() {
|
||||
let yaml =
|
||||
parse("{crc: 0x12345678, util: cleaner, detail: info, itm: 2, udr: 10, nav: 30}");
|
||||
let yaml = parse(
|
||||
"{crc: 0x12345678, util: cleaner, detail: info, itm: 2, udr: 10, nav: 30, condition: file(\"example.esp\")}",
|
||||
);
|
||||
|
||||
let data = PluginCleaningData::try_from_yaml(&yaml).unwrap();
|
||||
|
||||
@@ -261,6 +285,7 @@ mod tests {
|
||||
assert_eq!(2, data.itm_count());
|
||||
assert_eq!(10, data.deleted_reference_count());
|
||||
assert_eq!(30, data.deleted_navmesh_count());
|
||||
assert_eq!("file(\"example.esp\")", data.condition().unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -275,6 +300,7 @@ mod tests {
|
||||
assert_eq!(0, data.itm_count());
|
||||
assert_eq!(0, data.deleted_reference_count());
|
||||
assert_eq!(0, data.deleted_navmesh_count());
|
||||
assert!(data.condition().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -400,7 +426,8 @@ detail:
|
||||
MessageContent::new("english".into()).with_language("en".into()),
|
||||
MessageContent::new("french".into()).with_language("fr".into()),
|
||||
])
|
||||
.unwrap();
|
||||
.unwrap()
|
||||
.with_condition("file(\"example.esp\")".into());
|
||||
let yaml = emit(&data);
|
||||
|
||||
assert_eq!(
|
||||
@@ -414,7 +441,8 @@ detail:
|
||||
- lang: {}
|
||||
text: '{}'
|
||||
- lang: {}
|
||||
text: '{}'",
|
||||
text: '{}'
|
||||
condition: '{}'",
|
||||
data.cleaning_utility,
|
||||
data.itm_count,
|
||||
data.deleted_reference_count,
|
||||
@@ -422,7 +450,8 @@ detail:
|
||||
data.detail[0].language(),
|
||||
data.detail[0].text(),
|
||||
data.detail[1].language(),
|
||||
data.detail[1].text()
|
||||
data.detail[1].text(),
|
||||
data.condition.unwrap()
|
||||
),
|
||||
yaml
|
||||
);
|
||||
@@ -445,5 +474,25 @@ detail:
|
||||
|
||||
assert_eq!("crc: 0xDEADBEEF\nutil: 'TES5Edit'\ndetail: *content1", yaml);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_emit_an_alias_if_the_condition_has_an_anchor() {
|
||||
let data = PluginCleaningData::new(0xDEAD_BEEF, "TES5Edit".into())
|
||||
.with_condition("file(\"example.esp\")".into());
|
||||
|
||||
let mut anchors = YamlAnchors::new();
|
||||
anchors.set_condition_anchors(HashMap::from([(
|
||||
data.condition().unwrap(),
|
||||
"condition1".to_owned(),
|
||||
)]));
|
||||
anchors.record_written_anchor("condition1".to_owned());
|
||||
|
||||
let yaml = emit_with_anchors(&data, anchors);
|
||||
|
||||
assert_eq!(
|
||||
"crc: 0xDEADBEEF\nutil: 'TES5Edit'\ncondition: *condition1",
|
||||
yaml
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user