use std::ffi::OsStr; use std::fmt::Debug; use std::path::Path; use crate::error::*; #[derive(Debug, Clone)] pub struct IgnoredSuffixes<'a> { values: Vec<&'a str>, } impl Default for IgnoredSuffixes<'_> { fn default() -> Self { Self { values: vec![ // Editor etc backups "~", ".bak", ".old", ".orig", // Debian and derivatives apt/dpkg/ucf backups ".dpkg-dist", ".dpkg-new", ".dpkg-old", ".dpkg-tmp", ".ucf-dist", ".ucf-new", ".ucf-old", // Red Hat and derivatives rpm backups ".rpmnew", ".rpmorig", ".rpmsave", // Build system input/template files ".in", ], } } } impl<'a> IgnoredSuffixes<'a> { pub fn add_suffix(&mut self, suffix: &'a str) { self.values.push(suffix) } pub fn strip_suffix(&self, file_name: &'a str) -> Option<&'a str> { for suffix in self.values.iter() { if let Some(stripped_file_name) = file_name.strip_suffix(suffix) { return Some(stripped_file_name); } } None } /// If we find an ignored suffix on the file name, e.g. '~', we strip it and /// then try again without it. pub fn try_with_stripped_suffix(&self, file_name: &'a OsStr, func: F) -> Result> where F: Fn(&'a OsStr) -> Result>, { if let Some(file_str) = Path::new(file_name).to_str() { if let Some(stripped_file_name) = self.strip_suffix(file_str) { return func(OsStr::new(stripped_file_name)); } } Ok(None) } } #[test] fn internal_suffixes() { let ignored_suffixes = IgnoredSuffixes::default(); let file_names = ignored_suffixes .values .iter() .map(|suffix| format!("test.json{suffix}")); for file_name_str in file_names { let file_name = OsStr::new(&file_name_str); let expected_stripped_file_name = OsStr::new("test.json"); let stripped_file_name = ignored_suffixes .try_with_stripped_suffix(file_name, |stripped_file_name| Ok(Some(stripped_file_name))); assert_eq!( expected_stripped_file_name, stripped_file_name.unwrap().unwrap() ); } } #[test] fn external_suffixes() { let mut ignored_suffixes = IgnoredSuffixes::default(); ignored_suffixes.add_suffix(".development"); ignored_suffixes.add_suffix(".production"); let file_names = ignored_suffixes .values .iter() .map(|suffix| format!("test.json{suffix}")); for file_name_str in file_names { let file_name = OsStr::new(&file_name_str); let expected_stripped_file_name = OsStr::new("test.json"); let stripped_file_name = ignored_suffixes .try_with_stripped_suffix(file_name, |stripped_file_name| Ok(Some(stripped_file_name))); assert_eq!( expected_stripped_file_name, stripped_file_name.unwrap().unwrap() ); } }