You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
133 lines
3.3 KiB
133 lines
3.3 KiB
//#[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<PathBuf>,
|
|
/// Load the chain from the provided output file name before appending to it.
|
|
#[arg(short, long)]
|
|
load: Option<PathBuf>,
|
|
/// 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<NonZeroUsize>,
|
|
|
|
//TODO: Num of lines, etc.
|
|
}
|
|
|
|
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();
|
|
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<S>(stream: &mut S) -> io::Result<Chain<String>>
|
|
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<S>(stream: &mut S, chain: &Chain<String>) -> 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<String>
|
|
{
|
|
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<String>) -> 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::<Vec<_>>());
|
|
|
|
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();
|
|
}
|