Deny most of clippy's pedantic lints

They suggested a lot of good code style improvements and spotted a few mistakes.
This commit is contained in:
Oliver Hamlet
2025-04-23 00:16:08 +01:00
parent db8984096f
commit 724b8b55c1
25 changed files with 288 additions and 331 deletions
+4 -3
View File
@@ -77,9 +77,10 @@ pub(super) fn read_assets<T: BufRead + Seek>(
let file_path_bytes = trim_slashes(&file_path_bytes);
let (folder_hash, file_hash) = rsplit_on(file_path_bytes, b'\\')
.map(|(folder_path, file_path)| (hash(&folder_path), hash(&file_path)))
.unwrap_or_else(|| (0, hash(&file_path_bytes)));
let (folder_hash, file_hash) = rsplit_on(file_path_bytes, b'\\').map_or_else(
|| (0, hash(&file_path_bytes)),
|(folder_path, file_path)| (hash(&folder_path), hash(&file_path)),
);
let file_hashes: &mut BTreeSet<u64> = assets.entry(folder_hash).or_default();
+4 -7
View File
@@ -173,13 +173,10 @@ fn read_assets_with_header<T: BufRead, const U: usize>(
}
};
let file_records_buffer = match file_records_buffer.get(file_records_offset..) {
Some(s) => s,
None => {
return Err(ArchiveParsingError::InvalidFileRecordsOffset(
file_records_offset,
));
}
let Some(file_records_buffer) = file_records_buffer.get(file_records_offset..) else {
return Err(ArchiveParsingError::InvalidFileRecordsOffset(
file_records_offset,
));
};
let file_hashes: &mut BTreeSet<u64> = entry.or_default();
+8 -9
View File
@@ -53,27 +53,26 @@ impl std::fmt::Display for ArchiveParsingError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::IoError(_) => write!(f, "an I/O error occurred"),
Self::UnsupportedHeaderVersion(v) => write!(f, "unsupported archive version {}", v),
Self::UnsupportedHeaderArchiveType(a) => write!(f, "unsupported archive type {:?}", a),
Self::UnsupportedArchiveTypeId(t) => write!(f, "unsupported archive type ID {:?}", t),
Self::InvalidRecordsOffset(o) => write!(f, "invalid records offset {}", o),
Self::UnsupportedHeaderVersion(v) => write!(f, "unsupported archive version {v}"),
Self::UnsupportedHeaderArchiveType(a) => write!(f, "unsupported archive type {a:?}"),
Self::UnsupportedArchiveTypeId(t) => write!(f, "unsupported archive type ID {t:?}"),
Self::InvalidRecordsOffset(o) => write!(f, "invalid records offset {o}"),
Self::InvalidFolderNameLengthOffset(o) => {
write!(f, "invalid folder name length offset {}", o)
write!(f, "invalid folder name length offset {o}")
}
Self::InvalidFileRecordsOffset(o) => write!(f, "invalid file records offset {}", o),
Self::InvalidFileRecordsOffset(o) => write!(f, "invalid file records offset {o}"),
Self::UsesBigEndianNumbers => {
write!(f, "archive uses big-endian numbers, which is unsupported")
}
Self::FolderHashCollision(h) => {
write!(f, "unexpected collision for folder name hash {:x}", h)
write!(f, "unexpected collision for folder name hash {h:x}")
}
Self::HashCollision {
folder_hash,
file_hash,
} => write!(
f,
"unexpected collision for file name hash {:x} in set for folder name hash {:x}",
file_hash, folder_hash
"unexpected collision for file name hash {file_hash:x} in set for folder name hash {folder_hash:x}"
),
}
}
+24 -32
View File
@@ -62,9 +62,8 @@ fn find_associated_archives_with_suffixes(
archive_extension: &str,
supported_suffixes: &[&str],
) -> Vec<PathBuf> {
let file_stem = match plugin_path.file_stem() {
Some(s) => s,
None => return Vec::new(),
let Some(file_stem) = plugin_path.file_stem() else {
return Vec::new();
};
supported_suffixes
@@ -89,9 +88,8 @@ fn find_associated_archives_with_arbitrary_suffixes(
Some(s) => s.len(),
None => return Vec::new(),
};
let plugin_extension = match plugin_path.extension() {
Some(e) => e,
None => return Vec::new(),
let Some(plugin_extension) = plugin_path.extension() else {
return Vec::new();
};
game_cache
@@ -101,9 +99,8 @@ fn find_associated_archives_with_arbitrary_suffixes(
// but case insensitively. This is hard to do accurately, so
// instead check if the plugin with the same length basename and
// and the given plugin's file extension is equivalent.
let archive_filename = match path.file_name().and_then(|s| s.to_str()) {
Some(f) => f,
None => return false,
let Some(archive_filename) = path.file_name().and_then(|s| s.to_str()) else {
return false;
};
// Can't just slice the archive filename to the same length as the plugin file stem directly because that might not slice on a character boundary, so truncate the byte slice and then check it's still valid UTF-8.
@@ -111,11 +108,10 @@ fn find_associated_archives_with_arbitrary_suffixes(
return false;
}
let filename =
match std::str::from_utf8(&archive_filename.as_bytes()[..plugin_stem_len]) {
Ok(f) => f,
Err(_) => return false,
};
let Ok(filename) = std::str::from_utf8(&archive_filename.as_bytes()[..plugin_stem_len])
else {
return false;
};
let archive_plugin_path = plugin_path
.with_file_name(filename)
@@ -129,10 +125,6 @@ fn find_associated_archives_with_arbitrary_suffixes(
#[cfg(windows)]
fn are_file_paths_equivalent(lhs: &Path, rhs: &Path) -> bool {
if lhs == rhs {
return true;
}
use std::fs::File;
use std::os::windows::io::AsRawHandle;
use windows::Win32::{
@@ -140,14 +132,16 @@ fn are_file_paths_equivalent(lhs: &Path, rhs: &Path) -> bool {
Storage::FileSystem::{BY_HANDLE_FILE_INFORMATION, GetFileInformationByHandle},
};
let lhs_file = match File::open(lhs) {
Ok(f) => f,
Err(_) => return false,
if lhs == rhs {
return true;
}
let Ok(lhs_file) = File::open(lhs) else {
return false;
};
let rhs_file = match File::open(rhs) {
Ok(f) => f,
Err(_) => return false,
let Ok(rhs_file) = File::open(rhs) else {
return false;
};
let mut lhs_info = BY_HANDLE_FILE_INFORMATION::default();
@@ -170,20 +164,18 @@ fn are_file_paths_equivalent(lhs: &Path, rhs: &Path) -> bool {
#[cfg(not(windows))]
fn are_file_paths_equivalent(lhs: &Path, rhs: &Path) -> bool {
use std::os::unix::fs::MetadataExt;
if lhs == rhs {
return true;
}
use std::os::unix::fs::MetadataExt;
let lhs_metadata = match lhs.metadata() {
Ok(m) => m,
_ => return false,
let Ok(lhs_metadata) = lhs.metadata() else {
return false;
};
let rhs_metadata = match rhs.metadata() {
Ok(m) => m,
_ => return false,
let Ok(rhs_metadata) = rhs.metadata() else {
return false;
};
lhs_metadata.dev() == rhs_metadata.dev() && lhs_metadata.ino() == rhs_metadata.ino()
+2 -2
View File
@@ -61,9 +61,9 @@ mod tests {
let path = PathBuf::from("./testing-plugins/Skyrim/Data/Blank.bsa");
let assets2 = assets_in_archives(&[path]);
assert_eq!(assets1.get(&0), assets2.get(&0x2E01002E));
assert_eq!(assets1.get(&0), assets2.get(&0x2E01_002E));
assert!(!do_assets_overlap(&assets1, &assets2))
assert!(!do_assets_overlap(&assets1, &assets2));
}
}
}
+19 -19
View File
@@ -141,7 +141,7 @@ mod tests {
let path = Path::new("./testing-plugins/Oblivion/Data/Blank.bsa");
let assets = get_assets_in_archive(path).unwrap();
let files_count: usize = assets.values().map(|v| v.len()).sum();
let files_count: usize = assets.values().map(BTreeSet::len).sum();
let expected_key = 0;
assert_eq!(1, assets.len());
@@ -149,7 +149,7 @@ mod tests {
assert_eq!(expected_key, *assets.first_key_value().unwrap().0);
assert_eq!(1, assets.get(&expected_key).unwrap().len());
assert_eq!(
0x4670B6836C077365,
0x4670_B683_6C07_7365,
*assets.get(&expected_key).unwrap().first().unwrap()
);
}
@@ -159,15 +159,15 @@ mod tests {
let path = Path::new("./testing-plugins/Skyrim/Data/Blank.bsa");
let assets = get_assets_in_archive(path).unwrap();
let files_count: usize = assets.values().map(|v| v.len()).sum();
let files_count: usize = assets.values().map(BTreeSet::len).sum();
let expected_key = 0x2E01002E;
let expected_key = 0x2E01_002E;
assert_eq!(1, assets.len());
assert_eq!(1, files_count);
assert_eq!(expected_key, *assets.first_key_value().unwrap().0);
assert_eq!(1, assets.get(&expected_key).unwrap().len());
assert_eq!(
0x4670B6836C077365,
0x4670_B683_6C07_7365,
*assets.get(&expected_key).unwrap().first().unwrap()
);
}
@@ -177,15 +177,15 @@ mod tests {
let path = Path::new("./testing-plugins/SkyrimSE/Data/Blank.bsa");
let assets = get_assets_in_archive(path).unwrap();
let files_count: usize = assets.values().map(|v| v.len()).sum();
let files_count: usize = assets.values().map(BTreeSet::len).sum();
let expected_key = 0xB68102C964176E73;
let expected_key = 0xB681_02C9_6417_6E73;
assert_eq!(1, assets.len());
assert_eq!(1, files_count);
assert_eq!(expected_key, *assets.first_key_value().unwrap().0);
assert_eq!(1, assets.get(&expected_key).unwrap().len());
assert_eq!(
0x4670B6836C077365,
0x4670_B683_6C07_7365,
*assets.get(&expected_key).unwrap().first().unwrap()
);
}
@@ -195,7 +195,7 @@ mod tests {
let path = Path::new("./testing-plugins/Fallout 4/Data/Blank - Main.ba2");
let assets = get_assets_in_archive(path).unwrap();
let files_count: usize = assets.values().map(|v| v.len()).sum();
let files_count: usize = assets.values().map(BTreeSet::len).sum();
let expected_key = hash("dev\\git\\testing-plugins".as_bytes());
let expected_file_hash = hash("license.txt".as_bytes());
@@ -214,7 +214,7 @@ mod tests {
let path = Path::new("./testing-plugins/Fallout 4/Data/Blank - Textures.ba2");
let assets = get_assets_in_archive(path).unwrap();
let files_count: usize = assets.values().map(|v| v.len()).sum();
let files_count: usize = assets.values().map(BTreeSet::len).sum();
let expected_key = hash("dev\\git\\testing-plugins".as_bytes());
let expected_file_hash = hash("blank.dds".as_bytes());
@@ -260,15 +260,15 @@ mod tests {
let assets = assets_in_archives(&paths);
let files_count: usize = assets.values().map(|v| v.len()).sum();
let files_count: usize = assets.values().map(BTreeSet::len).sum();
assert_eq!(1, assets.len());
assert_eq!(1, files_count);
let (key, value) = assets.first_key_value().unwrap();
assert_eq!(0x2E01002E, *key);
assert_eq!(0x2E01_002E, *key);
assert_eq!(1, value.len());
assert_eq!(0x4670B6836C077365, *value.first().unwrap());
assert_eq!(0x4670_B683_6C07_7365, *value.first().unwrap());
}
#[test]
@@ -281,22 +281,22 @@ mod tests {
let assets = assets_in_archives(&paths);
let files_count: usize = assets.values().map(|v| v.len()).sum();
let files_count: usize = assets.values().map(BTreeSet::len).sum();
assert_eq!(3, assets.len());
assert_eq!(3, files_count);
let value = assets.get(&0).unwrap();
assert_eq!(1, value.len());
assert_eq!(0x4670B6836C077365, *value.first().unwrap());
assert_eq!(0x4670_B683_6C07_7365, *value.first().unwrap());
let value = assets.get(&0x2E01002E).unwrap();
let value = assets.get(&0x2E01_002E).unwrap();
assert_eq!(1, value.len());
assert_eq!(0x4670B6836C077365, *value.first().unwrap());
assert_eq!(0x4670_B683_6C07_7365, *value.first().unwrap());
let value = assets.get(&0xB68102C964176E73).unwrap();
let value = assets.get(&0xB681_02C9_6417_6E73).unwrap();
assert_eq!(1, value.len());
assert_eq!(0x4670B6836C077365, *value.first().unwrap());
assert_eq!(0x4670_B683_6C07_7365, *value.first().unwrap());
}
}
}
+7 -6
View File
@@ -132,9 +132,10 @@ mod tests {
plugin.set_group("group1".into());
let condition = "file(\"missing.esp\")".to_string();
let file1 = File::new(BLANK_ESP.into());
let file2 = File::new(BLANK_DIFFERENT_ESM.into()).with_condition(condition.clone());
let files = vec![file1.clone(), file2];
let files = vec![
File::new(BLANK_ESP.into()),
File::new(BLANK_DIFFERENT_ESM.into()).with_condition(condition.clone()),
];
plugin.set_load_after_files(files.clone());
plugin.set_requirements(files.clone());
plugin.set_incompatibilities(files.clone());
@@ -149,8 +150,8 @@ mod tests {
Tag::new("Relev".into(), TagSuggestion::Addition).with_condition(condition.clone());
plugin.set_tags(vec![tag1.clone(), tag2]);
let info1 = PluginCleaningData::new(0x374E2A6F, "utility1".into());
let info2 = PluginCleaningData::new(0xDEADBEEF, "utility2".into());
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()]);
plugin.set_clean_info(vec![info1.clone(), info2.clone()]);
@@ -160,7 +161,7 @@ mod tests {
);
let result = evaluate_all_conditions(plugin, &state).unwrap().unwrap();
let expected_files = &[file1];
let expected_files = &[files[0].clone()];
let expected_info = &[info1];
assert_eq!("group1", result.group().unwrap());
assert_eq!(expected_files, result.load_after_files());
+10 -7
View File
@@ -116,8 +116,11 @@ impl Database {
let mut doc = MetadataDocument::default();
for plugin in self.masterlist.plugins_iter() {
let mut minimal_plugin = PluginMetadata::new(plugin.name())
.expect("Regex plugin name from existing PluginMetadata object is valid");
let Ok(mut minimal_plugin) = PluginMetadata::new(plugin.name()) else {
// This should never happen because the regex plugin name from
// an existing PluginMetadata object should be valid.
continue;
};
minimal_plugin.set_tags(plugin.tags().to_vec());
minimal_plugin.set_dirty_info(plugin.dirty_info().to_vec());
@@ -300,7 +303,7 @@ impl Database {
}
fn validate_write_path(output_path: &Path, mode: WriteMode) -> Result<(), WriteMetadataError> {
if !output_path.parent().map(|p| p.exists()).unwrap_or(false) {
if !output_path.parent().is_some_and(Path::exists) {
Err(WriteMetadataError::new(
output_path.into(),
WriteMetadataErrorReason::ParentDirectoryNotFound,
@@ -975,7 +978,7 @@ plugins:
.plugin_metadata(BLANK_ESM, true, false)
.unwrap()
.is_none()
)
);
}
#[test]
@@ -990,7 +993,7 @@ plugins:
.plugin_metadata(BLANK_ESM, true, false)
.unwrap()
.is_none()
)
);
}
#[test]
@@ -1079,7 +1082,7 @@ plugins:
.plugin_user_metadata(BLANK_ESM, false)
.unwrap()
.is_none()
)
);
}
#[test]
@@ -1094,7 +1097,7 @@ plugins:
.plugin_user_metadata(BLANK_ESM, false)
.unwrap()
.is_none()
)
);
}
#[test]
+3 -3
View File
@@ -209,10 +209,10 @@ impl std::fmt::Display for SortPluginsError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::DatabaseLockPoisoned => DatabaseLockPoisonError.fmt(f),
Self::PluginNotLoaded(n) => write!(f, "the plugin \"{}\" has not been loaded", n),
Self::UndefinedGroup(g) => write!(f, "the group \"{}\" does not exist", g),
Self::PluginNotLoaded(n) => write!(f, "the plugin \"{n}\" has not been loaded"),
Self::UndefinedGroup(g) => write!(f, "the group \"{g}\" does not exist"),
Self::CycleFound(c) => write!(f, "found a cycle: {}", display_cycle(c)),
Self::CycleFoundInvolving(n) => write!(f, "found a cycle involving \"{}\"", n),
Self::CycleFoundInvolving(n) => write!(f, "found a cycle involving \"{n}\""),
Self::PluginDataError(_) => write!(f, "failed to read loaded plugin data"),
Self::MetadataRetrievalError(_) => write!(f, "failed to retrieve plugin metadata"),
Self::PathfindingError(_) => write!(f, "failed to find a path in the plugins graph"),
+17 -22
View File
@@ -446,7 +446,7 @@ impl Game {
.map(|n| {
self.cache
.plugin(n)
.ok_or_else(|| SortPluginsError::PluginNotLoaded(n.to_string()))
.ok_or_else(|| SortPluginsError::PluginNotLoaded((*n).to_owned()))
})
.collect::<Result<Vec<_>, _>>()?;
@@ -646,7 +646,7 @@ fn find_archives_in_path(
}
let paths = std::fs::read_dir(parent_path)?
.filter_map(|e| e.ok())
.filter_map(Result::ok)
.filter(|e| {
e.file_type().map(|f| f.is_file()).unwrap_or(false)
&& iends_with_ascii(&e.file_name().to_string_lossy(), archive_file_extension)
@@ -838,8 +838,8 @@ mod tests {
// The paths may contain forward slashes, which cmd doesn't accept,
// so replace them.
let original = OsString::from(original.to_str().unwrap().replace("/", "\\"));
let link = OsString::from(link.to_str().unwrap().replace("/", "\\"));
let original = OsString::from(original.to_str().unwrap().replace('/', "\\"));
let link = OsString::from(link.to_str().unwrap().replace('/', "\\"));
let status = std::process::Command::new("cmd")
.args([
@@ -895,7 +895,7 @@ mod tests {
relative_components
.into_iter()
.map(|c| c.as_os_str())
.map(Component::as_os_str)
.collect()
}
@@ -966,9 +966,7 @@ mod tests {
fn should_error_if_given_a_game_path_that_does_not_exist() {
let game_path = Path::new("missing");
match Game::new(GameType::TES3, game_path) {
Err(GameHandleCreationError::NotADirectory(p)) => {
assert_eq!(game_path, p)
}
Err(GameHandleCreationError::NotADirectory(p)) => assert_eq!(game_path, p),
_ => panic!("Expected a not-a-directory error"),
}
}
@@ -1062,9 +1060,7 @@ mod tests {
let game = Game::with_local_path(fixture.game_type, game_path, &fixture.local_path);
match game {
Err(GameHandleCreationError::NotADirectory(p)) => {
assert_eq!(game_path, p)
}
Err(GameHandleCreationError::NotADirectory(p)) => assert_eq!(game_path, p),
_ => panic!("Expected a not-a-directory error"),
}
}
@@ -1089,9 +1085,7 @@ mod tests {
let game = Game::with_local_path(fixture.game_type, &fixture.game_path, local_path);
match game {
Err(GameHandleCreationError::NotADirectory(p)) => {
assert_eq!(local_path, p)
}
Err(GameHandleCreationError::NotADirectory(p)) => assert_eq!(local_path, p),
_ => panic!("Expected a not-a-directory error"),
}
}
@@ -1246,7 +1240,7 @@ mod tests {
game.load_current_load_order_state().unwrap();
let mut load_order: Vec<_> =
game.load_order().iter().map(|s| s.to_string()).collect();
game.load_order().iter().map(ToString::to_string).collect();
let filename = "plugin.esp";
let data_file_path = fixture
@@ -1374,7 +1368,7 @@ mod tests {
if game_type == GameType::OpenMW {
std::fs::rename(
&path,
path.with_file_name(format!("{}.ghost", BLANK_MASTER_DEPENDENT_ESM)),
path.with_file_name(format!("{BLANK_MASTER_DEPENDENT_ESM}.ghost")),
)
.unwrap();
@@ -1625,10 +1619,10 @@ mod tests {
match source.downcast_ref::<esplugin::Error>().unwrap() {
esplugin::Error::PluginMetadataNotFound(p) => {
if game_type == GameType::Starfield {
assert_eq!(BLANK_FULL_ESM, p)
assert_eq!(BLANK_FULL_ESM, p);
} else {
assert_eq!(BLANK_ESM, p)
};
assert_eq!(BLANK_ESM, p);
}
}
_ => panic!("Unexpected esplugin error: {e}"),
}
@@ -1873,7 +1867,7 @@ mod tests {
let path = fixture
.data_path()
.join(format!("{}.ghost", BLANK_MASTER_DEPENDENT_ESM));
.join(format!("{BLANK_MASTER_DEPENDENT_ESM}.ghost"));
let plugins = game
.load_plugins_common(&[&path], LoadScope::HeaderOnly)
@@ -2007,9 +2001,10 @@ mod tests {
game.load_current_load_order_state().unwrap();
let mut load_order: Vec<_> = game.load_order().iter().map(|s| s.to_string()).collect();
let mut load_order: Vec<_> =
game.load_order().iter().map(ToString::to_string).collect();
load_order.swap(7, 10);
let load_order: Vec<_> = load_order.iter().map(|s| s.as_str()).collect();
let load_order: Vec<_> = load_order.iter().map(String::as_str).collect();
game.set_load_order(&load_order).unwrap();
+6
View File
@@ -1,3 +1,9 @@
#![deny(clippy::pedantic)]
// Allow a few clippy pedantic lints.
#![allow(clippy::doc_markdown)]
#![allow(clippy::must_use_candidate)]
#![allow(clippy::missing_errors_doc)]
mod archive;
mod database;
pub mod error;
+16 -17
View File
@@ -68,8 +68,7 @@ impl From<LogLevel> for log::Level {
LogLevel::Debug => log::Level::Debug,
LogLevel::Info => log::Level::Info,
LogLevel::Warning => log::Level::Warn,
LogLevel::Error => log::Level::Error,
LogLevel::Fatal => log::Level::Error,
LogLevel::Error | LogLevel::Fatal => log::Level::Error,
}
}
}
@@ -89,7 +88,7 @@ impl Logger {
pub(crate) fn log(&self, level: LogLevel, message: &str) {
if level >= self.level {
(self.callback)(level, message)
(self.callback)(level, message);
}
}
@@ -166,7 +165,7 @@ pub(crate) fn format_details<E: std::error::Error>(error: &E) -> String {
let mut details = error.to_string(); // The display string.
if let Some(source) = error.source() {
details += ": ";
details += &format_details(&source)
details += &format_details(&source);
}
details
@@ -188,8 +187,6 @@ mod tests {
#[test]
fn should_support_a_function() {
let _lock = TEST_LOCK.lock().unwrap();
static MESSAGES: LazyLock<Mutex<Vec<(LogLevel, String)>>> =
LazyLock::new(|| Mutex::new(Vec::new()));
@@ -199,6 +196,8 @@ mod tests {
}
}
let _lock = TEST_LOCK.lock().unwrap();
set_logging_callback(callback);
error!("Test message");
@@ -233,6 +232,15 @@ mod tests {
#[test]
fn set_logging_callback_should_be_callable_multiple_times() {
static MESSAGES: LazyLock<Mutex<Vec<(LogLevel, String)>>> =
LazyLock::new(|| Mutex::new(Vec::new()));
fn callback_fn(level: LogLevel, message: &str) {
if let Ok(mut messages) = MESSAGES.lock() {
messages.push((level, message.to_string()));
}
}
let _lock = TEST_LOCK.lock().unwrap();
let callback = |_, _: &str| {};
@@ -255,15 +263,6 @@ mod tests {
*messages.lock().unwrap()
);
static MESSAGES: LazyLock<Mutex<Vec<(LogLevel, String)>>> =
LazyLock::new(|| Mutex::new(Vec::new()));
fn callback_fn(level: LogLevel, message: &str) {
if let Ok(mut messages) = MESSAGES.lock() {
messages.push((level, message.to_string()));
}
}
set_logging_callback(callback_fn);
error!("Test message");
@@ -280,8 +279,6 @@ mod tests {
#[test]
fn should_set_the_level_used_to_filter_messages_passed_to_the_callback() {
let _lock = TEST_LOCK.lock().unwrap();
static MESSAGES: LazyLock<Mutex<Vec<(LogLevel, String)>>> =
LazyLock::new(|| Mutex::new(Vec::new()));
@@ -291,6 +288,8 @@ mod tests {
}
}
let _lock = TEST_LOCK.lock().unwrap();
set_logging_callback(callback);
set_log_level(LogLevel::Warning);
+18 -31
View File
@@ -142,42 +142,34 @@ impl std::fmt::Display for MetadataParsingErrorReason {
Self::InvalidCondition(b) => {
write!(f, "the condition string \"{}\" is invalid", b.0)
}
Self::MissingKey(key, yaml_object_type) => write!(
f,
"\"{}\" key in \"{}\" map is missing",
key, yaml_object_type
),
Self::MissingKey(key, yaml_object_type) => {
write!(f, "\"{key}\" key in \"{yaml_object_type}\" map is missing")
}
Self::InvalidRegex(_) => {
write!(f, "invalid regex in \"name\" key")
}
Self::InvalidMultilingualMessageContents => MultilingualMessageContentsError.fmt(f),
Self::UnexpectedType(expected_type, yaml_object_type) => write!(
f,
"\"{}\" object must be {}",
yaml_object_type, expected_type
),
Self::UnexpectedType(expected_type, yaml_object_type) => {
write!(f, "\"{yaml_object_type}\" object must be {expected_type}")
}
Self::UnexpectedValueType(key, expected_type, yaml_object_type) => write!(
f,
"\"{}\" key in \"{}\" map must be {}",
key, yaml_object_type, expected_type
"\"{key}\" key in \"{yaml_object_type}\" map must be {expected_type}"
),
Self::MissingPlaceholder(sub, placeholder_index) => write!(
f,
"failed to substitute \"{}\" into message, no placeholder {{{}}} was found",
sub, placeholder_index
"failed to substitute \"{sub}\" into message, no placeholder {{{placeholder_index}}} was found"
),
Self::MissingSubstitution(placeholder) => write!(
f,
"failed to substitute a value into message, no substitution was given for the placeholder \"{}\"",
placeholder
"failed to substitute a value into message, no substitution was given for the placeholder \"{placeholder}\""
),
Self::NonU32Number(i) => {
write!(f, "{} is not valid as a 32-bit unsigned integer", i)
write!(f, "{i} is not valid as a 32-bit unsigned integer")
}
Self::DuplicateEntry(id, yaml_object_type) => write!(
f,
"more than one entry exists for {} \"{}\"",
yaml_object_type, id
"more than one entry exists for {yaml_object_type} \"{id}\""
),
Self::Other(m) => m.fmt(f),
}
@@ -239,15 +231,12 @@ pub struct LoadMetadataError {
impl LoadMetadataError {
pub(super) fn new(path: PathBuf, reason: MetadataDocumentParsingError) -> Self {
Self {
path: path.to_path_buf(),
reason,
}
Self { path, reason }
}
pub(super) fn from_io_error(path: PathBuf, error: std::io::Error) -> Self {
Self {
path: path.to_path_buf(),
path,
reason: MetadataDocumentParsingError::IoError(error),
}
}
@@ -281,7 +270,7 @@ impl std::fmt::Display for MetadataDocumentParsingError {
match self {
Self::PathNotFound => write!(f, "path not found"),
Self::NoDocuments => write!(f, "no YAML document found"),
Self::MoreThanOneDocument(n) => write!(f, "expected 1 YAML document, found {}", n),
Self::MoreThanOneDocument(n) => write!(f, "expected 1 YAML document, found {n}"),
Self::IoError(_) => write!(f, "an I/O error occurred"),
Self::MetadataParsingError(_) => write!(f, "a metadata parsing error occurred"),
Self::YamlMergeKeyError(_) => {
@@ -294,9 +283,7 @@ impl std::fmt::Display for MetadataDocumentParsingError {
impl std::error::Error for MetadataDocumentParsingError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::PathNotFound => None,
Self::NoDocuments => None,
Self::MoreThanOneDocument(_) => None,
Self::PathNotFound | Self::NoDocuments | Self::MoreThanOneDocument(_) => None,
Self::IoError(e) => Some(e),
Self::MetadataParsingError(e) => Some(e),
Self::YamlMergeKeyError(e) => Some(e),
@@ -335,10 +322,10 @@ pub(super) struct YamlMergeKeyError {
}
impl YamlMergeKeyError {
pub(super) fn new(value: saphyr::MarkedYaml) -> Self {
pub(super) fn new(value: &saphyr::MarkedYaml) -> Self {
let mut yaml = String::new();
let unmarked_yaml = to_unmarked_yaml(&value);
let unmarked_yaml = to_unmarked_yaml(value);
if saphyr::YamlEmitter::new(&mut yaml)
.dump(&unmarked_yaml)
@@ -350,7 +337,7 @@ impl YamlMergeKeyError {
yaml = yaml.split_off(index);
}
} else {
yaml = format!("{:?}", yaml);
yaml = format!("{yaml:?}");
}
YamlMergeKeyError {
+3 -3
View File
@@ -78,8 +78,8 @@ impl std::default::Default for Group {
fn default() -> Self {
Self {
name: Group::DEFAULT_NAME.into(),
description: Default::default(),
after_groups: Default::default(),
description: Option::default(),
after_groups: Box::default(),
}
}
}
@@ -98,7 +98,7 @@ impl TryFromYaml for Group {
Ok(Group {
name: name.into(),
description: description.map(|d| d.1.into()),
after_groups: after.iter().map(|a| a.to_string()).collect(),
after_groups: after.into_iter().map(str::to_owned).collect(),
})
}
}
+6 -3
View File
@@ -91,7 +91,7 @@ impl std::default::Default for MessageContent {
#[must_use]
fn default() -> Self {
Self {
text: Default::default(),
text: Box::default(),
language: MessageContent::DEFAULT_LANGUAGE.into(),
}
}
@@ -343,12 +343,15 @@ impl TryFromYaml for Message {
}
for (index, sub) in subs.iter().enumerate() {
let placeholder = format!("{{{}}}", index);
let placeholder = format!("{{{index}}}");
if !mc.text.contains(&placeholder) {
return Err(ParseMetadataError::new(
value.span.start,
MetadataParsingErrorReason::MissingPlaceholder(sub.to_string(), index),
MetadataParsingErrorReason::MissingPlaceholder(
(*sub).to_owned(),
index,
),
));
}
+23 -26
View File
@@ -104,16 +104,13 @@ impl MetadataDocument {
}
let doc = process_merge_keys(doc)?;
let doc = match doc.data {
YamlData::Mapping(h) => h,
_ => {
return Err(ParseMetadataError::unexpected_type(
doc.span.start,
YamlObjectType::MetadataDocument,
ExpectedType::Map,
)
.into());
}
let YamlData::Mapping(doc) = doc.data else {
return Err(ParseMetadataError::unexpected_type(
doc.span.start,
YamlObjectType::MetadataDocument,
ExpectedType::Map,
)
.into());
};
let mut plugins: HashMap<Filename, PluginMetadata> = HashMap::new();
@@ -292,12 +289,12 @@ impl MetadataDocument {
// Ensure that the default group is present.
let default_group_exists = groups.iter().any(|g| g.name() == Group::DEFAULT_NAME);
if !default_group_exists {
if default_group_exists {
self.groups = groups;
} else {
self.groups.clear();
self.groups.push(Group::default());
self.groups.extend(groups);
} else {
self.groups = groups;
}
}
@@ -328,11 +325,11 @@ impl MetadataDocument {
impl std::default::Default for MetadataDocument {
fn default() -> Self {
Self {
bash_tags: Default::default(),
bash_tags: Vec::default(),
groups: vec![Group::default()],
messages: Default::default(),
plugins: Default::default(),
regex_plugins: Default::default(),
messages: Vec::default(),
plugins: HashMap::default(),
regex_plugins: Vec::default(),
}
}
}
@@ -349,7 +346,7 @@ fn replace_prelude(masterlist: String, prelude: &str) -> String {
}
fn detect_line_ending(masterlist: &str) -> &'static str {
if let Some(pos) = masterlist.rfind("\n") {
if let Some(pos) = masterlist.rfind('\n') {
if pos == 0 {
"\n"
} else if masterlist.as_bytes()[pos - 1] == b'\r' {
@@ -393,8 +390,8 @@ fn find_prelude_bounds(masterlist: &str) -> Option<(usize, usize)> {
}
fn indent_prelude(prelude: &str, line_ending: &str) -> String {
let prelude = ("\n ".to_string() + &prelude.replace("\n", "\n "))
.replace(&format!(" {}", line_ending), line_ending);
let prelude = ("\n ".to_string() + &prelude.replace('\n', "\n "))
.replace(&format!(" {line_ending}"), line_ending);
if prelude.ends_with("\n ") {
prelude[..prelude.len() - 2].to_string()
@@ -459,7 +456,7 @@ plugins:
#[test]
fn load_from_str_should_resolve_aliases() {
let yaml = r#"
let yaml = r"
prelude:
- &anchor
type: say
@@ -467,7 +464,7 @@ plugins:
globals:
- *anchor
"#;
";
let mut metadata_list = MetadataDocument::default();
metadata_list.load_from_str(yaml).unwrap();
@@ -492,7 +489,7 @@ plugins:
#[test]
fn load_from_str_should_error_if_a_plugin_has_two_exact_entries() {
let yaml = r#"
let yaml = r"
plugins:
- name: 'Blank.esm'
msg:
@@ -503,7 +500,7 @@ plugins:
msg:
- type: error
content: 'This plugin entry will cause a failure, as it is not the first exact entry.'
"#;
";
let mut metadata_list = MetadataDocument::default();
assert!(metadata_list.load_from_str(yaml).is_err());
@@ -547,7 +544,7 @@ plugins:
fn load_should_error_if_an_invalid_metadata_file_is_given() {
let tmp_dir = tempdir().unwrap();
let path = tmp_dir.path().join("masterlist.yaml");
let yaml = r#"
let yaml = r"
- 'C.Climate'
- 'Relev'
@@ -559,7 +556,7 @@ plugins:
- name: 'Blank.+\.esp'
after:
- 'Blank.esm'
"#;
";
std::fs::write(&path, yaml).unwrap();
+13 -16
View File
@@ -140,15 +140,12 @@ impl TryFromYaml for PluginCleaningData {
fn try_from_yaml(value: &MarkedYaml) -> Result<Self, ParseMetadataError> {
let mapping = as_mapping(value, YamlObjectType::PluginCleaningData)?;
let crc = match get_u32_value(mapping, "crc", YamlObjectType::PluginCleaningData)? {
Some(n) => n,
None => {
return Err(ParseMetadataError::missing_key(
value.span.start,
"crc",
YamlObjectType::PluginCleaningData,
));
}
let Some(crc) = get_u32_value(mapping, "crc", YamlObjectType::PluginCleaningData)? else {
return Err(ParseMetadataError::missing_key(
value.span.start,
"crc",
YamlObjectType::PluginCleaningData,
));
};
let util = get_required_string_value(
@@ -285,7 +282,7 @@ mod tests {
let data = PluginCleaningData::try_from_yaml(&yaml).unwrap();
assert_eq!(0x12345678, data.crc());
assert_eq!(0x1234_5678, data.crc());
assert_eq!("cleaner", data.cleaning_utility());
assert_eq!(&[MessageContent::new("info".into())], data.detail());
assert_eq!(2, data.itm_count());
@@ -299,7 +296,7 @@ mod tests {
let data = PluginCleaningData::try_from_yaml(&yaml).unwrap();
assert_eq!(0x12345678, data.crc());
assert_eq!(0x1234_5678, data.crc());
assert_eq!("cleaner", data.cleaning_utility());
assert!(data.detail().is_empty());
assert_eq!(0, data.itm_count());
@@ -354,7 +351,7 @@ mod tests {
#[test]
fn should_omit_zero_counts() {
let data = PluginCleaningData::new(0xDEADBEEF, "TES5Edit".into());
let data = PluginCleaningData::new(0xDEAD_BEEF, "TES5Edit".into());
let yaml = emit(&data);
assert_eq!("crc: 0xDEADBEEF\nutil: 'TES5Edit'", yaml);
@@ -362,7 +359,7 @@ mod tests {
#[test]
fn should_emit_non_zero_counts() {
let data = PluginCleaningData::new(0xDEADBEEF, "TES5Edit".into())
let data = PluginCleaningData::new(0xDEAD_BEEF, "TES5Edit".into())
.with_itm_count(1)
.with_deleted_reference_count(2)
.with_deleted_navmesh_count(3);
@@ -376,7 +373,7 @@ mod tests {
#[test]
fn should_emit_map_with_a_detail_string_if_detail_is_monolingual() {
let data = PluginCleaningData::new(0xDEADBEEF, "TES5Edit".into())
let data = PluginCleaningData::new(0xDEAD_BEEF, "TES5Edit".into())
.with_detail(vec![MessageContent::new("message".into())])
.unwrap();
let yaml = emit(&data);
@@ -392,7 +389,7 @@ mod tests {
#[test]
fn should_emit_map_with_a_detail_array_if_detail_is_multilingual() {
let data = PluginCleaningData::new(0xDEADBEEF, "TES5Edit".into())
let data = PluginCleaningData::new(0xDEAD_BEEF, "TES5Edit".into())
.with_detail(vec![
MessageContent::new("english".into()).with_language("en".into()),
MessageContent::new("french".into()).with_language("fr".into()),
@@ -420,7 +417,7 @@ detail:
#[test]
fn should_emit_map_with_all_fields_set() {
let data = PluginCleaningData::new(0xDEADBEEF, "TES5Edit".into())
let data = PluginCleaningData::new(0xDEAD_BEEF, "TES5Edit".into())
.with_itm_count(1)
.with_deleted_reference_count(2)
.with_deleted_navmesh_count(3)
+20 -21
View File
@@ -99,13 +99,13 @@ impl PluginMetadata {
/// Set the plugin's group.
pub fn set_group(&mut self, group: String) {
self.group = Some(group.into_boxed_str())
self.group = Some(group.into_boxed_str());
}
/// Unsets the plugin's group, so that it is implicitly a member of the
/// default group.
pub fn unset_group(&mut self) {
self.group = None
self.group = None;
}
/// Get the plugins that the plugin must load after.
@@ -159,7 +159,7 @@ impl PluginMetadata {
}
if self.group.is_none() && plugin.group.is_some() {
self.group = plugin.group.clone();
self.group.clone_from(&plugin.group);
}
merge_slices(&mut self.load_after, &plugin.load_after);
@@ -344,11 +344,11 @@ fn merge_slices<T: Clone + PartialEq>(target: &mut Box<[T]>, source: &[T]) {
let mut vec = target.to_vec();
for element in source {
if !target.contains(element) {
vec.push(element.clone())
vec.push(element.clone());
}
}
*target = vec.into_boxed_slice()
*target = vec.into_boxed_slice();
}
fn replace_capturing_groups(regex_string: &str) -> Cow<'_, str> {
@@ -356,9 +356,8 @@ fn replace_capturing_groups(regex_string: &str) -> Cow<'_, str> {
let mut prefix_length = 0;
let mut remainder = regex_string;
while let Some(pos) = remainder.find('(') {
let (before, after) = match remainder.split_at_checked(pos + 1) {
Some(t) => t,
None => break,
let Some((before, after)) = remainder.split_at_checked(pos + 1) else {
break;
};
if after.starts_with('?') || (before.ends_with("\\(") && !before.ends_with("\\\\(")) {
@@ -679,9 +678,9 @@ mod tests {
let mut plugin1 = PluginMetadata::new(BLANK_ESM).unwrap();
let mut plugin2 = PluginMetadata::new(BLANK_ESM).unwrap();
let data1 = PluginCleaningData::new(0x12345678, "util1".into());
let data2 = PluginCleaningData::new(0xDEADBEEF, "util2".into());
let data3 = PluginCleaningData::new(0xFEEDCAFE, "util3".into());
let data1 = PluginCleaningData::new(0x1234_5678, "util1".into());
let data2 = PluginCleaningData::new(0xDEAD_BEEF, "util2".into());
let data3 = PluginCleaningData::new(0xFEED_CAFE, "util3".into());
plugin1.set_dirty_info(vec![data1.clone(), data2.clone()]);
plugin2.set_dirty_info(vec![data1.clone(), data3.clone()]);
@@ -698,9 +697,9 @@ mod tests {
let mut plugin1 = PluginMetadata::new(BLANK_ESM).unwrap();
let mut plugin2 = PluginMetadata::new(BLANK_ESM).unwrap();
let data1 = PluginCleaningData::new(0x12345678, "util1".into());
let data2 = PluginCleaningData::new(0xDEADBEEF, "util2".into());
let data3 = PluginCleaningData::new(0xFEEDCAFE, "util3".into());
let data1 = PluginCleaningData::new(0x1234_5678, "util1".into());
let data2 = PluginCleaningData::new(0xDEAD_BEEF, "util2".into());
let data3 = PluginCleaningData::new(0xFEED_CAFE, "util3".into());
plugin1.set_clean_info(vec![data1.clone(), data2.clone()]);
plugin2.set_clean_info(vec![data1.clone(), data3.clone()]);
@@ -801,7 +800,7 @@ mod tests {
#[test]
fn should_be_false_if_dirty_info_is_set() {
let mut plugin = PluginMetadata::new(BLANK_ESM).unwrap();
plugin.set_dirty_info(vec![PluginCleaningData::new(0x12345678, "util1".into())]);
plugin.set_dirty_info(vec![PluginCleaningData::new(0x1234_5678, "util1".into())]);
assert!(!plugin.has_name_only());
}
@@ -809,7 +808,7 @@ mod tests {
#[test]
fn should_be_false_if_clean_info_is_set() {
let mut plugin = PluginMetadata::new(BLANK_ESM).unwrap();
plugin.set_clean_info(vec![PluginCleaningData::new(0x12345678, "util1".into())]);
plugin.set_clean_info(vec![PluginCleaningData::new(0x1234_5678, "util1".into())]);
assert!(!plugin.has_name_only());
}
@@ -1172,7 +1171,7 @@ mod tests {
#[test]
fn should_emit_dirty_info() {
let mut plugin = PluginMetadata::new("test.esp").unwrap();
plugin.set_dirty_info(vec![PluginCleaningData::new(0xDEADBEEF, "utility".into())]);
plugin.set_dirty_info(vec![PluginCleaningData::new(0xDEAD_BEEF, "utility".into())]);
let yaml = emit(&plugin);
assert_eq!(
@@ -1189,7 +1188,7 @@ mod tests {
#[test]
fn should_emit_clean_info() {
let mut plugin = PluginMetadata::new("test.esp").unwrap();
plugin.set_clean_info(vec![PluginCleaningData::new(0xDEADBEEF, "utility".into())]);
plugin.set_clean_info(vec![PluginCleaningData::new(0xDEAD_BEEF, "utility".into())]);
let yaml = emit(&plugin);
assert_eq!(
@@ -1238,7 +1237,7 @@ mod tests {
match replace_capturing_groups(input) {
Cow::Borrowed(output) => assert_eq!(input, output),
Cow::Owned(output) => panic!("Expected borrowed output, got {}", output),
Cow::Owned(output) => panic!("Expected borrowed output, got {output}"),
}
}
@@ -1248,14 +1247,14 @@ mod tests {
match replace_capturing_groups(input) {
Cow::Borrowed(output) => assert_eq!(input, output),
Cow::Owned(output) => panic!("Expected borrowed output, got {}", output),
Cow::Owned(output) => panic!("Expected borrowed output, got {output}"),
}
let input = "no paren(?:th(?:e)s)es";
match replace_capturing_groups(input) {
Cow::Borrowed(output) => assert_eq!(input, output),
Cow::Owned(output) => panic!("Expected borrowed output, got {}", output),
Cow::Owned(output) => panic!("Expected borrowed output, got {output}"),
}
}
+3 -3
View File
@@ -106,7 +106,7 @@ impl YamlEmitter {
_ => self.scope.push(YamlBlock::Map),
}
self.write(&format!("{}:", key));
self.write(&format!("{key}:"));
}
pub fn begin_array(&mut self) {
@@ -298,7 +298,7 @@ fn double_quote(value: &str) -> String {
})
.collect();
format!("\"{}\"", escaped)
format!("\"{escaped}\"")
}
impl<T: EmitYaml> EmitYaml for &[T] {
@@ -315,7 +315,7 @@ impl<T: EmitYaml> EmitYaml for &[T] {
elements => {
emitter.begin_array();
for element in elements.iter() {
for element in *elements {
element.emit_yaml(emitter);
}
+2 -2
View File
@@ -55,11 +55,11 @@ fn merge_into_mapping<'a, 'b>(
if let YamlData::Mapping(h) = e.data {
Ok(merge_mappings(acc, h))
} else {
Err(YamlMergeKeyError::new(e))
Err(YamlMergeKeyError::new(&e))
}
}),
YamlData::Mapping(h) => Ok(merge_mappings(mapping, h)),
_ => Err(YamlMergeKeyError::new(value)),
_ => Err(YamlMergeKeyError::new(&value)),
}
}

Some files were not shown because too many files have changed in this diff Show More