diff --git a/Cargo.lock b/Cargo.lock index a566980..1ac008b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,4 +1,99 @@ [root] name = "turnout-for-what" -version = "0.1.0" +version = "0.2.0" +dependencies = [ + "clap 2.14.0 (registry+https://github.com/rust-lang/crates.io-index)", +] +[[package]] +name = "ansi_term" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "bitflags" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "clap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)", + "strsim 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "term_size 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-segmentation 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-width 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "vec_map 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "libc" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "strsim" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "term_size" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicode-segmentation" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unicode-width" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "vec_map" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[metadata] +"checksum ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "23ac7c30002a5accbf7e8987d0632fa6de155b7c3d39d0067317a391e00a2ef6" +"checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" +"checksum clap 2.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5fa304b03c49ccbb005784fc26e985b5d2310b1d37f2c311ce90dbcd18ea5fde" +"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +"checksum libc 0.2.16 (registry+https://github.com/rust-lang/crates.io-index)" = "408014cace30ee0f767b1c4517980646a573ec61a57957aeeabcac8ac0a02e8d" +"checksum strsim 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "50c069df92e4b01425a8bf3576d5d417943a6a7272fbabaf5bd80b1aaa76442e" +"checksum term_size 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3f7f5f3f71b0040cecc71af239414c23fd3c73570f5ff54cf50e03cef637f2a0" +"checksum unicode-segmentation 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b905d0fc2a1f0befd86b0e72e31d1787944efef9d38b9358a9e92a69757f7e3b" +"checksum unicode-width 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2d6722facc10989f63ee0e20a83cd4e1714a9ae11529403ac7e0afd069abc39e" +"checksum vec_map 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cac5efe5cb0fa14ec2f84f83c701c562ee63f6dcc680861b21d65c682adfb05f" +"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" +"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" diff --git a/Cargo.toml b/Cargo.toml index 12628f3..2a80306 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,10 @@ [package] name = "turnout-for-what" -version = "0.1.0" -authors = ["John Wrenn "] +version = "0.2.0" +authors = ["Jack Wrenn "] [[bin]] name = "tfw" + +[dependencies] +clap = "2.14.0" diff --git a/README.rst b/README.rst index 516031e..265838f 100644 --- a/README.rst +++ b/README.rst @@ -7,40 +7,33 @@ Usage ----- :: - tfw SWITCH FILES... + tfw SWITCH ``SWITCH`` - A file descriptor yielding a stream of line-separated numbers. For every byte received from stdin, the last number received from SWITCH indicates the index of the FILE that the byte should be redirected to. - -``FILES`` - One or more file paths. + A file descriptor yielding a stream of line-separated paths. For every line received from stdin, the last path received from SWITCH indicates the file that the line should be redirected to. Example ------- A load-balancer for the natural numbers:: - #!/usr/bin/env bash - i=0; - echo "$i" > nat - trap "rm nat *.txt" EXIT - - while true; do - wc -l *.txt - sleep 0.2 - clear - done & - - while true; do - sleep 0.2; - i=$(expr $i + 1); - echo "$i"; - done \ - | tee -a nat \ - | cargo run -- \ - <(while true; do - echo $(expr "$(tail -n 1 nat)" % 5) - done) \ - 0.txt 1.txt 2.txt 3.txt 4.txt + #!/usr/bin/env bash + + trap "rm -f nat {0..4}; pkill -P $$" EXIT + + touch nat {0..4} + + while true; do + echo $((i++)) + sleep 0.2; + done > nat & + + tail -f nat | cargo run -- <(tail -f nat | xargs -I{} expr {} % 5) & + + while true; do + sleep 0.2 + clear + wc -l {0..4} + done This script will create five text files, balance the natural numbers between them, and continuously print their line counts. diff --git a/src/main.rs b/src/main.rs index a391983..7250c5f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,58 +1,76 @@ -use std::io; -use std::env; -use std::thread; -use std::fs::File; -use std::io::Write; -use std::sync::Arc; +#[macro_use] +extern crate clap; + +use clap::App; +use clap::Arg; + +use std::io::copy; +use std::io::stdin; use std::io::BufRead; use std::io::BufReader; +use std::io::Result; use std::fs::OpenOptions; -use std::sync::atomic::Ordering; -use std::sync::atomic::AtomicUsize; +use std::thread; +use std::process; +use std::sync::Arc; +use std::sync::Mutex; + fn main() { - let files = env::args(). - skip(2). - map(|path| - OpenOptions::new(). - read(false). - write(true). - create(true). - open(path)). - map(Result::unwrap). - collect::>(); - - let target = Arc::new(AtomicUsize::new(0)); - let target_writer = target.clone(); - - thread::spawn(move || { - for line in BufReader::new( - env::args().nth(1). - map(|path| - OpenOptions::new(). - read(true). - write(false). - create(false). - open(path)). - unwrap().unwrap()). - lines() { - target_writer.store( - line.unwrap().parse::().unwrap_or( - target_writer.load(Ordering::Relaxed)), - Ordering::Relaxed); - } - }); - - let stdin = io::stdin(); - let mut buffer = stdin.lock(); - - loop { - let consumed = buffer.fill_buf(). - map(|bytes| - files.get(target.load(Ordering::Relaxed)). - map(|mut file| file.write(bytes))). - unwrap().unwrap().unwrap(); - buffer.consume(consumed); + let matches = App::new("tfw") + .version(crate_version!()) + .author(crate_authors!()) + .about("Shell utility for conditional redirection") + .arg(Arg::with_name("switch") + .value_name("SWITCH") + .help("File whose last line indicates where to direct the input.") + .takes_value(true) + .required(true) + .multiple(false)) + .get_matches(); + + let switch = unwrap( + matches.value_of("switch") + .map(|path| OpenOptions::new().read(true).open(path)) + .unwrap()); + + let target = Arc::new(Mutex::new(None)); + + { + let target = target.clone(); + thread::spawn(move || { + let files = + BufReader::new(switch) + .lines().map(unwrap) + .map(|path| + OpenOptions::new() + .create(true) + .append(true) + .open(path)).map(unwrap); + for file in files { + *(target.lock().unwrap()) = Some(file); + } + }); + } + + let stdin = stdin(); + let lines = stdin.lock().lines().map(unwrap); + + while target.lock().unwrap().is_none() {}; + + for mut line in lines { + line.push('\n'); + unwrap(copy( + &mut line.as_bytes(), + (*target.lock().unwrap()).as_mut().unwrap())); } } + + +fn unwrap(v : Result) -> T { + v.unwrap_or_else(|e| { + println!("{}", e); + process::exit(1) + }) +}