diff --git a/.clj-kondo/config.edn b/.clj-kondo/config.edn index 8bd021f1..3ec45fb6 100644 --- a/.clj-kondo/config.edn +++ b/.clj-kondo/config.edn @@ -1 +1,3 @@ -{:lint-as {me.raynes.conch/let-programs clojure.core/let}} +{:lint-as {me.raynes.conch/let-programs clojure.core/let + babashka.impl.File/gen-wrapper-fn clojure.core/def + babashka.impl.File/gen-wrapper-fn-2 clojure.core/def}} diff --git a/README.md b/README.md index 43ae53a8..c26a376b 100644 --- a/README.md +++ b/README.md @@ -95,24 +95,46 @@ You may also download a binary from [Github](https://github.com/borkdude/babashk ## Usage ``` shellsession -bb [ --help ] | [ --version ] | ( [ -i ] [ -o ] | [ -io ] ) [ --stream ] ( expression | -f ) -``` +$ bb --help +Usage: bb [ --help ] | [ --version ] | ( [ -i ] [ -o ] | [ -io ] ) [ --stream ] ( expression | -f ) -Type `bb --help` to see a full explanation of the options. +Options: + + --help: print this help text. + --version: print the current version of babashka. + + -i: read shell input into a list of strings instead of reading EDN. + -o: write shell output instead of EDN. + -io: combination of -i and -o. + --stream: stream over lines or EDN values from stdin. Combined with -i *in* becomes a single line per iteration. + --file or -f: read expressions from file instead of argument wrapped in an implicit do. + --time: print execution time before exiting. +``` The `clojure.core` functions are accessible without a namespace alias. The following Clojure namespaces are required by default and only available -through the aliases: +through the aliases. If not all vars are available, they are enumerated +explicitly. - `clojure.string` aliased as `str` - `clojure.set` aliased as `set` -- `clojure.edn` aliased as `edn` (only `read-string` is available) -- `clojure.java.shell` aliases as `shell` (only `sh` is available) +- `clojure.edn` aliased as `edn`: + - `read-string` +- `clojure.java.shell` aliases as `shell`: + - `sh` +- `clojure.java.io` aliased as `io`: + - `as-relative-path`, `copy`, `delete-file`, `file` From Java the following is available: - `System`: `exit`, `getProperty`, `setProperty`, `getProperties`, `getenv` +- `File`: `.canRead`, `.canWrite`, `.delete`, `.deleteOnExit`, `.exists`, + `.getAbsoluteFile`, `.getCanonicalFile`, `.getCanonicalPath`, `.getName`, + `.getParent`, `.getParentFile`, `.getPath`, `.isAbsolute`, `.isDirectory`, + `.isFile`, `.isHidden`, `.lastModified`, `.length`, `.list`, `.listFiles`, + `.mkdir`, `.mkdirs`, `.renameTo`, `.setLastModified`, `.setReadOnly`, + `.setReadable`, `.toPath`, `.toURI`. Special vars: diff --git a/src/babashka/impl/File.clj b/src/babashka/impl/File.clj new file mode 100644 index 00000000..641de24d --- /dev/null +++ b/src/babashka/impl/File.clj @@ -0,0 +1,80 @@ +(ns babashka.impl.File + (:refer-clojure :exclude [list]) + (:require [clojure.java.io :as io] + [clojure.string :as str])) + +(set! *warn-on-reflection* true) +;; also see https://gist.github.com/plexus/68594ba9b5e3f0d63fe84dcae31c4a53 + +(defmacro gen-wrapper-fn [method-name] + `(defn ~method-name {:bb/export true} + [~(with-meta 'x {:tag 'java.io.File})] + (~(symbol (str "." method-name)) ~'x))) + +(defmacro gen-wrapper-fn-2 [method-name] + `(defn ~method-name {:bb/export true} + [~(with-meta 'x {:tag 'java.io.File}) ~'y] + (~(symbol (str "." method-name)) ~'x ~'y))) + +(gen-wrapper-fn canRead) +(gen-wrapper-fn canWrite) +(gen-wrapper-fn delete) +(gen-wrapper-fn deleteOnExit) +(gen-wrapper-fn exists) +(gen-wrapper-fn getAbsoluteFile) +(gen-wrapper-fn getCanonicalFile) +(gen-wrapper-fn getCanonicalPath) +(gen-wrapper-fn getName) +(gen-wrapper-fn getParent) +(gen-wrapper-fn getParentFile) +(gen-wrapper-fn getPath) +(gen-wrapper-fn isAbsolute) +(gen-wrapper-fn isDirectory) +(gen-wrapper-fn isFile) +(gen-wrapper-fn isHidden) +(gen-wrapper-fn lastModified) +(gen-wrapper-fn length) +(gen-wrapper-fn list) +(gen-wrapper-fn listFiles) +(gen-wrapper-fn mkdir) +(gen-wrapper-fn mkdirs) +(gen-wrapper-fn-2 renameTo) +(defn setExecutable + ([^java.io.File f b] + (.setExecutable f b)) + ([^java.io.File f b ownerOnly] + (.setExecutable f b ownerOnly))) +(gen-wrapper-fn-2 setLastModified) +(gen-wrapper-fn-2 setReadable) +(gen-wrapper-fn setReadOnly) +(defn setWritable + ([^java.io.File f b] + (.setWritable f b)) + ([^java.io.File f b ownerOnly] + (.setWritable f b ownerOnly))) +(gen-wrapper-fn toPath) +(gen-wrapper-fn toURI) + +(def bindings + (reduce (fn [acc [k v]] + (if (-> v meta :bb/export) + (assoc acc (symbol (str "." k)) + @v) + acc)) + {} + (ns-publics *ns*))) + +(comment + (canRead (clojure.java.io/file "README.md")) + (canWrite (clojure.java.io/file "README.md")) + (exists (clojure.java.io/file "README.md")) + (renameTo (io/file "/tmp/script2.clj") (io/file "/tmp/script.clj")) + (.setWritable (io/file "/tmp/script.clj") true true) + (meta #'toURI) + (first bindings) + (get bindings '.length) + (get bindings '.canWrite) + (meta #'canWrite) + ;; for README.md: + (str/join ", " (map #(format "`%s`" %) (sort (keys bindings)))) + ) diff --git a/src/babashka/main.clj b/src/babashka/main.clj index ebd5576c..1b0e02fc 100644 --- a/src/babashka/main.clj +++ b/src/babashka/main.clj @@ -5,7 +5,8 @@ [clojure.java.io :as io] [clojure.java.shell :as shell] [clojure.string :as str] - [sci.core :as sci]) + [sci.core :as sci] + [babashka.impl.File :as File]) (:gen-class)) (set! *warn-on-reflection* true) @@ -111,23 +112,35 @@ (throw (ex-info "" {:bb/exit-code n}))) (def bindings - {'run! run! - 'shell/sh shell/sh - 'csh shell/sh ;; backwards compatibility, deprecated - 'namespace namespace - 'slurp slurp - 'spit spit - 'pmap pmap - 'print print - 'pr-str pr-str - 'prn prn - 'println println - 'edn/read-string edn/read-string - 'System/getenv get-env - 'System/getProperty get-property - 'System/setProperty set-property - 'System/getProperties get-properties - 'System/exit exit}) + (merge {'run! run! + 'shell/sh shell/sh + 'csh shell/sh ;; backwards compatibility, deprecated + 'namespace namespace + 'slurp slurp + 'spit spit + 'pmap pmap + 'print print + 'pr-str pr-str + 'prn prn + 'println println + + ;; clojure.java.io + 'io/as-relative-path io/as-relative-path + 'io/copy io/copy + 'io/delete-file io/delete-file + 'io/file io/file + ;; '.canRead File/canRead + ;; '.canWrite File/canWrite + ;; '.exists File/exists + ;; '.delete File/delete + + 'edn/read-string edn/read-string + 'System/getenv get-env + 'System/getProperty get-property + 'System/setProperty set-property + 'System/getProperties get-properties + 'System/exit exit} + File/bindings)) (defn read-edn [] (edn/read {;;:readers *data-readers* diff --git a/test/babashka/main_test.clj b/test/babashka/main_test.clj index 5402d5fd..353fb7a4 100644 --- a/test/babashka/main_test.clj +++ b/test/babashka/main_test.clj @@ -110,3 +110,7 @@ ;; THIS TEST REQUIRES: ;; export BABASHKA_PRELOADS='(defn __bb__foo [] "foo") (defn __bb__bar [] "bar")' (is (= "foobar" (bb nil "(str (__bb__foo) (__bb__bar))")))) + +(deftest io-test + (is (true? (bb nil "(.exists (io/file \"README.md\"))"))) + (is (true? (bb nil "(.canWrite (io/file \"README.md\"))"))))