1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-28 19:47:45 +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; extern crate uucore;
use clap::{crate_version, App, Arg}; use clap::{crate_version, App, Arg};
use uucore::error::{FromIo, UCustomError, UResult};
use std::env; use std::env;
use std::error::Error;
use std::fmt::Display;
use std::iter; use std::iter;
use std::path::{is_separator, PathBuf}; use std::path::{is_separator, PathBuf};
@ -37,7 +40,40 @@ fn get_usage() -> String {
format!("{0} [OPTION]... [TEMPLATE]", executable!()) 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 usage = get_usage();
let matches = uu_app().usage(&usage[..]).get_matches_from(args); 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 dry_run = matches.is_present(OPT_DRY_RUN);
let suppress_file_err = matches.is_present(OPT_QUIET); let suppress_file_err = matches.is_present(OPT_QUIET);
let (prefix, rand, suffix) = match parse_template(template) { let (prefix, rand, suffix) = parse_template(template, matches.value_of(OPT_SUFFIX))?;
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");
}
if matches.is_present(OPT_TMPDIR) && PathBuf::from(prefix).is_absolute() { if matches.is_present(OPT_TMPDIR) && PathBuf::from(prefix).is_absolute() {
show_error!( return Err(MkTempError::InvalidTemplate(template.into()).into());
"invalid template, '{}'; with --tmpdir, it may not be absolute", }
template
);
return 1;
};
if matches.is_present(OPT_T) { if matches.is_present(OPT_T) {
tmpdir = env::temp_dir() tmpdir = env::temp_dir()
}; }
if dry_run { let res = if dry_run {
dry_exec(tmpdir, prefix, rand, suffix) dry_exec(tmpdir, prefix, rand, suffix)
} else { } 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') { let right = match temp.rfind('X') {
Some(r) => r + 1, 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 left = temp[..right].rfind(|c| c != 'X').map_or(0, |i| i + 1);
let prefix = &temp[..left]; let prefix = &temp[..left];
let rand = right - 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 len = prefix.len() + suffix.len() + rand;
let mut buf = String::with_capacity(len); let mut buf = String::with_capacity(len);
buf.push_str(prefix); buf.push_str(prefix);
@ -208,51 +245,35 @@ pub fn dry_exec(mut tmpdir: PathBuf, prefix: &str, rand: usize, suffix: &str) ->
} }
tmpdir.push(buf); tmpdir.push(buf);
println!("{}", tmpdir.display()); println!("{}", tmpdir.display());
0 Ok(())
} }
fn exec(dir: PathBuf, prefix: &str, rand: usize, suffix: &str, make_dir: bool, quiet: bool) -> i32 { fn exec(dir: PathBuf, prefix: &str, rand: usize, suffix: &str, make_dir: bool) -> UResult<()> {
let res = if make_dir { let context = || {
let tmpdir = Builder::new() format!(
.prefix(prefix) "failed to create file via template '{}{}{}'",
.rand_bytes(rand) prefix,
.suffix(suffix) "X".repeat(rand),
.tempdir_in(&dir); suffix
)
// `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),
}
}; };
match res { let mut builder = Builder::new();
Ok(ref f) => { builder.prefix(prefix).rand_bytes(rand).suffix(suffix);
println!("{}", f);
0 let path = if make_dir {
} builder
Err(e) => { .tempdir_in(&dir)
if !quiet { .map_err_context(context)?
show_error!("{}: {}", e, dir.display()); .into_path() // `into_path` consumes the TempDir without removing it
} } else {
1 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) .arg(TEST_TEMPLATE8)
.fails() .fails()
.no_stdout() .no_stdout()
.stderr_contains("suffix cannot contain any path separators"); .stderr_contains("invalid suffix")
.stderr_contains("contains directory separator");
} }
#[test] #[test]