From 19b5f322dcd2a625124ba8c3a08732c64fc5c3f9 Mon Sep 17 00:00:00 2001 From: Avril Date: Tue, 18 Mar 2025 16:21:10 +0000 Subject: [PATCH] Added `--append`. Added some basic heuristics for not re-writing redundant files unless explicitly asked for (see `--append`.) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fortune for genmarkov's current commit: Half curse − 半凶 --- src/main.rs | 113 +++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 90 insertions(+), 23 deletions(-) diff --git a/src/main.rs b/src/main.rs index de843d9..5b37de4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,15 +18,32 @@ use std::{ }, }; -#[derive(Debug, Parser)] -#[command(name = env!("CARGO_PKG_NAME"), version, about = env!("CARGO_PKG_DESCRIPTION"), long_about = None)] -pub struct Cli { - /// Save the chain to the provided output file name after appending to it. +#[derive(Debug, clap::Args)] +#[group(required=false,multiple=true)] +struct ChainFile { + /// Load the chain from the provided file & then save it back after appending to the chain. + /// + /// Note that if there is no data fed to the chain before saving, it is not re-written to the file unless `--force` is also provided. + /// + /// # Overwriting + /// + /// If `-s` & `-l` are provided on the same file instead of `-a`, but `-f` is not, the save will still fail. #[arg(short, long)] + append: Option, + /// Save the chain to the provided output file name after appending to it. + #[arg(short, long, conflicts_with="append")] save: Option, /// Load the chain from the provided output file name before appending to it. - #[arg(short, long)] + #[arg(short, long, conflicts_with="append")] load: Option, // TODO: Group with `save` & add `-F/--file` opt for save & load from same file. +} + +#[derive(Debug, Parser)] +#[command(name = env!("CARGO_PKG_NAME"), version, about = env!("CARGO_PKG_DESCRIPTION"), long_about = None)] +pub struct Cli { + #[command(flatten)] + chain: ChainFile, + /// Force over-writes of files, and force output of binary data to TTY. #[arg(short, long)] force: bool, @@ -46,6 +63,41 @@ pub struct Cli { lines: usize, } +impl Cli { + #[inline(always)] + fn append(&self) -> Option<&Path> + { + self.chain.append.as_ref().map(Path::new) + } + + #[inline] + pub fn load(&self) -> Option<&Path> + { + self.chain.load.as_ref().map(Path::new).or_else(|| self.append()) + } + #[inline] + pub fn save(&self) -> Option<&Path> + { + self.chain.save.as_ref().map(Path::new).or_else(|| self.append()) + } + + /// If the save operation will be saving to the file loaded by the load operation. + /// + /// # Overwriting + /// Note that forceful overwriting where `-l` & `-s` supply the same file is not applied without `--force`, whereas if `--append` is provided it is. + #[inline] + pub fn is_append(&self) -> bool + { + self.chain.append.is_some() || (self.chain.save.is_some() && &self.chain.save == &self.chain.load) + } + + /// If the output should be overwritten. + pub fn force_overwrite(&self) -> bool + { + self.force || self.chain.append.is_some() + } +} + fn buffered_read_all_lines io::Result<()>>(input: &mut T, mut then: F) -> io::Result { let mut buffer = String::new(); @@ -83,27 +135,35 @@ where S: io::Write + ?Sized fn create_chain(cli: &Cli) -> Chain { - if let Some(load) = &cli.load { + if let Some(load) = cli.load() { let mut input = std::fs::OpenOptions::new() .read(true) - .open(&load).expect("Failed to open chain load file"); + .open(load).expect("Failed to open chain load file"); load_chain(&mut input).expect("Failed to load chain from file") } else { Chain::new() } } -fn complete_chain(cli: &Cli, chain: Chain) -> io::Result<()> +fn complete_chain(cli: &Cli, chain: Chain, is_unmodified: bool) -> io::Result<()> { - if let Some(save) = &cli.save { - let mut output = std::fs::OpenOptions::new() - .create_new(! cli.force) - - .create(cli.force) - .write(true) - .truncate(cli.force) - .open(&save).expect("Failed to open chain save file"); - save_chain(&mut output, &chain).expect("Failed to save chain to file") // TODO: Error type + if cli.force || // If `--force` is provided, always attempt write. + !is_unmodified || // And attempt write if there was something appended to the chain + !cli.is_append() // Or, attempt write if we are *not* appending *even if* there is no change. + { + let force = cli.force_overwrite(); + if let Some(save) = cli.save() { + let mut output = std::fs::OpenOptions::new() + .create_new(! force) + + .create(force) + .write(true) + .truncate(force) + .open(save).expect("Failed to open chain save file"); + save_chain(&mut output, &chain).expect("Failed to save chain to file") // TODO: Error type + } + } else if cfg!(debug_assertions) { + eprintln!("Warning: Not writing to potential save location {:?}: is_unmodified: {is_unmodified}, is_append: {}, force: overwrite = {} (always: {})", cli.save(), cli.is_append(), cli.force_overwrite(), cli.force); } Ok(()) @@ -111,16 +171,23 @@ fn complete_chain(cli: &Cli, chain: Chain) -> io::Result<()> fn main() { let cli = parse_cli(); + + let mut unmodified = cli.load().is_some(); //XXX: Should we init this to `false` for all non-loaded chains? let stdin = io::stdin(); let mut stdin = stdin.lock(); - let mut chain = create_chain(&cli); - + let mut chain = create_chain(&cli); // TODO: If `self.force_overwrite() && self.is_append()`, keep the file open for the process duration, and `lseek(0)` it before writing instead of opening it again. (XXX: Should we add `O_EXCL` to the opened file if ) + if !(cli.write_only || (cli.no_consume && ! chain.is_empty())) { + // TODO: If there are 0 lines fed, set a flag for `unmodified` so if `cli.is_append()`, the file isn't overwritten for no reason. buffered_read_all_lines(&mut stdin, |string| { - chain.feed(&string.split_whitespace() - .filter(|word| !word.is_empty()) - .map(|s| s.to_owned()).collect::>()); + let words = string.split_whitespace() + .filter(|word| !word.is_empty()) + .map(|s| s.to_owned()).collect::>(); + if !words.is_empty() { + unmodified = false; + chain.feed(&words); + } Ok(()) }).expect("Failed to read from stdin"); @@ -138,5 +205,5 @@ fn main() { }; } - complete_chain(&cli, chain).unwrap(); + complete_chain(&cli, chain, unmodified).unwrap(); }