//#[macro_use] extern crate serde; use clap::{ Parser, Subcommand, ValueEnum, }; use chain::{ Chain, }; use std::{ num::NonZeroUsize, path::{ PathBuf, Path, }, io::{ BufRead, self, }, }; #[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. #[arg(short, long)] save: Option, /// Load the chain from the provided output file name before appending to it. #[arg(short, long)] load: Option, /// Force over-writes of files, and force output of binary data to TTY. #[arg(short, long)] force: bool, /// The number of lines to output from the chain. lines: Option, //TODO: Num of lines, etc. } fn buffered_read_all_lines io::Result<()>>(input: &mut T, mut then: F) -> io::Result { let mut buffer = String::new(); let mut read; let mut total=0; while {read = input.read_line(&mut buffer)?; read!=0} { if buffer.trim().len() > 0 { then(&buffer[..])?; } buffer.clear(); total += read; } Ok(total) } #[inline] fn parse_cli() -> Cli { use clap::Parser; Cli::parse() } fn load_chain(stream: &mut S) -> io::Result> where S: io::Read + ?Sized { let mut stream = io::BufReader::new(stream); Ok(serde_cbor::from_reader(&mut stream).expect("Failed to read chain from input stream")) // TODO: Error type } fn save_chain(stream: &mut S, chain: &Chain) -> io::Result<()> where S: io::Write + ?Sized { use io::Write; let mut stream = io::BufWriter::new(stream); serde_cbor::to_writer(&mut stream, chain).expect("Failed to write chain to output stream"); // TODO: Error type stream.flush() } fn create_chain(cli: &Cli) -> Chain { if let Some(load) = &cli.load { let mut input = std::fs::OpenOptions::new() .read(true) .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<()> { 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 } Ok(()) } fn main() { let cli = parse_cli(); let stdin = io::stdin(); let mut stdin = stdin.lock(); let mut chain = create_chain(&cli); buffered_read_all_lines(&mut stdin, |string| { chain.feed(&string.split_whitespace() .filter(|word| !word.is_empty()) .map(|s| s.to_owned()).collect::>()); Ok(()) }).expect("Failed to read from stdin"); if !chain.is_empty() { let lines = cli.lines.map(NonZeroUsize::get).unwrap_or(1); if lines > 1 { for string in chain.str_iter_for(lines) { println!("{}", string); } } else { println!("{}", chain.generate_str()); } } complete_chain(&cli, chain).unwrap(); }