use std::io::{self, Write}; #[cfg(unix)] use std::os::unix::ffi::OsStrExt; #[cfg(windows)] use std::os::windows::ffi::OsStrExt; use std::path::PathBuf; use snailquote::escape; use crate::scanerror::ScanError; use crate::Findings; const VERSION: Option<&'static str> = option_env!("CARGO_PKG_VERSION"); type Entries = [Result]; fn write_pathbuf(f: &mut W, path: &PathBuf) -> io::Result<()> { match path.to_str() { Some(string) => { write!(f, "{}", escape(string)) } None => { write!(f, "'")?; #[cfg(unix)] f.write_all(&*path.as_os_str().as_bytes())?; #[cfg(windows)] f.write_all(&*path.as_os_str().encode_wide().collect())?; // TODO: TEST THIS write!(f, "'") } } } pub trait Format { fn new() -> Self; fn rename(&self, f: &mut W, from: &PathBuf, to: &PathBuf) -> io::Result<()>; fn no_known_extension(&self, f: &mut W, path: &PathBuf) -> io::Result<()>; fn unreadable(&self, f: &mut W, path: &PathBuf) -> io::Result<()>; fn unknown_type(&self, f: &mut W, path: &PathBuf) -> io::Result<()>; fn header(&self, entries: &Entries, f: &mut W) -> io::Result<()>; fn footer(&self, entries: &Entries, f: &mut W) -> io::Result<()>; fn write_all(&self, entries: &Entries, f: &mut W) -> io::Result<()> { // TODO: clean this up - it's horrifying self.header(entries, f)?; for entry in entries { match entry { Ok(finding) => { // the file was successfully scanned, and a mimetype was detected if !finding.valid { // the file's extension is wrong - check for known extension if let Some(ext) = finding.recommended_extension() { self.rename(f, &finding.file, &finding.file.with_extension(ext.as_str()))? } else { self.no_known_extension(f, &finding.file)? } } } Err(error) => { // something went wrong 0uo match error.0 { // failed to read the file ScanError::File => self.unreadable(f, &error.1)?, // file was read successfully, but we couldn't determine a mimetype ScanError::Mime => self.unknown_type(f, &error.1)?, } } } } self.footer(entries, f) } } pub struct Script {} impl Format for Script { fn new() -> Self { Self {} } fn rename(&self, f: &mut W, from: &PathBuf, to: &PathBuf) -> io::Result<()> { // TODO: surely there's a better way... write!(f, "mv -v -i -- ")?; write_pathbuf(f, from)?; write!(f, " ")?; write_pathbuf(f, to)?; writeln!(f,) } fn no_known_extension(&self, f: &mut W, path: &PathBuf) -> io::Result<()> { write!(f, "echo No known extension for ")?; write_pathbuf(f, path)?; writeln!(f,) } fn unreadable(&self, f: &mut W, path: &PathBuf) -> io::Result<()> { write!(f, "# Failed to read ")?; write_pathbuf(f, path)?; writeln!(f,) } fn unknown_type(&self, f: &mut W, path: &PathBuf) -> io::Result<()> { write!(f, "# Failed to detect mime type for ")?; write_pathbuf(f, path)?; writeln!(f,) } fn header(&self, _: &Entries, f: &mut W) -> io::Result<()> { write!( f, "#!/usr/bin/env sh\n\nGenerated by fif {}.\n\n", VERSION.unwrap_or("???") ) } fn footer(&self, _: &Entries, f: &mut W) -> io::Result<()> { writeln!(f, "\necho Done.") } }