diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..eb5a316 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..a566980 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,4 @@ +[root] +name = "turnout-for-what" +version = "0.1.0" + diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..12628f3 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "turnout-for-what" +version = "0.1.0" +authors = ["John Wrenn "] + +[[bin]] +name = "tfw" diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..516031e --- /dev/null +++ b/README.rst @@ -0,0 +1,46 @@ +================ +turnout-for-what +================ +.. image:: https://upload.wikimedia.org/wikipedia/commons/a/a2/V%C3%BDm%C4%9Bna_hrotit%C3%A1.jpg + +Usage +----- +:: + + tfw SWITCH FILES... + +``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. + +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 + +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 new file mode 100644 index 0000000..a391983 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,58 @@ +use std::io; +use std::env; +use std::thread; +use std::fs::File; +use std::io::Write; +use std::sync::Arc; +use std::io::BufRead; +use std::io::BufReader; +use std::fs::OpenOptions; +use std::sync::atomic::Ordering; +use std::sync::atomic::AtomicUsize; + +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); + } +}