use crate::cache::TemplateSourceMatcher;
pub struct FileExtensionMatcher {
extension: String,
case_insensitive: bool,
}
impl FileExtensionMatcher {
pub fn new(extension: &str) -> Self {
if extension.contains('/') {
panic!("A file extension can't contain \"/\": {extension}");
}
if extension.contains('*') {
panic!("A file extension can't contain \"*\": {extension}");
}
if extension.contains('?') {
panic!("A file extension can't contain \"*\": {extension}");
}
if extension.starts_with('.') {
panic!("A file extension can't start with \".\": {extension}");
}
FileExtensionMatcher {
extension: extension.to_string(),
case_insensitive: true,
}
}
pub fn is_case_insensitive(&self) -> bool {
self.case_insensitive
}
pub fn set_case_insensitive(&mut self, case_insensitive: bool) {
self.case_insensitive = case_insensitive;
}
pub fn case_insensitive(mut self, case_insensitive: bool) -> Self {
self.set_case_insensitive(case_insensitive);
self
}
}
impl TemplateSourceMatcher for FileExtensionMatcher {
fn matches(&self, source_name: &str) -> bool {
let ln = source_name.len();
let ext_ln = self.extension.len();
if ln < ext_ln + 1 || source_name.as_bytes()[ln - ext_ln - 1] != b'.' {
return false;
}
let ext_part = &source_name[ln - ext_ln..];
if self.case_insensitive {
ext_part.to_lowercase() == self.extension.to_lowercase()
} else {
ext_part == self.extension
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn extension_matching() {
let m = FileExtensionMatcher::new("ftlh");
assert!(m.matches("foo.ftlh"));
assert!(m.matches("foo/bar.ftlh"));
assert!(m.matches("foo.FTLH"), "默认大小写不敏感");
assert!(!m.matches("foo.ftl"));
assert!(!m.matches("fooftlh"));
assert!(!m.matches("foo.ftlhx"));
let m = FileExtensionMatcher::new("ftlh").case_insensitive(false);
assert!(m.matches("foo.ftlh"));
assert!(!m.matches("foo.FTLH"));
}
#[test]
#[should_panic]
fn slash_rejected() {
let _ = FileExtensionMatcher::new("a/b");
}
}