repo_description: A cat(1) clone with wings.
repo_topics: cli command-line git hacktoberfest rust syntax-highlighting terminal tool
code: 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<T, F>(&self, file_name: &'a OsStr, func: F) -> Result<Option<T>>
      where
          F: Fn(&'a OsStr) -> Result<Option<T>>,
      {
          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()
          );
      }
  }
  