Skip to content

Commit

Permalink
Make the utility ask before overwriting files by default.
Browse files Browse the repository at this point in the history
  • Loading branch information
turbocool3r committed Nov 10, 2022
1 parent 45224dc commit 457c1ba
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 14 deletions.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ log = "0.4"
simple_logger = "4.0"
hex = "0.4"
serde = { version = "1.0", features = ["derive"] }
toml = "0.5"
toml = "0.5"
dialoguer = { version = "0.10", default-features = false }
5 changes: 5 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,11 @@ impl FileOpError {
pub fn make_write(name: &'static str, path: PathBuf, error: io::Error) -> Box<Self> {
Self::boxed(FileOpAction::Write, name, path, error)
}

/// Checks if the underlying error kind is `ErrorKind::AlreadyExists`.
pub fn is_exists(&self) -> bool {
self.error.kind() == io::ErrorKind::AlreadyExists
}
}

impl fmt::Display for FileOpError {
Expand Down
25 changes: 20 additions & 5 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ fn do_unpack<'a>(
overwrite: bool,
create_parent_dirs: bool,
print_header: bool,
silent: bool,
) -> Result<(), UnpackError<'a>> {
use UnpackError::*;

Expand Down Expand Up @@ -93,7 +94,7 @@ fn do_unpack<'a>(
filename.push("ApImg4Ticket.der");

let ticket_path = util::qualify_path_if_needed(&filename, out_dir);
util::save_file("ticket", ticket_path, ticket, overwrite)?;
util::save_file("ticket", ticket_path, ticket, overwrite, silent)?;

the_manifest.ticket = Some(filename);
}
Expand All @@ -104,7 +105,13 @@ fn do_unpack<'a>(
match segments_parser.next_segment()? {
None => {
let serialized_manifest = toml::to_vec(&the_manifest).unwrap();
util::save_file("manifest", manifest_path, &serialized_manifest, overwrite)?;
util::save_file(
"manifest",
manifest_path,
&serialized_manifest,
overwrite,
silent,
)?;

info!("Done.");

Expand All @@ -114,7 +121,7 @@ fn do_unpack<'a>(
let filename = filename_for_tag(segment.tag);
let path = util::qualify_path_if_needed(&filename, out_dir);

util::save_file("segment", path, segment.data, overwrite)?;
util::save_file("segment", path, segment.data, overwrite, silent)?;

the_manifest.segments.push(SegmentDesc {
path: filename,
Expand All @@ -130,6 +137,7 @@ fn do_pack<'a>(
manifest_path: &'a Path,
out_path: Option<&'a Path>,
overwrite: bool,
silent: bool,
) -> Result<(), PackError<'a>> {
use PackError::*;

Expand All @@ -141,7 +149,7 @@ fn do_pack<'a>(
// create the output file
let input_dir = manifest_path.parent();
let out_file_path = util::qualify_path_or_default_if_needed(out_path, input_dir, "ftab.bin");
let mut out_file = util::create_file("output file", &out_file_path, overwrite)?;
let mut out_file = util::create_file("output file", &out_file_path, overwrite, silent)?;

debug!("Writing ftab to {}.", out_file_path.display());

Expand Down Expand Up @@ -173,6 +181,11 @@ fn main() {
(disables logging entirely), TRACE, DEBUG, INFO, WARN and ERROR.",
),
)
.arg(
arg!(silent: -s --silent).help(
"Makes all user prompts take their default action instead of being displayed.",
),
)
.subcommand(
Command::new("unpack")
.arg(arg!(overwrite: -o --overwrite).help(
Expand Down Expand Up @@ -230,6 +243,7 @@ fn main() {
_ => LevelFilter::Warn,
};
let print_header = matches.get_flag("print_header");
let silent = matches.get_flag("silent");

SimpleLogger::new().with_level(log_level).init().unwrap();

Expand All @@ -247,6 +261,7 @@ fn main() {
overwrite,
create_parent_dirs,
print_header,
silent,
) {
error!("{}", e);
}
Expand All @@ -258,7 +273,7 @@ fn main() {
.map(PathBuf::as_path);
let overwrite = sub_matches.get_flag("overwrite");

if let Err(e) = do_pack(manifest_path, out_file, overwrite) {
if let Err(e) = do_pack(manifest_path, out_file, overwrite, silent) {
error!("{}", e);
}
}
Expand Down
52 changes: 44 additions & 8 deletions src/util.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::error::FileOpError;
use dialoguer::Confirm;
use std::{
borrow::Cow,
fs::{File, OpenOptions},
Expand Down Expand Up @@ -28,20 +29,51 @@ fn create_file_impl(
name: &'static str,
path: &Path,
overwrite: bool,
silent: bool,
) -> Result<File, Box<FileOpError>> {
OpenOptions::new()
let map_error = |error| FileOpError::make_create(name, path.to_path_buf(), error);
let result = OpenOptions::new()
.write(true)
.create_new(!overwrite)
.create(overwrite)
.truncate(overwrite)
.open(path)
.map_err(|error| FileOpError::make_create(name, path.to_path_buf(), error))
.map_err(map_error);

let Err(error) = result else {
return result
};

// In case neither the overwrite flag nor the silent flag was passed, we want to ask the user if
// they want to overwrite the file on receiving a "file exists" error.
if !overwrite && !silent && error.is_exists() && path.is_file() {
let response = Confirm::new()
.with_prompt(format!(
"Do you want to overwrite the file at '{}'?",
path.display()
))
.default(false)
.interact()
.expect("failed to display a prompt to the user");

if response {
return OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open(path)
.map_err(map_error);
}
}

Err(error)
}

/// Creates a file at the specified path.
///
/// In case the `overwrite` argument is `true`, the file will be either created or truncated if it
/// exists, otherwise an existing file at the specified path will cause an error to be returned.
/// exists, otherwise in case `silent` is `false` the user will be asked if overwriting the file is
/// ok, otherwise an error will be returned.
///
/// # Errors
/// This function will return a boxed `FileOpError` with the `FileOpAction::Create` action in case
Expand All @@ -50,17 +82,19 @@ pub fn create_file<P: AsRef<Path>>(
name: &'static str,
path: P,
overwrite: bool,
silent: bool,
) -> Result<File, Box<FileOpError>> {
create_file_impl(name, path.as_ref(), overwrite)
create_file_impl(name, path.as_ref(), overwrite, silent)
}

fn save_file_impl(
name: &'static str,
path: &Path,
data: &[u8],
overwrite: bool,
silent: bool,
) -> Result<(), Box<FileOpError>> {
create_file(name, path, overwrite)?
create_file(name, path, overwrite, silent)?
.write_all(data)
.map_err(|error| FileOpError::make_write(name, path.to_path_buf(), error))?;

Expand All @@ -72,8 +106,9 @@ fn save_file_impl(
/// Creates a file at the specified path and writes data from a slice into it.
///
/// In case the `overwrite` argument is `true`, the file will be either created or truncated and
/// overwritten if it exists, otherwise an existing file at the specified path will cause an error
/// to be returned.
/// overwritten if it exists. If the `overwrite` argument is false either a prompt will be
/// displayed to the user to try and open an existing file truncating it or, in case the `silent`
/// argument is `true`, an error will be returned.
///
/// File creation is handled by the [`create_file`] function internally.
///
Expand All @@ -86,8 +121,9 @@ pub fn save_file<P: AsRef<Path>>(
path: P,
data: &[u8],
overwrite: bool,
silent: bool,
) -> Result<(), Box<FileOpError>> {
save_file_impl(name, path.as_ref(), data, overwrite)
save_file_impl(name, path.as_ref(), data, overwrite, silent)
}

fn qualify_path_if_needed_impl<'a>(path: &'a Path, dir: Option<&Path>) -> Cow<'a, Path> {
Expand Down

0 comments on commit 457c1ba

Please sign in to comment.