1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-28 11:37:44 +00:00

mktemp: adapt to standardized error handling

This commit is contained in:
Terts Diepraam 2021-06-28 13:45:15 +02:00
parent 8c5052fcb7
commit 73a7ead857
2 changed files with 105 additions and 83 deletions

View file

@ -12,8 +12,11 @@
extern crate uucore;
use clap::{crate_version, App, Arg};
use uucore::error::{FromIo, UCustomError, UResult};
use std::env;
use std::error::Error;
use std::fmt::Display;
use std::iter;
use std::path::{is_separator, PathBuf};
@ -37,7 +40,40 @@ fn get_usage() -> String {
format!("{0} [OPTION]... [TEMPLATE]", executable!())
}
pub fn uumain(args: impl uucore::Args) -> i32 {
#[derive(Debug)]
enum MkTempError {
PersistError(PathBuf),
MustEndInX(String),
TooFewXs(String),
ContainsDirSeparator(String),
InvalidTemplate(String),
}
impl UCustomError for MkTempError {}
impl Error for MkTempError {}
impl Display for MkTempError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use MkTempError::*;
match self {
PersistError(p) => write!(f, "could not persist file '{}'", p.display()),
MustEndInX(s) => write!(f, "with --suffix, template '{}' must end in X", s),
TooFewXs(s) => write!(f, "too few X's in template '{}'", s),
ContainsDirSeparator(s) => {
write!(f, "invalid suffix '{}', contains directory separator", s)
}
InvalidTemplate(s) => write!(
f,
"invalid template, '{}'; with --tmpdir, it may not be absolute",
s
),
}
}
}
#[uucore_procs::gen_uumain]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let usage = get_usage();
let matches = uu_app().usage(&usage[..]).get_matches_from(args);
@ -73,47 +109,27 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
let dry_run = matches.is_present(OPT_DRY_RUN);
let suppress_file_err = matches.is_present(OPT_QUIET);
let (prefix, rand, suffix) = match parse_template(template) {
Some((p, r, s)) => match matches.value_of(OPT_SUFFIX) {
Some(suf) => {
if s.is_empty() {
(p, r, suf)
} else {
crash!(
1,
"Template should end with 'X' when you specify suffix option."
)
}
}
None => (p, r, s),
},
None => ("", 0, ""),
};
if rand < 3 {
crash!(1, "Too few 'X's in template")
}
if suffix.chars().any(is_separator) {
crash!(1, "suffix cannot contain any path separators");
}
let (prefix, rand, suffix) = parse_template(template, matches.value_of(OPT_SUFFIX))?;
if matches.is_present(OPT_TMPDIR) && PathBuf::from(prefix).is_absolute() {
show_error!(
"invalid template, '{}'; with --tmpdir, it may not be absolute",
template
);
return 1;
};
return Err(MkTempError::InvalidTemplate(template.into()).into());
}
if matches.is_present(OPT_T) {
tmpdir = env::temp_dir()
};
}
if dry_run {
let res = if dry_run {
dry_exec(tmpdir, prefix, rand, suffix)
} else {
exec(tmpdir, prefix, rand, suffix, make_dir, suppress_file_err)
exec(tmpdir, prefix, rand, suffix, make_dir)
};
if suppress_file_err {
// Mapping all UErrors to ExitCodes prevents the errors from being printed
res.map_err(|e| e.code().into())
} else {
res
}
}
@ -173,19 +189,40 @@ pub fn uu_app() -> App<'static, 'static> {
)
}
fn parse_template(temp: &str) -> Option<(&str, usize, &str)> {
fn parse_template<'a>(
temp: &'a str,
suffix: Option<&'a str>,
) -> UResult<(&'a str, usize, &'a str)> {
let right = match temp.rfind('X') {
Some(r) => r + 1,
None => return None,
None => return Err(MkTempError::TooFewXs(temp.into()).into()),
};
let left = temp[..right].rfind(|c| c != 'X').map_or(0, |i| i + 1);
let prefix = &temp[..left];
let rand = right - left;
let suffix = &temp[right..];
Some((prefix, rand, suffix))
if rand < 3 {
return Err(MkTempError::TooFewXs(temp.into()).into());
}
let mut suf = &temp[right..];
if let Some(s) = suffix {
if suf.is_empty() {
suf = s;
} else {
return Err(MkTempError::MustEndInX(temp.into()).into());
}
};
if suf.chars().any(is_separator) {
return Err(MkTempError::ContainsDirSeparator(suf.into()).into());
}
Ok((prefix, rand, suf))
}
pub fn dry_exec(mut tmpdir: PathBuf, prefix: &str, rand: usize, suffix: &str) -> i32 {
pub fn dry_exec(mut tmpdir: PathBuf, prefix: &str, rand: usize, suffix: &str) -> UResult<()> {
let len = prefix.len() + suffix.len() + rand;
let mut buf = String::with_capacity(len);
buf.push_str(prefix);
@ -208,51 +245,35 @@ pub fn dry_exec(mut tmpdir: PathBuf, prefix: &str, rand: usize, suffix: &str) ->
}
tmpdir.push(buf);
println!("{}", tmpdir.display());
0
Ok(())
}
fn exec(dir: PathBuf, prefix: &str, rand: usize, suffix: &str, make_dir: bool, quiet: bool) -> i32 {
let res = if make_dir {
let tmpdir = Builder::new()
.prefix(prefix)
.rand_bytes(rand)
.suffix(suffix)
.tempdir_in(&dir);
// `into_path` consumes the TempDir without removing it
tmpdir.map(|d| d.into_path().to_string_lossy().to_string())
} else {
let tmpfile = Builder::new()
.prefix(prefix)
.rand_bytes(rand)
.suffix(suffix)
.tempfile_in(&dir);
match tmpfile {
Ok(f) => {
// `keep` ensures that the file is not deleted
match f.keep() {
Ok((_, p)) => Ok(p.to_string_lossy().to_string()),
Err(e) => {
show_error!("'{}': {}", dir.display(), e);
return 1;
}
}
}
Err(x) => Err(x),
}
fn exec(dir: PathBuf, prefix: &str, rand: usize, suffix: &str, make_dir: bool) -> UResult<()> {
let context = || {
format!(
"failed to create file via template '{}{}{}'",
prefix,
"X".repeat(rand),
suffix
)
};
match res {
Ok(ref f) => {
println!("{}", f);
0
}
Err(e) => {
if !quiet {
show_error!("{}: {}", e, dir.display());
}
1
}
}
let mut builder = Builder::new();
builder.prefix(prefix).rand_bytes(rand).suffix(suffix);
let path = if make_dir {
builder
.tempdir_in(&dir)
.map_err_context(context)?
.into_path() // `into_path` consumes the TempDir without removing it
} else {
builder
.tempfile_in(&dir)
.map_err_context(context)?
.keep() // `keep` ensures that the file is not deleted
.map_err(|e| MkTempError::PersistError(e.file.path().to_path_buf()))?
.1
};
println!("{}", path.display());
Ok(())
}

View file

@ -125,7 +125,8 @@ fn test_mktemp_mktemp_t() {
.arg(TEST_TEMPLATE8)
.fails()
.no_stdout()
.stderr_contains("suffix cannot contain any path separators");
.stderr_contains("invalid suffix")
.stderr_contains("contains directory separator");
}
#[test]