qubit_mime/detector/
mime_detector_backend.rs1use std::fmt::Debug;
13use std::fs::File;
14use std::path::Path;
15
16use qubit_io::ReadSeek;
17
18use crate::{
19 DetectionSource,
20 MimeDetectionPolicy,
21 MimeDetector,
22 MimeDetectorCore,
23 MimeResult,
24 StreamBasedMimeDetector,
25};
26
27use super::stream_based_mime_detector::read_prefix;
28
29pub trait MimeDetectorBackend: Debug + Send + Sync {
31 fn core(&self) -> &MimeDetectorCore;
36
37 fn max_test_bytes(&self) -> usize;
42
43 fn guess_from_filename(&self, filename: &str) -> Vec<String>;
51
52 fn guess_from_content(&self, content: &[u8]) -> MimeResult<Vec<String>>;
63
64 fn guess_from_reader(&self, reader: &mut dyn ReadSeek) -> MimeResult<(Vec<String>, Vec<u8>)> {
75 let content = read_prefix(reader, self.max_test_bytes())?;
76 let candidates = self.guess_from_content(&content)?;
77 Ok((candidates, content))
78 }
79
80 fn guess_from_file(&self, file: &Path) -> MimeResult<(Vec<String>, Vec<u8>)> {
91 let mut reader = File::open(file)?;
92 self.guess_from_reader(&mut reader)
93 }
94}
95
96impl<T> MimeDetector for T
97where
98 T: MimeDetectorBackend,
99{
100 fn detect_by_filename(&self, filename: &str) -> Option<String> {
102 self.guess_from_filename(filename).first().map(|mime_type| {
103 self.core()
104 .refine_detected_mime_type(mime_type, Some(filename), DetectionSource::None)
105 })
106 }
107
108 fn detect_by_content(&self, content: &[u8]) -> Option<String> {
110 self.guess_from_content(content)
111 .ok()?
112 .first()
113 .map(|mime_type| {
114 self.core().refine_detected_mime_type(
115 mime_type,
116 None,
117 DetectionSource::Content(content),
118 )
119 })
120 }
121
122 fn detect(
124 &self,
125 content: &[u8],
126 filename: Option<&str>,
127 policy: MimeDetectionPolicy,
128 ) -> Option<String> {
129 let from_filename = filename
130 .map(|filename| self.guess_from_filename(filename))
131 .unwrap_or_default();
132 let from_content =
133 if from_filename.len() == 1 && policy == MimeDetectionPolicy::PreferFilename {
134 Vec::new()
135 } else {
136 self.guess_from_content(content).unwrap_or_default()
137 };
138 self.core().select_result(
139 &from_filename,
140 &from_content,
141 filename,
142 policy,
143 DetectionSource::Content(content),
144 )
145 }
146
147 fn detect_reader(
149 &self,
150 reader: &mut dyn ReadSeek,
151 filename: Option<&str>,
152 policy: MimeDetectionPolicy,
153 ) -> MimeResult<Option<String>> {
154 let from_filename = filename
155 .map(|filename| self.guess_from_filename(filename))
156 .unwrap_or_default();
157 let (from_content, content) =
158 if from_filename.len() == 1 && policy == MimeDetectionPolicy::PreferFilename {
159 (Vec::new(), Vec::new())
160 } else {
161 self.guess_from_reader(reader)?
162 };
163 Ok(self.core().select_result(
164 &from_filename,
165 &from_content,
166 filename,
167 policy,
168 DetectionSource::Content(&content),
169 ))
170 }
171
172 fn detect_file(&self, file: &Path, policy: MimeDetectionPolicy) -> MimeResult<Option<String>> {
174 let filename = file.to_string_lossy();
175 let from_filename = self.guess_from_filename(&filename);
176 let (from_content, _content) =
177 if from_filename.len() == 1 && policy == MimeDetectionPolicy::PreferFilename {
178 (Vec::new(), Vec::new())
179 } else {
180 self.guess_from_file(file)?
181 };
182 Ok(self.core().select_result(
183 &from_filename,
184 &from_content,
185 Some(&filename),
186 policy,
187 DetectionSource::Path(file),
188 ))
189 }
190}
191
192impl<T> MimeDetectorBackend for T
193where
194 T: StreamBasedMimeDetector,
195{
196 fn core(&self) -> &MimeDetectorCore {
198 StreamBasedMimeDetector::core(self)
199 }
200
201 fn max_test_bytes(&self) -> usize {
203 StreamBasedMimeDetector::max_test_bytes(self)
204 }
205
206 fn guess_from_filename(&self, filename: &str) -> Vec<String> {
208 StreamBasedMimeDetector::guess_from_filename(self, filename)
209 }
210
211 fn guess_from_content(&self, content: &[u8]) -> MimeResult<Vec<String>> {
213 StreamBasedMimeDetector::guess_from_content_bytes(self, content)
214 }
215
216 fn guess_from_reader(&self, reader: &mut dyn ReadSeek) -> MimeResult<(Vec<String>, Vec<u8>)> {
218 StreamBasedMimeDetector::guess_from_reader_stream(self, reader)
219 }
220
221 fn guess_from_file(&self, file: &Path) -> MimeResult<(Vec<String>, Vec<u8>)> {
223 StreamBasedMimeDetector::guess_from_file_stream(self, file)
224 }
225}