use std::path::Path; use anyhow::Context; /// If the given string ends up with the given suffix, returns the portion of /// the string before the suffix. pub fn match_trailing<'a>(input: &'a str, suffix: &str) -> Option<&'a str> { if input.ends_with(suffix) { let end = input.len().saturating_sub(suffix.len()); Some(&input[..end]) } else { None } } pub trait PathExt { fn file_name_ends_with(&self, suffix: &str) -> bool; fn file_name_trim_end<'a>(&'a self, suffix: &str) -> anyhow::Result<&'a str>; } impl

PathExt for P where P: AsRef, { fn file_name_ends_with(&self, suffix: &str) -> bool { self.as_ref() .file_name() .and_then(|name| name.to_str()) .map(|name| name.ends_with(suffix)) .unwrap_or(false) } fn file_name_trim_end<'a>(&'a self, suffix: &str) -> anyhow::Result<&'a str> { let path = self.as_ref(); let file_name = path .file_name() .and_then(|name| name.to_str()) .with_context(|| format!("Path did not have a file name: {}", path.display()))?; match_trailing(&file_name, suffix) .with_context(|| format!("Path did not end in {}: {}", suffix, path.display())) } }