use std;
pub fn file_new_append_incremental (file_path : &std::path::Path)
-> Result <(std::path::PathBuf, std::fs::File), std::io::Error>
{
let file_pathbuf = file_path_incremental (file_path)?;
let file = file_new_append (file_pathbuf.as_path())?;
Ok ((file_pathbuf, file))
}
pub fn file_new_append (file_path : &std::path::Path)
-> Result <std::fs::File, std::io::Error>
{
if !is_file (file_path)? {
return Err (std::io::Error::new (std::io::ErrorKind::InvalidInput,
"not a file".to_string()))
}
let dir = file_path.parent().unwrap_or_else (|| std::path::Path::new (""));
std::fs::create_dir_all (dir)?;
std::fs::OpenOptions::new().append (true).create_new (true).open (file_path)
}
pub fn file_path_incremental (file_path : &std::path::Path)
-> Result <std::path::PathBuf, std::io::Error>
{
if !is_file (file_path)? {
return Err (std::io::Error::new (
std::io::ErrorKind::InvalidInput, "not a file".to_string()))
}
let file_name = file_path.file_name().unwrap_or_else (
|| panic!("fatal: path should be a valid file")
).to_str().unwrap_or_else (
|| panic!("fatal: `file_path.file_name()` \
returned invalid os str: {:?}", file_path.file_name()));
let dir = file_path.parent().unwrap_or_else (|| std::path::Path::new (""));
for i in 0.. {
let name = String::from (file_name) + &format!("-{}", i);
let fp = dir.join (name);
if !fp.exists() {
return Ok (fp)
}
}
unreachable!("fatal: incremental file name loop should have returned")
}
pub fn file_path_incremental_with_extension (file_path : &std::path::Path)
-> Result <std::path::PathBuf, std::io::Error>
{
if !is_file (file_path)? {
return Err (std::io::Error::new (
std::io::ErrorKind::InvalidInput, "not a file".to_string()))
}
if file_path.extension().is_none() {
return file_path_incremental (file_path)
}
let extension = file_path.extension().unwrap().to_str().unwrap();
let file_stem = file_path.file_stem()
.unwrap_or_else (|| panic!("fatal: path should be a valid file")).to_str()
.unwrap_or_else (|| panic!("fatal: `file_path.file_name()` \
returned invalid os str: {:?}", file_path.file_name()));
let dir = file_path.parent().unwrap_or_else (|| std::path::Path::new (""));
for i in 0.. {
let name = &format!("{}-{}.{}", file_stem, i, extension);
let fp = dir.join (name);
if !fp.exists() {
return Ok (fp)
}
}
unreachable!("fatal: incremental file name loop should have returned")
}
pub fn is_file (file_path : &std::path::Path) -> Result <bool, std::io::Error> {
let s = file_path.to_str().ok_or (std::io::Error::new (
std::io::ErrorKind::InvalidInput, "not valid unicode".to_string()))?;
if s.ends_with (std::path::MAIN_SEPARATOR) {
return Ok (false)
}
if let None = std::path::Path::new (file_path).file_name() {
return Ok (false)
}
Ok (true)
}
#[cfg(test)]
mod tests {
use std;
use tempfile;
use quickcheck;
use super::*;
#[ignore] #[quickcheck_macros::quickcheck]
fn prop_is_file_implies_not_directory (file_path : String)
-> quickcheck::TestResult
{
let file_path = std::path::Path::new (file_path.as_str());
if !is_file (file_path).unwrap() {
return quickcheck::TestResult::discard()
}
if let Some (s) = file_path.parent() {
if !s.to_str().unwrap().is_empty() {
return quickcheck::TestResult::discard()
}
}
let temp_dir = tempfile::Builder::new().prefix ("tmp").tempdir().unwrap();
let file_path = temp_dir.path().join (file_path);
quickcheck::TestResult::from_bool (
if let Err(e) = std::fs::OpenOptions::new()
.append (true).create (true).open (file_path.clone())
{
e.kind() != std::io::ErrorKind::Other
} else {
true
}
)
}
}