kael_document/
file_type.rs1use std::path::Path;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub struct FileType {
8 pub name: &'static str,
10 pub extensions: &'static [&'static str],
12 pub uti: Option<&'static str>,
14 pub mime: Option<&'static str>,
16}
17
18pub fn file_type_index_for_path(path: &Path, file_types: &[FileType]) -> Option<usize> {
20 let extension = path.extension()?.to_str()?.to_ascii_lowercase();
21 file_types.iter().position(|file_type| {
22 file_type
23 .extensions
24 .iter()
25 .map(|candidate| candidate.trim_start_matches('.'))
26 .any(|candidate| !candidate.is_empty() && candidate.eq_ignore_ascii_case(&extension))
27 })
28}
29
30pub fn default_file_type_index(file_types: &[FileType]) -> Option<usize> {
32 (!file_types.is_empty()).then_some(0)
33}
34
35#[cfg(test)]
36mod tests {
37 use super::*;
38
39 const TYPES: &[FileType] = &[FileType {
40 name: "Text",
41 extensions: &["txt", ".md", ""],
42 uti: None,
43 mime: Some("text/plain"),
44 }];
45
46 #[test]
47 fn extension_matching_is_case_insensitive_and_accepts_leading_dots() {
48 assert_eq!(
49 file_type_index_for_path(Path::new("README.MD"), TYPES),
50 Some(0)
51 );
52 assert_eq!(
53 file_type_index_for_path(Path::new("notes.TXT"), TYPES),
54 Some(0)
55 );
56 assert_eq!(
57 file_type_index_for_path(Path::new("trailing."), TYPES),
58 None
59 );
60 }
61}