initial commit with base feature set

This commit is contained in:
Mike Cugini 2021-03-15 23:56:58 -04:00
commit 94208e39fc
4 changed files with 239 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/target

87
Cargo.lock generated Normal file
View File

@ -0,0 +1,87 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "argh"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91792f088f87cdc7a2cfb1d617fa5ea18d7f1dc22ef0e1b5f82f3157cdc522be"
dependencies = [
"argh_derive",
"argh_shared",
]
[[package]]
name = "argh_derive"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4eb0c0c120ad477412dc95a4ce31e38f2113e46bd13511253f79196ca68b067"
dependencies = [
"argh_shared",
"heck",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "argh_shared"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "781f336cc9826dbaddb9754cb5db61e64cab4f69668bd19dcc4a0394a86f4cb1"
[[package]]
name = "chop"
version = "0.1.0"
dependencies = [
"argh",
]
[[package]]
name = "heck"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87cbf45460356b7deeb5e3415b5563308c0a9b057c85e12b06ad551f98d0a6ac"
dependencies = [
"unicode-segmentation",
]
[[package]]
name = "proc-macro2"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quote"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
dependencies = [
"proc-macro2",
]
[[package]]
name = "syn"
version = "1.0.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fd9d1e9976102a03c542daa2eff1b43f9d72306342f3f8b3ed5fb8908195d6f"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]]
name = "unicode-segmentation"
version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796"
[[package]]
name = "unicode-xid"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"

10
Cargo.toml Normal file
View File

@ -0,0 +1,10 @@
[package]
name = "chop"
version = "0.1.0"
authors = ["Mike Cugini <mike@betamike.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
argh = "0.1.4"

141
src/main.rs Normal file
View File

@ -0,0 +1,141 @@
use std::error::Error;
use std::fs::File;
use std::io::{self, BufReader};
use std::io::prelude::*;
use argh::FromArgs;
#[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 0, not alphabetic
#[argh(option, long = "numeric-start", default = "0")]
numeric_start: usize,
/// filename to read from, or "-" for stdin (default "-")
#[argh(positional, default = "String::from(\"-\")")]
filename: String,
}
struct AlphabeticSuffixGenerator {
suffix: Vec<u8>,
remaining: usize,
}
impl AlphabeticSuffixGenerator {
fn new(count: usize) -> AlphabeticSuffixGenerator {
// given 27 letters, minimum number of digits is floor( Log 27 (count) + 1 )
let suffix_length = ((count as f64).log(27f64) + 1.0).floor() as usize;
let mut suffix = vec![b'a'; suffix_length];
suffix[suffix_length-1] -= 1;
AlphabeticSuffixGenerator{suffix, remaining: count}
}
}
impl Iterator for AlphabeticSuffixGenerator {
type Item = String;
fn next(&mut self) -> Option<String> {
if self.remaining == 0 {
return None
}
for idx in (0..self.suffix.len()).rev() {
if self.suffix[idx] < b'z' {
self.suffix[idx] += 1;
break
} else {
self.suffix[idx] = b'a';
}
}
self.remaining -= 1;
Some(String::from_utf8(self.suffix.to_vec()).expect("invalid suffix generated"))
}
}
fn try_main() -> Result<(), Box<dyn Error>> {
let opts: Options = argh::from_env();
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 prefix = match opts.prefix.as_str() {
"" => String::from(""),
_ => opts.prefix + "_",
};
let suffix_gen: Box<dyn Iterator<Item = String>> = if opts.numeric {
Box::new((opts.numeric_start..(opts.numeric_start + opts.count)).map(|num| format!("{}", num)))
} else {
Box::new(AlphabeticSuffixGenerator::new(opts.count))
};
let mut eof = false;
let mut reader = BufReader::new(input);
for suffix in suffix_gen {
let mut out_file = File::create(format!("{}{}", prefix, suffix))?;
let mut line = String::new();
for _ in 0..opts.lines {
match reader.read_line(&mut line)? {
0 => {
eof = true;
break
},
_ => {
out_file.write_all(line.as_bytes())?;
line.clear();
}
};
}
}
// see if there's anything left for the remainder file
if !eof {
let mut out_file = File::create(format!("{}rest", prefix))?;
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);
}
}