winprint_kit/
file_type.rs1use url::Url;
4
5const SUPPORTED_TYPES: &[&str] = &[
6 "pdf", "docx", "doc", "xlsx", "xls", "pptx", "ppt", "odt", "ods", "odp", "jpg", "jpeg", "png",
7 "gif", "bmp", "tif", "tiff", "webp", "html", "htm",
8 "xps",
9];
10
11const BOUNDARY_CHARS: &[u8] = b"?&#/\"' ;:";
12
13fn validate_extension(ext: &str) -> Option<String> {
14 let ext = ext.trim().to_lowercase();
15 if ext.is_empty() || ext.len() > 10 {
16 return None;
17 }
18 SUPPORTED_TYPES.contains(&ext.as_str()).then_some(ext)
19}
20
21fn is_boundary(s: &str, pos: usize) -> bool {
22 pos >= s.len() || BOUNDARY_CHARS.contains(&s.as_bytes()[pos])
23}
24
25fn extract_ext_from_filename(filename: &str) -> Option<String> {
26 let dot_pos = filename.rfind('.')?;
27 validate_extension(&filename[dot_pos + 1..])
28}
29
30pub fn detect_type_from_url(url: &str) -> Option<String> {
31 if let Ok(parsed) = Url::parse(url) {
32 if let Some(filename) = parsed.path_segments().and_then(|mut segs| segs.next_back()) {
33 if let Some(ext) = extract_ext_from_filename(filename) {
34 return Some(ext);
35 }
36 }
37
38 for (_key, value) in parsed.query_pairs() {
39 if let Some(ext) = extract_ext_from_filename(&value) {
40 return Some(ext);
41 }
42 }
43 }
44
45 let url_lower = url.to_lowercase();
46
47 SUPPORTED_TYPES.iter().find_map(|ext| {
48 [format!(".{}", ext), format!("%2e{}", ext)]
49 .into_iter()
50 .find_map(|pattern| {
51 url_lower.find(&pattern).and_then(|pos| {
52 is_boundary(&url_lower, pos + pattern.len()).then(|| ext.to_string())
53 })
54 })
55 })
56}
57
58pub fn detect_type_from_content_type(content_type: &str) -> Option<String> {
59 let ct = content_type.split(';').next()?.trim().to_lowercase();
60 let extension = match ct.as_str() {
61 "application/pdf" => "pdf",
62 "application/vnd.openxmlformats-officedocument.wordprocessingml.document" => "docx",
63 "application/msword" => "doc",
64 "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" => "xlsx",
65 "application/vnd.ms-excel" => "xls",
66 "application/vnd.openxmlformats-officedocument.presentationml.presentation" => "pptx",
67 "application/vnd.ms-powerpoint" => "ppt",
68 "application/vnd.oasis.opendocument.text" => "odt",
69 "application/vnd.oasis.opendocument.spreadsheet" => "ods",
70 "application/vnd.oasis.opendocument.presentation" => "odp",
71 "application/vnd.ms-xpsdocument" => "xps",
72 "image/jpeg" => "jpg",
73 "image/png" => "png",
74 "image/gif" => "gif",
75 "image/bmp" => "bmp",
76 "image/tiff" => "tiff",
77 "image/webp" => "webp",
78 "text/html" => "html",
79 "application/xhtml+xml" => "htm",
80 _ => return None,
81 };
82 Some(extension.to_string())
83}
84
85pub fn is_supported_type(file_type: &str) -> bool {
86 SUPPORTED_TYPES.contains(&file_type)
87}
88
89pub fn is_office_type(typ: &str) -> bool {
90 matches!(
91 typ,
92 "doc" | "docx" | "odt" | "xls" | "xlsx" | "ods" | "ppt" | "pptx" | "odp"
93 )
94}
95
96pub fn is_image_type(typ: &str) -> bool {
97 matches!(
98 typ,
99 "png" | "jpg" | "jpeg" | "gif" | "bmp" | "tiff" | "tif" | "webp"
100 )
101}
102
103pub fn is_html_type(typ: &str) -> bool {
104 matches!(typ, "html" | "htm")
105}