use std::convert::TryFrom;
use std::ffi::OsString;
use std::fs::OpenOptions;
use std::path::{Path, PathBuf};
use log::info;
use sanitize_filename_reader_friendly::sanitize;
use crate::{Vec2d, ZoomError};
pub fn reserve_output_file(path: &Path) -> Result<(), ZoomError> {
OpenOptions::new().write(true).create_new(true).open(path)?;
Ok(())
}
pub fn get_outname(
outfile: &Option<PathBuf>,
zoom_name: &Option<String>,
base_dir: &Path,
size: Option<Vec2d>,
) -> PathBuf {
let fits_in_jpg = size
.map(|Vec2d { x, y }| u16::try_from(x.max(y)).is_ok());
let extension = if fits_in_jpg == Some(true) { "jpg" } else { "png" };
if let Some(path) = outfile {
if let Some(forced_extension) = path.extension() {
if fits_in_jpg == Some(false) && (forced_extension == "jpg" || forced_extension == "jpeg") {
log::error!("This file is too large to be saved as JPEG")
}
path.into()
} else {
path.with_extension(extension)
}
} else {
let base = zoom_name.as_ref()
.map(|s| sanitize(s))
.filter(|s| !s.is_empty())
.unwrap_or_else(|| "dezoomified".into());
let mut path = base_dir.join(base).with_extension(extension);
let filename = path.file_stem().map(OsString::from).unwrap_or_default();
let ext = path.extension().map(OsString::from).unwrap_or_default();
for i in 1.. {
if !path.exists() { break; }
info!("File {:?} already exists. Trying another file name...", &path);
let mut name = OsString::from(&filename);
name.push(&format!("_{:04}.", i));
name.push(&ext);
path.set_file_name(name);
}
path
}
}
#[allow(clippy::expect_fun_call)]
#[cfg(test)]
mod tests {
use std::env::{current_dir, set_current_dir};
use std::error::Error;
use std::fs::{File, remove_file};
use std::path::Path;
use std::sync::Mutex;
use tempdir::TempDir;
use super::*;
fn in_tmp_dir<T, F: Fn(&Path) -> T>(f: F) -> T {
lazy_static::lazy_static! {
static ref CWD_MUTEX: Mutex<()> = Mutex::new(());
}
let tmp = tempdir::TempDir::new("dezoomify-rs-tests")
.expect("Unable to create a temporary directory to run the tests in");
let lock = CWD_MUTEX.lock().unwrap(); let cwd = current_dir().expect("Unable to getcwd");
set_current_dir(&tmp).expect(&format!("Unable to cd into {:?}", &tmp));
let res = f(tmp.as_ref());
set_current_dir(&cwd).expect(&format!("Unable to cd into {:?}", &cwd));
drop(lock);
res
}
fn assert_filename_ok(filename: &str) -> Result<(), Box<dyn Error>> {
let base_dir = TempDir::new("dezoomify-rs-test-filename")?;
let outname = get_outname(&None, &Some(filename.to_string()), base_dir.as_ref(), None);
assert!(!outname.exists(), "get_outname cannot overwrite {:?}", outname);
File::create(&outname)
.expect(&format!("Could not to create a file named {:?} for input {:?}", outname, filename));
remove_file(&outname)?;
Ok(())
}
#[test]
fn test_special_chars() {
let filenames = vec![
"? [Question Mark] Australian WWI Poster",
"The Rocky Mountains, Lander's Peak",
"\"Is It So Nominated in the Bond?\" (Scene from \"The Merchant of Venice\")",
"", ];
for filename in filenames {
assert_filename_ok(filename).expect(&format!("Invalid filename {}", filename))
}
}
#[test]
fn test_existing_file() {
in_tmp_dir(|cwd| {
let name = cwd.join("xxx");
File::create(&name).expect("cannot create file");
assert_filename_ok(&name.to_string_lossy()).expect("Invalid file name")
})
}
#[test]
fn switch_to_png_for_large_files() {
let base_dir = TempDir::new("dezoomify-rs-test-png").unwrap();
let base = |s| base_dir.as_ref().join(s);
let tests = vec![
(None, Some("hello".to_string()), None, base("hello.png")),
(None, Some("hello".to_string()), Some(Vec2d { x: 1000, y: 1000 }), base("hello.jpg"), ),
(None, Some(String::new()), None, base("dezoomified.png"), ),
(None, None, None, base("dezoomified.png")),
(None, None, Some(Vec2d { x: 1000, y: 1000 }), base("dezoomified.jpg")),
(Some("test.tiff".into()), Some("hello".to_string()), Some(Vec2d { x: 1000, y: 1000 }), "test.tiff".into()),
];
for (outfile, zoom_name, size, expected_result) in tests.into_iter() {
let outname = get_outname(&outfile, &zoom_name, base_dir.as_ref(), size);
assert_eq!(outname, expected_result);
}
}
}