Added `--append`. Added some basic heuristics for not re-writing redundant files unless explicitly asked for (see `--append`.)

Fortune for genmarkov's current commit: Half curse − 半凶
cli
Avril 2 weeks ago
parent 995e1cf80e
commit 19b5f322dc
Signed by: flanchan
GPG Key ID: 284488987C31F630

@ -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<PathBuf>,
/// Save the chain to the provided output file name after appending to it.
#[arg(short, long, conflicts_with="append")]
save: Option<PathBuf>,
/// Load the chain from the provided output file name before appending to it.
#[arg(short, long)]
#[arg(short, long, conflicts_with="append")]
load: Option<PathBuf>, // 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<T: BufRead+?Sized, F: FnMut(&str) -> io::Result<()>>(input: &mut T, mut then: F) -> io::Result<usize>
{
let mut buffer = String::new();
@ -83,27 +135,35 @@ where S: io::Write + ?Sized
fn create_chain(cli: &Cli) -> Chain<String>
{
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<String>) -> io::Result<()>
fn complete_chain(cli: &Cli, chain: Chain<String>, 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<String>) -> 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::<SmallVec<[_; 16]>>());
let words = string.split_whitespace()
.filter(|word| !word.is_empty())
.map(|s| s.to_owned()).collect::<SmallVec<[_; 16]>>();
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();
}

Loading…
Cancel
Save