renamed --argument-file to --target-file; fixed "-" not being treated as special for --target-file; improved relevant help information; versionup minor
This commit is contained in:
parent
b51fab3ca1
commit
e2730b4d7a
3 changed files with 57 additions and 31 deletions
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "lndups"
|
name = "lndups"
|
||||||
version = "0.1.1"
|
version = "0.1.2"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
Binary file not shown.
84
src/main.rs
84
src/main.rs
|
@ -9,6 +9,7 @@ use crate::structopt::StructOpt;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
macro_rules! s_arg_target_file_name { () => { "target-file" } }
|
||||||
macro_rules! s_default_target_separator { () => { ";" } }
|
macro_rules! s_default_target_separator { () => { ";" } }
|
||||||
|
|
||||||
|
|
||||||
|
@ -24,14 +25,19 @@ fn main() -> Result<(), i32> {
|
||||||
verbosity
|
verbosity
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(arg_file) = args.argument_file {
|
if let Some(arg_file) = args.file_containing_targets {
|
||||||
if !args.targets.is_empty() {
|
if !args.targets.is_empty() {
|
||||||
eprintln!("No targets should be provided as cli arguments if arguments are being read from file");
|
eprintln!("No targets should be provided as cli arguments if arguments are being read from file");
|
||||||
return Err(1);
|
return Err(1);
|
||||||
}
|
}
|
||||||
let path = Path::new(&arg_file);
|
if let Err(s) = {
|
||||||
if let Err(s) = read_file_lines(path, &mut args.targets) {
|
if arg_file == "-" {
|
||||||
eprintln!("Error reading argument file: {}", s);
|
read_lines(std::io::stdin().lock(), &mut args.targets)
|
||||||
|
} else {
|
||||||
|
read_file_lines(Path::new(&arg_file), &mut args.targets)
|
||||||
|
}
|
||||||
|
} {
|
||||||
|
eprintln!("Error reading file containing targets: {}", s);
|
||||||
return Err(1);
|
return Err(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -103,40 +109,56 @@ struct Config {
|
||||||
usage=concat!(env!("CARGO_PKG_NAME"), " [OPTION]... TARGET... ['", s_default_target_separator!(), "' TARGET...]")
|
usage=concat!(env!("CARGO_PKG_NAME"), " [OPTION]... TARGET... ['", s_default_target_separator!(), "' TARGET...]")
|
||||||
)]
|
)]
|
||||||
struct CLIArguments {
|
struct CLIArguments {
|
||||||
#[structopt(short, long, parse(from_occurrences),
|
#[structopt(short, long, parse(from_occurrences), help="Increase verbosity")]
|
||||||
help="Increase verbosity")]
|
|
||||||
verbose: i8,
|
verbose: i8,
|
||||||
|
|
||||||
#[structopt(short, long, parse(from_occurrences),
|
#[structopt(short, long, parse(from_occurrences), help="Decrease verbosity")]
|
||||||
help="Decrease verbosity")]
|
|
||||||
quiet: i8,
|
quiet: i8,
|
||||||
|
|
||||||
#[structopt(long,
|
#[structopt(long, help=concat!(
|
||||||
help="Disable brace notation for output\n Ex: /home/user/{dir,backup}/file")]
|
"Disable brace notation for output\n",
|
||||||
|
" Ex: /home/user/{dir,backup}/file",
|
||||||
|
))]
|
||||||
no_brace_output: bool,
|
no_brace_output: bool,
|
||||||
|
|
||||||
#[structopt(long,
|
#[structopt(long, help=concat!(
|
||||||
help="Perform no operations on the filesystem")]
|
"Perform no operations on the filesystem",
|
||||||
|
))]
|
||||||
dry_run: bool,
|
dry_run: bool,
|
||||||
|
|
||||||
#[structopt(short="i",
|
#[structopt(short="i", help=concat!(
|
||||||
help="Prompt once before operating\nDoesn't occurs if no targets are provided")]
|
"Prompt once before operating\n",
|
||||||
|
"Doesn't occurs if no targets are provided",
|
||||||
|
))]
|
||||||
prompt: bool,
|
prompt: bool,
|
||||||
|
|
||||||
#[structopt(short, long, value_name="VALUE",
|
#[structopt(short, long, value_name="VALUE", help=concat!(
|
||||||
help="Minimum file size to be considered for hardlinking\nNever goes below 1 (the default)")]
|
"Minimum file size to be considered for hardlinking\n",
|
||||||
|
"Never goes below 1 (the default)",
|
||||||
|
))]
|
||||||
min_size: Option<u64>,
|
min_size: Option<u64>,
|
||||||
|
|
||||||
#[structopt(short, long, value_name="SEPARATOR",
|
#[structopt(short, long, value_name="SEPARATOR", help=concat!(
|
||||||
help=concat!("Separator between sets of targets (default: ", s_default_target_separator!(), ")"))]
|
"Separator between sets of targets (default: ", s_default_target_separator!(), ")",
|
||||||
|
))]
|
||||||
separator: Option<String>,
|
separator: Option<String>,
|
||||||
|
|
||||||
#[structopt(long, value_name="FILE",
|
#[structopt(long=s_arg_target_file_name!(), value_name="FILE", help=concat!(
|
||||||
help="File to source arguments from (can be '-' for stdin)")]
|
"File to source targets from (can be '-' for stdin)\n",
|
||||||
argument_file: Option<String>,
|
"Same rules as CLI argument targets apply\n",
|
||||||
|
"Mutually exclusive with CLI argument targets",
|
||||||
|
))]
|
||||||
|
file_containing_targets: Option<String>,
|
||||||
|
|
||||||
#[structopt(value_name="TARGET",
|
#[structopt(value_name="TARGET", help=concat!(
|
||||||
help="Target files and directories (recursive)\nEach SEPARATOR denotes a new set of targets\n Each set of targets are separate from all other sets\n All targets must be on the same device\nAll symlinks are ignored\n'-' is not treated as special")]
|
"Target files and directories (recursive)\n",
|
||||||
|
"Each SEPARATOR denotes a new set of targets\n",
|
||||||
|
" Each set of targets are separate from all other sets\n",
|
||||||
|
" All targets must be on the same device\n",
|
||||||
|
"All symlinks are ignored\n",
|
||||||
|
"'-' is not treated as special\n",
|
||||||
|
"Mutually exclusive with ", s_arg_target_file_name!(),
|
||||||
|
))]
|
||||||
targets: Vec<String>,
|
targets: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,12 +184,7 @@ fn prompt_confirm<'a, T: Borrow<[Y]>, Y: AsRef<str>>(run_targets: &[T]) -> bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn read_file_lines(path: &Path, dest: &mut Vec<String>) -> Result<(), String> {
|
fn read_lines<R: BufRead>(reader: R, dest: &mut Vec<String>) -> Result<(), String> {
|
||||||
if !path.is_file() {
|
|
||||||
return Err(format!("File does not exist or is not a normal file ({})", shlex::try_quote(&path.to_string_lossy()).unwrap()));
|
|
||||||
}
|
|
||||||
if let Ok(f) = std::fs::File::open(path) {
|
|
||||||
let reader = BufReader::new(f);
|
|
||||||
for line in reader.lines() {
|
for line in reader.lines() {
|
||||||
match line {
|
match line {
|
||||||
Ok(line) => dest.push(line),
|
Ok(line) => dest.push(line),
|
||||||
|
@ -175,6 +192,15 @@ fn read_file_lines(path: &Path, dest: &mut Vec<String>) -> Result<(), String> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_file_lines(path: &Path, dest: &mut Vec<String>) -> Result<(), String> {
|
||||||
|
if !path.is_file() {
|
||||||
|
return Err(format!("File does not exist or is not a normal file ({})", shlex::try_quote(&path.to_string_lossy()).unwrap()));
|
||||||
|
}
|
||||||
|
if let Ok(f) = std::fs::File::open(path) {
|
||||||
|
let reader = BufReader::new(f);
|
||||||
|
read_lines(reader, dest)
|
||||||
} else {
|
} else {
|
||||||
Err(format!("Could not open {}", shlex::try_quote(&path.to_string_lossy()).unwrap()))
|
Err(format!("Could not open {}", shlex::try_quote(&path.to_string_lossy()).unwrap()))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue