@ -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 ( ) ;
}