qubit_mime/detector/
mime_detector_backend.rs1use std::fmt::Debug;
11use std::path::Path;
12
13use qubit_io::ReadSeek;
14use qubit_local_files::{FileReadOptions, LocalFiles};
15
16use crate::{
17 DetectionSource, MimeDetectionPolicy, MimeDetector, MimeDetectorCore, MimeResult,
18 StreamBasedMimeDetector,
19};
20
21use super::stream_based_mime_detector::read_prefix;
22
23pub trait MimeDetectorBackend: Debug + Send + Sync {
25 fn core(&self) -> &MimeDetectorCore;
30
31 fn max_test_bytes(&self) -> usize;
36
37 fn guess_from_filename(&self, filename: &str) -> Vec<String>;
45
46 fn guess_from_content(&self, content: &[u8]) -> MimeResult<Vec<String>>;
57
58 fn guess_from_reader(&self, reader: &mut dyn ReadSeek) -> MimeResult<(Vec<String>, Vec<u8>)> {
69 let content = read_prefix(reader, self.max_test_bytes(), self.core().max_buffer_size())?;
70 let candidates = self.guess_from_content(&content)?;
71 Ok((candidates, content))
72 }
73
74 fn guess_from_file(&self, file: &Path) -> MimeResult<(Vec<String>, Vec<u8>)> {
86 let mut reader = LocalFiles::open_reader(file, FileReadOptions::buffered())?;
87 self.guess_from_reader(&mut reader)
88 }
89}
90
91impl<T> MimeDetector for T
92where
93 T: MimeDetectorBackend,
94{
95 fn detect_by_filename(&self, filename: &str) -> Option<String> {
97 self.guess_from_filename(filename).first().map(|mime_type| {
98 self.core()
99 .refine_detected_mime_type(mime_type, Some(filename), DetectionSource::None)
100 })
101 }
102
103 fn detect_by_content(&self, content: &[u8]) -> Option<String> {
105 self.guess_from_content(content)
106 .ok()?
107 .first()
108 .map(|mime_type| {
109 self.core().refine_detected_mime_type(
110 mime_type,
111 None,
112 DetectionSource::Content(content),
113 )
114 })
115 }
116
117 fn detect(
119 &self,
120 content: &[u8],
121 filename: Option<&str>,
122 policy: MimeDetectionPolicy,
123 ) -> Option<String> {
124 let from_filename = filename
125 .map(|filename| self.guess_from_filename(filename))
126 .unwrap_or_default();
127 let from_content =
128 if from_filename.len() == 1 && policy == MimeDetectionPolicy::PreferFilename {
129 Vec::new()
130 } else {
131 self.guess_from_content(content).unwrap_or_default()
132 };
133 self.core().select_result(
134 &from_filename,
135 &from_content,
136 filename,
137 policy,
138 DetectionSource::Content(content),
139 )
140 }
141
142 fn detect_reader(
144 &self,
145 reader: &mut dyn ReadSeek,
146 filename: Option<&str>,
147 policy: MimeDetectionPolicy,
148 ) -> MimeResult<Option<String>> {
149 let from_filename = filename
150 .map(|filename| self.guess_from_filename(filename))
151 .unwrap_or_default();
152 let (from_content, content) =
153 if from_filename.len() == 1 && policy == MimeDetectionPolicy::PreferFilename {
154 (Vec::new(), Vec::new())
155 } else {
156 self.guess_from_reader(reader)?
157 };
158 Ok(self.core().select_result(
159 &from_filename,
160 &from_content,
161 filename,
162 policy,
163 DetectionSource::Content(&content),
164 ))
165 }
166
167 fn detect_file(&self, file: &Path, policy: MimeDetectionPolicy) -> MimeResult<Option<String>> {
169 let filename = file.to_string_lossy();
170 let from_filename = self.guess_from_filename(&filename);
171 let (from_content, _content) =
172 if from_filename.len() == 1 && policy == MimeDetectionPolicy::PreferFilename {
173 (Vec::new(), Vec::new())
174 } else {
175 self.guess_from_file(file)?
176 };
177 Ok(self.core().select_result(
178 &from_filename,
179 &from_content,
180 Some(&filename),
181 policy,
182 DetectionSource::Path(file),
183 ))
184 }
185}
186
187impl<T> MimeDetectorBackend for T
188where
189 T: StreamBasedMimeDetector,
190{
191 fn core(&self) -> &MimeDetectorCore {
193 StreamBasedMimeDetector::core(self)
194 }
195
196 fn max_test_bytes(&self) -> usize {
198 StreamBasedMimeDetector::max_test_bytes(self)
199 }
200
201 fn guess_from_filename(&self, filename: &str) -> Vec<String> {
203 StreamBasedMimeDetector::guess_from_filename(self, filename)
204 }
205
206 fn guess_from_content(&self, content: &[u8]) -> MimeResult<Vec<String>> {
208 StreamBasedMimeDetector::guess_from_content_bytes(self, content)
209 }
210
211 fn guess_from_reader(&self, reader: &mut dyn ReadSeek) -> MimeResult<(Vec<String>, Vec<u8>)> {
213 StreamBasedMimeDetector::guess_from_reader_stream(self, reader)
214 }
215
216 fn guess_from_file(&self, file: &Path) -> MimeResult<(Vec<String>, Vec<u8>)> {
218 StreamBasedMimeDetector::guess_from_file_stream(self, file)
219 }
220}