a unix-y command to chop a little off the top
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.
chop/src/main.rs

133 lines
4.2 KiB

// Copyright (C) 2021 Mike Cugini <mike@betamike.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
use std::error::Error;
use std::fs::File;
use std::io::prelude::*;
use std::io::{self, BufReader};
use argh::FromArgs;
mod suffix;
use crate::suffix::{AlphabeticSuffixGenerator, NumericSuffixGenerator};
#[derive(FromArgs)]
#[argh(description = "\
chop off bits of file\n\n\
chop will take produce --count files of --lines lines from the beginning of a\n\
file or stdin. Any remaining lines will be written to a final catchall file.")]
struct Options {
/// number of lines in each chunk
#[argh(option, short = 'n')]
lines: usize,
/// count of chunks to produce (default 1)
#[argh(option, short = 'c', default = "1")]
count: usize,
/// optional prefix to use for generated files (default "")
#[argh(option, short = 'p', default = "String::from(\"\")")]
prefix: String,
/// use numeric suffixes starting with 0, not alphabetic
#[argh(switch, short = 'd')]
numeric: bool,
/// use numeric suffixes starting with this value
#[argh(option, long = "numeric-start")]
numeric_start: Option<usize>,
/// filename to read from, or "-" for stdin (default "-")
#[argh(positional, default = "String::from(\"-\")")]
filename: String,
}
fn try_main() -> Result<(), Box<dyn Error>> {
let opts: Options = argh::from_env();
// open source file
let input: Box<dyn Read> = match opts.filename.as_str() {
"-" => Box::new(io::stdin()),
path => match File::open(path) {
Err(why) => panic!("failed to open {}: {}", path, why),
Ok(file) => Box::new(file),
},
};
let mut reader = BufReader::new(input);
// build prefix format
let prefix = match opts.prefix.as_str() {
"" => String::from(""),
_ => opts.prefix + "_",
};
// use numeric suffixes if --numeric or --numeric-start are passed
let use_numeric = opts.numeric || opts.numeric_start.is_some();
let suffix_gen: Box<dyn Iterator<Item = String>> = if use_numeric {
let numeric_start = match opts.numeric_start {
Some(num) => num,
None => 0,
};
Box::new(NumericSuffixGenerator::new(numeric_start, opts.count))
} else {
Box::new(AlphabeticSuffixGenerator::new(opts.count))
};
let mut line = String::new();
for suffix in suffix_gen {
// do a test read to see if there's any/another line before creating a new file
let read = reader.read_line(&mut line)?;
if read == 0 {
break;
}
let mut out_file = File::create(format!("{}{}", prefix, suffix))?;
out_file.write_all(line.as_bytes())?;
for _ in 1..opts.lines {
line.clear();
match reader.read_line(&mut line)? {
0 => {
break;
}
_ => {
out_file.write_all(line.as_bytes())?;
}
};
}
line.clear();
}
// see if there's anything left for the remainder file
let read = reader.read_line(&mut line)?;
if read > 0 {
let mut out_file = File::create(format!("{}rest", prefix))?;
out_file.write_all(line.as_bytes())?;
for result in reader.lines() {
// lines() strips newline characters, so add one
out_file.write_all((result? + "\n").as_bytes())?;
}
}
Ok(())
}
fn main() {
// display nicer, non-debug representation of the error
if let Err(err) = try_main() {
eprintln!("{}", err);
std::process::exit(1);
}
}