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:
parent
8c5052fcb7
commit
73a7ead857
2 changed files with 105 additions and 83 deletions
|
@ -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(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -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]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue