Skip to content

Commit

Permalink
Read destination paths from file.
Browse files Browse the repository at this point in the history
  • Loading branch information
jswrenn committed Oct 8, 2016
1 parent 88b2635 commit 430e264
Show file tree
Hide file tree
Showing 4 changed files with 189 additions and 80 deletions.
97 changes: 96 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 5 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
[package]
name = "turnout-for-what"
version = "0.1.0"
authors = ["John Wrenn <john_wrenn@brown.edu>"]
version = "0.2.0"
authors = ["Jack Wrenn <me@jswrenn.com>"]

[[bin]]
name = "tfw"

[dependencies]
clap = "2.14.0"
47 changes: 20 additions & 27 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.
118 changes: 68 additions & 50 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -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;

This comment has been minimized.

Copy link
@k4rtik

k4rtik Oct 9, 2016

Idiomatic Rust way is to combine dependencies from the same module, like so:

use std::io::{copy, stdin, BufRead, BufReader}

Also, suggest using rustfmt, takes care of most syntactic issues.

This comment has been minimized.

Copy link
@k4rtik

k4rtik Oct 9, 2016

...but not all, like it doesn't automatically compress the includes like I showed above.

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::<Vec<File>>();

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::<usize>().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(

This comment has been minimized.

Copy link
@k4rtik

k4rtik Oct 9, 2016

The unwrap() function confused me for a while as it is also a standard library method for unwrapping a Result. If it is taking care of an error and silently exiting, I would want to see it closer to its use or get that from its name. Just a tiny nitpick.

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<T>(v : Result<T>) -> T {
v.unwrap_or_else(|e| {
println!("{}", e);
process::exit(1)
})
}

0 comments on commit 430e264

Please sign in to comment.