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