fif/src/formats.rs

121 lines
3.3 KiB
Rust

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<Findings, (ScanError, PathBuf)>];
fn write_pathbuf<W: Write>(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<W: Write>(&self, f: &mut W, from: &PathBuf, to: &PathBuf) -> io::Result<()>;
fn no_known_extension<W: Write>(&self, f: &mut W, path: &PathBuf) -> io::Result<()>;
fn unreadable<W: Write>(&self, f: &mut W, path: &PathBuf) -> io::Result<()>;
fn unknown_type<W: Write>(&self, f: &mut W, path: &PathBuf) -> io::Result<()>;
fn header<W: Write>(&self, entries: &Entries, f: &mut W) -> io::Result<()>;
fn footer<W: Write>(&self, entries: &Entries, f: &mut W) -> io::Result<()>;
fn write_all<W: Write>(&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<W: Write>(&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<W: Write>(&self, f: &mut W, path: &PathBuf) -> io::Result<()> {
write!(f, "echo No known extension for ")?;
write_pathbuf(f, path)?;
writeln!(f,)
}
fn unreadable<W: Write>(&self, f: &mut W, path: &PathBuf) -> io::Result<()> {
write!(f, "# Failed to read ")?;
write_pathbuf(f, path)?;
writeln!(f,)
}
fn unknown_type<W: Write>(&self, f: &mut W, path: &PathBuf) -> io::Result<()> {
write!(f, "# Failed to detect mime type for ")?;
write_pathbuf(f, path)?;
writeln!(f,)
}
fn header<W: Write>(&self, _: &Entries, f: &mut W) -> io::Result<()> {
write!(
f,
"#!/usr/bin/env sh\n\nGenerated by fif {}.\n\n",
VERSION.unwrap_or("???")
)
}
fn footer<W: Write>(&self, _: &Entries, f: &mut W) -> io::Result<()> {
writeln!(f, "\necho Done.")
}
}