#lang racket (module+ test (require rackunit)) (require racket/async-channel) (provide (contract-out [tee-pipe (->* (input-port?) #:rest (listof (-> input-port? any)) list?)])) (define (tee-pipe in . handlers) (define-values (r-outputs r-channels) (for/fold ([outputs '()] [channels '()]) ([h (in-list handlers)]) (define-values (i o) (make-pipe)) (define c (make-async-channel)) (define t (thread (thunk (with-handlers [(exn:fail? (lambda (e) (async-channel-put c e)))] (async-channel-put c (h i))) (close-input-port i)))) (values (cons o outputs) (cons c channels)))) (define outputs (reverse r-outputs)) (define channels (reverse r-channels)) (thread (thunk (apply copy-port in outputs) (for ([o (in-list outputs)]) (close-output-port o)))) (for/list ([c (in-list channels)]) (async-channel-get c))) (module+ test (check-equal? '("123\nabc\nABC" ("123" "abc" "ABC")) (tee-pipe (open-input-string "123\nabc\nABC") port->string port->lines)) (check-equal? '("123\nabc\nABC" ("123" "abc" "ABC") (#"123" #"abc" #"ABC")) (tee-pipe (open-input-string "123\nabc\nABC") port->string port->lines port->bytes-lines)))