use std::path::{MAIN_SEPARATOR, Path};
const STDIO_PATH: &str = "-";
const NO_PATH_SPECIFIED: &str = "output file path must be specified.";
pub fn check_outfile_path(path: &str) -> Result<String, String> {
if path == STDIO_PATH {
return Ok(path.to_string());
}
if path.is_empty() {
return Err(NO_PATH_SPECIFIED.to_string());
}
if path.ends_with('/') || path.ends_with(MAIN_SEPARATOR) {
return Err(dir_error(path));
}
if Path::new(path).is_dir() {
return Err(dir_error(path));
}
Ok(path.to_string())
}
fn dir_error(path: &str) -> String {
format!("output file path must not be a directory: {path}")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn accepts_stdio_dash() {
assert_eq!(check_outfile_path("-").unwrap(), "-");
}
#[test]
fn accepts_nonexistent_file_path() {
let path = "/nonexistent/dir123/out.bin";
assert_eq!(check_outfile_path(path).unwrap(), path);
}
#[test]
fn rejects_empty_path() {
assert!(check_outfile_path("").is_err());
}
#[test]
fn rejects_current_dir() {
let err = check_outfile_path(".").unwrap_err();
assert!(err.contains("directory"), "unexpected err: {err}");
}
#[test]
fn rejects_parent_dir() {
let err = check_outfile_path("..").unwrap_err();
assert!(err.contains("directory"), "unexpected err: {err}");
}
#[test]
fn rejects_existing_directory() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().to_string_lossy().to_string();
let err = check_outfile_path(&path).unwrap_err();
assert!(err.contains("directory"), "unexpected err: {err}");
}
#[test]
fn rejects_trailing_separator_even_when_nonexistent() {
let path = format!("/nonexistent/dir123{MAIN_SEPARATOR}");
let err = check_outfile_path(&path).unwrap_err();
assert!(err.contains("directory"), "unexpected err: {err}");
}
#[test]
fn rejects_trailing_forward_slash() {
let err = check_outfile_path("some/dir/").unwrap_err();
assert!(err.contains("directory"), "unexpected err: {err}");
}
}