126 lines
3.9 KiB
Clojure
126 lines
3.9 KiB
Clojure
(ns net.deertopia.doerg.common
|
|
(:require [babashka.process :as p]
|
|
[clojure.string :as str]
|
|
[clojure.tools.logging :as l]
|
|
[clojure.java.io :as io])
|
|
(:import (java.io FilterInputStream StringWriter InputStream
|
|
OutputStream PrintStream ByteArrayOutputStream
|
|
ByteArrayInputStream FilterOutputStream)
|
|
(java.nio.charset StandardCharsets)))
|
|
|
|
(defn deref-with-timeout [process ms]
|
|
(let [p (promise)
|
|
process-future (future (deliver p @process))
|
|
timeout-future (future (Thread/sleep ms)
|
|
(future-cancel process-future)
|
|
(p/destroy-tree process)
|
|
(deliver p ::timed-out))]
|
|
(if (= @p ::timed-out)
|
|
(throw (ex-info (format "external command `%s' timed out after %.2fs."
|
|
(str/join " " (:cmd process))
|
|
(/ (double ms) 1000))
|
|
{:process process
|
|
:timed-out-after-milliseconds ms}))
|
|
@p)))
|
|
|
|
(defn tee-input-stream
|
|
"Return a wrapped `InputStream` that writes all bytes read from
|
|
input-stream to sink, à la the UNIX command tee(1)."
|
|
[input-stream sink]
|
|
(proxy [FilterInputStream] [input-stream]
|
|
(read
|
|
([]
|
|
(let [c (proxy-super read)]
|
|
(when (not= c -1)
|
|
(.write sink c))
|
|
c))
|
|
([^bytes bs]
|
|
(let [n (proxy-super read bs)]
|
|
(when (not= n -1)
|
|
(.write sink bs 0 n))
|
|
n))
|
|
([^bytes bs off len]
|
|
(let [n (proxy-super read bs off len)]
|
|
(when (not= n -1)
|
|
(.write sink bs off n))
|
|
n)))
|
|
(close []
|
|
(try (proxy-super close)
|
|
(finally (.close sink))))))
|
|
|
|
(defn tee-output-stream
|
|
"Return a wrapped `OutputStream` that writes all bytes written to
|
|
output-stream to sink, à la the UNIX command tee(1)."
|
|
[output-stream sink]
|
|
(proxy [FilterOutputStream] [output-stream]
|
|
(write
|
|
([bs-or-b]
|
|
(proxy-super write bs-or-b)
|
|
(.write sink bs-or-b))
|
|
([^bytes bs off len]
|
|
(proxy-super write bs off len)
|
|
(.write sink bs off len)))
|
|
(close []
|
|
(try (proxy-super close)
|
|
(finally (.close sink))))))
|
|
|
|
#_
|
|
(defn hook-input-stream [input-stream hook]
|
|
(proxy [FilterInputStream] [input-stream]
|
|
(read
|
|
([]
|
|
(let [c (proxy-super read)]
|
|
(when (not= c -1)
|
|
(hook (byte-array [c])))
|
|
c))
|
|
([^bytes bs]
|
|
(let [n (proxy-super read bs)]
|
|
(when (not= n -1)
|
|
(let [bs* (byte-array n 0)]
|
|
(System/arraycopy bs 0 bs* 0 n)
|
|
(hook bs*)))
|
|
n))
|
|
([^bytes bs off len]
|
|
(let [n (proxy-super read bs off len)]
|
|
(when (not= n -1)
|
|
(.write sink bs off n))
|
|
n)))
|
|
(close []
|
|
(try (proxy-super close)
|
|
(finally (.close sink))))))
|
|
|
|
(comment
|
|
(with-open [sink (ByteArrayOutputStream.)
|
|
out (ByteArrayOutputStream.)
|
|
in (ByteArrayInputStream. (.getBytes "hello worms"))]
|
|
(io/copy (tee-input-stream in sink) out)
|
|
(def the-out out)
|
|
(def the-sink sink)
|
|
{:out out
|
|
:sink sink})
|
|
(with-open [sink (l/log-stream :info "blah")
|
|
out (ByteArrayOutputStream.)
|
|
in (ByteArrayInputStream. (.getBytes "hello worms"))]
|
|
(io/copy (tee-input-stream in sink) out)
|
|
(def the-out out)
|
|
(def the-sink sink)
|
|
{:out out
|
|
:sink sink}))
|
|
|
|
(comment
|
|
(let [out (ByteArrayOutputStream.)]
|
|
(p/shell {:out (tee-output-stream
|
|
out (l/log-stream :info "blah"))}
|
|
"echo" "hello\n" "worms")
|
|
(.toString out)))
|
|
|
|
(defn invoke [opts & cmd]
|
|
(l/info (str/join " " (cons "$" cmd)))
|
|
(let [r (apply p/shell
|
|
(merge {:continue true
|
|
:in nil :out :string :err :string}
|
|
opts)
|
|
cmd)
|
|
bin (first cmd)]
|
|
r))
|