cljrs-stdlib 0.2.28

Built-in standard library namespaces for clojurust (clojure.string, clojure.set, clojure.test, …)
Documentation
(ns clojure.java.shell
  "Run a host program and capture its result: the clojure.java.shell API over
   the native clojure.rust.process/run. The program is started by argv, never
   through a shell.")

(def ^:dynamic *sh-dir*
  "Working directory for `sh` calls made in this binding, or nil to inherit."
  nil)

(def ^:dynamic *sh-env*
  "Environment map for `sh` calls made in this binding, or nil to inherit."
  nil)

(defmacro with-sh-dir
  "Evaluate body with `sh` running programs in directory dir."
  [dir & body]
  `(binding [*sh-dir* ~dir] ~@body))

(defmacro with-sh-env
  "Evaluate body with `sh` running programs under environment env, which
   replaces the inherited environment."
  [env & body]
  `(binding [*sh-env* ~env] ~@body))

(defn- utf-8? [enc]
  (or (nil? enc) (contains? #{"UTF-8" "utf-8" "UTF8" "utf8"} enc)))

(defn- env-map [env]
  (cond
    (nil? env) nil
    (map? env) (into {} (map (fn [[k v]] [(if (keyword? k) (name k) (str k)) (str v)]) env))
    :else (into {}
                (map (fn [s]
                       (let [s (str s)
                             i (clojure.string/index-of s "=")]
                         (if (nil? i)
                           (throw (ex-info (str "sh: an :env entry must be NAME=value, got " (pr-str s)) {:entry s}))
                           [(subs s 0 i) (subs s (inc i))])))
                     env))))

(defn sh
  "Run a host program and wait for it to finish.

   args are the program and its arguments as strings, followed by options:
     :in       text for the program's standard input; a non-string is slurped
     :in-enc   encoding of :in (UTF-8 only)
     :out-enc  encoding of stdout: UTF-8 (the default) or :bytes
     :dir      working directory
     :env      environment replacing the inherited one: a map, or a seq of
               \"NAME=value\" strings

   Returns {:exit code :out stdout :err stderr}. A non-zero exit is returned,
   not thrown."
  [& args]
  (let [[cmd opts] (split-with string? args)
        _ (when (empty? cmd)
            (throw (ex-info "sh: the program name must be given as a string" {:args args})))
        _ (when (odd? (count opts))
            (throw (ex-info (str "sh: options must be key/value pairs, got " (pr-str opts)) {:opts opts})))
        {:keys [in in-enc out-enc dir env]
         :or {dir *sh-dir* env *sh-env*}} (apply hash-map opts)]
    (when-not (utf-8? in-enc)
      (throw (ex-info (str "sh: unsupported :in-enc " (pr-str in-enc) "; only UTF-8 is supported") {:in-enc in-enc})))
    (when-not (or (utf-8? out-enc) (= :bytes out-enc))
      (throw (ex-info (str "sh: unsupported :out-enc " (pr-str out-enc) "; only UTF-8 and :bytes are supported") {:out-enc out-enc})))
    (clojure.rust.process/run
     (vec cmd)
     (cond-> {}
       (some? in) (assoc :in (if (string? in) in (slurp in)))
       (some? dir) (assoc :dir (str dir))
       (some? env) (assoc :env (env-map env))
       (= :bytes out-enc) (assoc :out-bytes true)))))