use super::Cmd; use clap::{Arg, ArgAction, Command, ValueHint}; use std::{ffi::CString, fs, io, path::PathBuf}; #[derive(Debug, Default)] pub struct MkTemp; impl Cmd for MkTemp { fn cli(&self) -> clap::Command { Command::new("mktemp") .about("create a unique temporary file") .author("Nathan Fisher") .version(env!("CARGO_PKG_VERSION")) .args([ Arg::new("directory") .short('d') .long("directory") .help("create a directory instead of a file") .action(ArgAction::SetTrue), Arg::new("tmpdir") .short('p') .long("tmpdir") .help("specify the directory to create temporary files in") .value_hint(ValueHint::DirPath) .num_args(1), Arg::new("prefix") .short('t') .long("template") .help("specify a prefix to append to the created files") .num_args(1), Arg::new("dryrun") .short('u') .long("dryrun") .help("immediately remove created files and directories") .action(ArgAction::SetTrue), ]) } fn run(&self, matches: &clap::ArgMatches) -> Result<(), Box> { let mut path = if let Some(t) = matches.get_one::("tmpdir") { PathBuf::from(t) } else { PathBuf::from("/tmp") }; let fname = if let Some(p) = matches.get_one::("prefix") { format!("{p}.XXXXXX") } else { "mktemp.XXXXXX".to_string() }; path.push(&fname); let fname = path .to_str() .ok_or(io::Error::new(io::ErrorKind::Other, "utf8 error"))?; let cname = CString::new(fname)?; let ptr = cname.into_raw(); if matches.get_flag("directory") { let raw_fd = unsafe { libc::mkdtemp(ptr) }; if raw_fd.is_null() { return Err(io::Error::last_os_error().into()); } let cname = unsafe { CString::from_raw(ptr) }; let fname = cname.to_str()?; println!("{fname}"); if matches.get_flag("dryrun") { fs::remove_dir(&fname)?; } } else { let raw_fd = unsafe { libc::mkstemp(ptr) }; if raw_fd == -1 { return Err(io::Error::last_os_error().into()); } let cname = unsafe { CString::from_raw(ptr) }; let fname = cname.to_str()?; println!("{fname}"); if matches.get_flag("dryrun") { fs::remove_file(&fname)?; } } Ok(()) } fn path(&self) -> Option { Some(shitbox::Path::UsrBin) } }