1use crate::output::{EvaluationResult, MatchResult};
10
11#[must_use]
41pub fn format_text_result(result: &MatchResult) -> String {
42 result.message.clone()
43}
44
45#[must_use]
87pub fn format_text_output(results: &[MatchResult]) -> String {
88 if results.is_empty() {
89 return "data".to_string(); }
91
92 results
93 .iter()
94 .map(|result| result.message.as_str())
95 .collect::<Vec<&str>>()
96 .join(", ")
97}
98
99#[must_use]
136pub fn format_evaluation_result(evaluation: &EvaluationResult) -> String {
137 let filename = evaluation
138 .filename
139 .file_name()
140 .and_then(|name| name.to_str())
141 .unwrap_or("unknown");
142
143 let description = if evaluation.matches.is_empty() {
144 if let Some(ref error) = evaluation.error {
145 format!("ERROR: {error}")
146 } else {
147 "data".to_string()
148 }
149 } else {
150 format_text_output(&evaluation.matches)
151 };
152
153 format!("{filename}: {description}")
154}
155
156#[cfg(test)]
157mod tests {
158 use cfg_if::cfg_if;
159
160 use super::*;
161 use crate::output::EvaluationMetadata;
162 use crate::parser::ast::Value;
163 use std::path::PathBuf;
164
165 #[test]
166 fn test_format_text_result() {
167 let result = MatchResult::new(
168 "ELF 64-bit LSB executable".to_string(),
169 0,
170 Value::Bytes(vec![0x7f, 0x45, 0x4c, 0x46]),
171 );
172
173 let formatted = format_text_result(&result);
174 assert_eq!(formatted, "ELF 64-bit LSB executable");
175 }
176
177 #[test]
178 fn test_format_text_result_with_special_characters() {
179 let result = MatchResult::new(
180 "Text file with UTF-8 Unicode (with BOM) text".to_string(),
181 0,
182 Value::Bytes(vec![0xef, 0xbb, 0xbf]),
183 );
184
185 let formatted = format_text_result(&result);
186 assert_eq!(formatted, "Text file with UTF-8 Unicode (with BOM) text");
187 }
188
189 #[test]
190 fn test_format_text_output_single_result() {
191 let results = vec![MatchResult::new(
192 "PNG image data".to_string(),
193 0,
194 Value::Bytes(vec![0x89, 0x50, 0x4e, 0x47]),
195 )];
196
197 let formatted = format_text_output(&results);
198 assert_eq!(formatted, "PNG image data");
199 }
200
201 #[test]
202 fn test_format_text_output_multiple_results() {
203 let results = vec![
204 MatchResult::new(
205 "ELF 64-bit LSB executable".to_string(),
206 0,
207 Value::Bytes(vec![0x7f, 0x45, 0x4c, 0x46]),
208 ),
209 MatchResult::new("x86-64".to_string(), 18, Value::Uint(0x3e)),
210 MatchResult::new("version 1 (SYSV)".to_string(), 7, Value::Uint(0x01)),
211 MatchResult::new("dynamically linked".to_string(), 16, Value::Uint(0x02)),
212 ];
213
214 let formatted = format_text_output(&results);
215 assert_eq!(
216 formatted,
217 "ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked"
218 );
219 }
220
221 #[test]
222 fn test_format_text_output_empty_results() {
223 let results = vec![];
224 let formatted = format_text_output(&results);
225 assert_eq!(formatted, "data");
226 }
227
228 #[test]
229 fn test_format_text_output_with_confidence_variations() {
230 let results = vec![
232 MatchResult::with_metadata(
233 "JPEG image data".to_string(),
234 0,
235 2,
236 Value::Bytes(vec![0xff, 0xd8]),
237 vec!["image".to_string(), "jpeg".to_string()],
238 95,
239 Some("image/jpeg".to_string()),
240 ),
241 MatchResult::with_metadata(
242 "JFIF standard 1.01".to_string(),
243 6,
244 5,
245 Value::String("JFIF\0".to_string()),
246 vec!["image".to_string(), "jpeg".to_string(), "jfif".to_string()],
247 85,
248 None,
249 ),
250 ];
251
252 let formatted = format_text_output(&results);
253 assert_eq!(formatted, "JPEG image data, JFIF standard 1.01");
254 }
255
256 #[test]
257 fn test_format_evaluation_result_with_matches() {
258 let result = MatchResult::new(
259 "PNG image data".to_string(),
260 0,
261 Value::Bytes(vec![0x89, 0x50, 0x4e, 0x47]),
262 );
263
264 let metadata = EvaluationMetadata::new(2048, 1.5, 10, 1);
265 let evaluation = EvaluationResult::new(
266 PathBuf::from("/home/user/images/photo.png"),
267 vec![result],
268 metadata,
269 );
270
271 let formatted = format_evaluation_result(&evaluation);
272 assert_eq!(formatted, "photo.png: PNG image data");
273 }
274
275 #[test]
276 fn test_format_evaluation_result_with_multiple_matches() {
277 let results = vec![
278 MatchResult::new(
279 "ELF 64-bit LSB executable".to_string(),
280 0,
281 Value::Bytes(vec![0x7f, 0x45, 0x4c, 0x46]),
282 ),
283 MatchResult::new("x86-64".to_string(), 18, Value::Uint(0x3e)),
284 MatchResult::new("dynamically linked".to_string(), 16, Value::Uint(0x02)),
285 ];
286
287 let metadata = EvaluationMetadata::new(8192, 3.2, 25, 3);
288 let evaluation = EvaluationResult::new(PathBuf::from("/usr/bin/ls"), results, metadata);
289
290 let formatted = format_evaluation_result(&evaluation);
291 assert_eq!(
292 formatted,
293 "ls: ELF 64-bit LSB executable, x86-64, dynamically linked"
294 );
295 }
296
297 #[test]
298 fn test_format_evaluation_result_no_matches() {
299 let metadata = EvaluationMetadata::new(1024, 0.8, 5, 0);
300 let evaluation = EvaluationResult::new(PathBuf::from("unknown.bin"), vec![], metadata);
301
302 let formatted = format_evaluation_result(&evaluation);
303 assert_eq!(formatted, "unknown.bin: data");
304 }
305
306 #[test]
307 fn test_format_evaluation_result_with_error() {
308 let metadata = EvaluationMetadata::new(0, 0.0, 0, 0);
309 let evaluation = EvaluationResult::with_error(
310 PathBuf::from("missing.txt"),
311 "File not found".to_string(),
312 metadata,
313 );
314
315 let formatted = format_evaluation_result(&evaluation);
316 assert_eq!(formatted, "missing.txt: ERROR: File not found");
317 }
318
319 #[test]
320 fn test_format_evaluation_result_filename_extraction() {
321 let metadata = EvaluationMetadata::new(100, 0.5, 1, 0);
323
324 let eval1 = EvaluationResult::new(
326 PathBuf::from("/home/user/document.pdf"),
327 vec![],
328 metadata.clone(),
329 );
330 let formatted1 = format_evaluation_result(&eval1);
331 assert_eq!(formatted1, "document.pdf: data");
332
333 let eval2 = EvaluationResult::new(
335 PathBuf::from(r"C:\Users\user\file.exe"),
336 vec![],
337 metadata.clone(),
338 );
339 let formatted2 = format_evaluation_result(&eval2);
340 cfg_if! {
342 if #[cfg(windows)] {
343 assert_eq!(formatted2, "file.exe: data");
344 } else {
345 assert_eq!(formatted2, r"C:\Users\user\file.exe: data");
346 }
347 }
348 let eval3 =
350 EvaluationResult::new(PathBuf::from("./test/sample.dat"), vec![], metadata.clone());
351 let formatted3 = format_evaluation_result(&eval3);
352 assert_eq!(formatted3, "sample.dat: data");
353
354 let eval4 = EvaluationResult::new(PathBuf::from("simple.txt"), vec![], metadata);
356 let formatted4 = format_evaluation_result(&eval4);
357 assert_eq!(formatted4, "simple.txt: data");
358 }
359
360 #[test]
361 fn test_format_evaluation_result_edge_cases() {
362 let metadata = EvaluationMetadata::new(0, 0.0, 0, 0);
363
364 let eval1 = EvaluationResult::new(PathBuf::from(""), vec![], metadata.clone());
366 let formatted1 = format_evaluation_result(&eval1);
367 assert_eq!(formatted1, "unknown: data");
368
369 let eval2 = EvaluationResult::new(PathBuf::from("/"), vec![], metadata);
371 let formatted2 = format_evaluation_result(&eval2);
372 assert_eq!(formatted2, "unknown: data");
373 }
374
375 #[test]
376 fn test_format_text_output_preserves_message_order() {
377 let results = vec![
379 MatchResult::new("First".to_string(), 0, Value::Uint(1)),
380 MatchResult::new("Second".to_string(), 4, Value::Uint(2)),
381 MatchResult::new("Third".to_string(), 8, Value::Uint(3)),
382 ];
383
384 let formatted = format_text_output(&results);
385 assert_eq!(formatted, "First, Second, Third");
386 }
387
388 #[test]
389 fn test_format_text_result_handles_empty_message() {
390 let result = MatchResult::new(String::new(), 0, Value::Uint(0));
391 let formatted = format_text_result(&result);
392 assert_eq!(formatted, "");
393 }
394
395 #[test]
396 fn test_format_text_output_with_empty_messages() {
397 let results = vec![
398 MatchResult::new("Valid message".to_string(), 0, Value::Uint(1)),
399 MatchResult::new(String::new(), 4, Value::Uint(2)),
400 MatchResult::new("Another message".to_string(), 8, Value::Uint(3)),
401 ];
402
403 let formatted = format_text_output(&results);
404 assert_eq!(formatted, "Valid message, , Another message");
405 }
406
407 #[test]
408 fn test_format_text_output_realistic_file_types() {
409 let pdf_results = vec![
413 MatchResult::new(
414 "PDF document".to_string(),
415 0,
416 Value::String("%PDF".to_string()),
417 ),
418 MatchResult::new(
419 "version 1.4".to_string(),
420 5,
421 Value::String("1.4".to_string()),
422 ),
423 ];
424 assert_eq!(
425 format_text_output(&pdf_results),
426 "PDF document, version 1.4"
427 );
428
429 let zip_results = vec![
431 MatchResult::new(
432 "Zip archive data".to_string(),
433 0,
434 Value::String("PK".to_string()),
435 ),
436 MatchResult::new("at least v2.0 to extract".to_string(), 4, Value::Uint(20)),
437 ];
438 assert_eq!(
439 format_text_output(&zip_results),
440 "Zip archive data, at least v2.0 to extract"
441 );
442
443 let jpeg_results = vec![
445 MatchResult::new(
446 "JPEG image data".to_string(),
447 0,
448 Value::Bytes(vec![0xff, 0xd8]),
449 ),
450 MatchResult::new(
451 "JFIF standard 1.01".to_string(),
452 6,
453 Value::String("JFIF".to_string()),
454 ),
455 MatchResult::new("resolution (DPI)".to_string(), 13, Value::Uint(1)),
456 MatchResult::new("density 72x72".to_string(), 14, Value::Uint(72)),
457 ];
458 assert_eq!(
459 format_text_output(&jpeg_results),
460 "JPEG image data, JFIF standard 1.01, resolution (DPI), density 72x72"
461 );
462 }
463}