feat: java.io.File model + multimethod/assoc/defmulti fixes for migratus (jolt-hjw)
File API (jolt-hjw): io/file and (File. …) build a tagged :jolt/file value (instance? File true) with a full method surface (isFile/isDirectory/exists/ getName/getPath/getAbsolutePath/listFiles/toPath/delete/createNewFile/…) backed by os/ and file/. file-seq is File-aware (leaves are File values). str/slurp/spit coerce :jolt/file to its path. ClassLoader/getSystemClassLoader + a classloader stub whose getResource returns nil degrade migratus's classpath lookup to the filesystem. java.nio.file Path/FileSystem/PathMatcher are shimmed just enough for script-excluded?'s glob (recursive * / ? matcher). Three bugs found getting migratus's migration discovery to work: - (assoc nil k v) returned a raw janet table, not a map, so assoc-in built tables that count/seq rejected. Now returns an immutable map. - methods/get-method resolved the multimethod symbol at runtime in the current ns, so a bare multifn ref in its defining ns saw an empty table once defmethods lived elsewhere. Now they take the multimethod VALUE and recover the var via a registry (Clojure semantics). - defmulti now drops a leading docstring/attr-map (migratus's multimethods carry docstrings) instead of treating the docstring as the dispatch fn. Conformance 335/335 x3, clojure-test-suite at baseline.
This commit is contained in:
parent
9813186ef9
commit
b304c43333
6 changed files with 164 additions and 19 deletions
|
|
@ -519,7 +519,11 @@
|
||||||
;; via the host dir primitives. Paths (strings), not File objects. (Lives below
|
;; via the host dir primitives. Paths (strings), not File objects. (Lives below
|
||||||
;; tree-seq: forward references are analysis errors now — jolt-2o7.3.)
|
;; tree-seq: forward references are analysis errors now — jolt-2o7.3.)
|
||||||
(defn file-seq [root]
|
(defn file-seq [root]
|
||||||
(tree-seq __dir? __list-dir root))
|
(if (__file? root)
|
||||||
|
;; java.io.File tree: walk via the File method surface so leaves are File
|
||||||
|
;; values callers can invoke .isFile/.getName/slurp on (jolt-hjw).
|
||||||
|
(tree-seq (fn [f] (.isDirectory f)) (fn [f] (seq (.listFiles f))) root)
|
||||||
|
(tree-seq __dir? __list-dir root)))
|
||||||
|
|
||||||
;; Canonical flatten via tree-seq: the leaves (non-sequential nodes) in order.
|
;; Canonical flatten via tree-seq: the leaves (non-sequential nodes) in order.
|
||||||
;; Flattens lists too (sequential?), matching Clojure/CLJS.
|
;; Flattens lists too (sequential?), matching Clojure/CLJS.
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,15 @@
|
||||||
;; clojure.core fns) so they compile as plain invokes. name/mm are passed quoted;
|
;; clojure.core fns) so they compile as plain invokes. name/mm are passed quoted;
|
||||||
;; the dispatch fn, options, and dispatch value evaluate normally, and the method
|
;; the dispatch fn, options, and dispatch value evaluate normally, and the method
|
||||||
;; body becomes a compiled (fn …).
|
;; body becomes a compiled (fn …).
|
||||||
(defmacro defmulti [name dispatch & opts]
|
;; Clojure allows (defmulti name docstring? attr-map? dispatch-fn & options);
|
||||||
`(defmulti-setup (quote ~name) ~dispatch ~@opts))
|
;; drop a leading docstring and/or attr-map so the dispatch fn isn't mistaken for
|
||||||
|
;; one (migratus's multimethods carry docstrings).
|
||||||
|
(defmacro defmulti [name & args]
|
||||||
|
(let [args (if (string? (first args)) (rest args) args)
|
||||||
|
args (if (and (map? (first args)) (not (symbol? (first args)))) (rest args) args)
|
||||||
|
dispatch (first args)
|
||||||
|
opts (rest args)]
|
||||||
|
`(defmulti-setup (quote ~name) ~dispatch ~@opts)))
|
||||||
|
|
||||||
(defmacro defmethod [mm dispatch-val & fn-tail]
|
(defmacro defmethod [mm dispatch-val & fn-tail]
|
||||||
`(defmethod-setup (quote ~mm) ~dispatch-val (fn ~@fn-tail)))
|
`(defmethod-setup (quote ~mm) ~dispatch-val (fn ~@fn-tail)))
|
||||||
|
|
@ -39,11 +46,14 @@
|
||||||
(defmacro remove-all-methods [mm]
|
(defmacro remove-all-methods [mm]
|
||||||
`(remove-all-methods-setup (quote ~mm)))
|
`(remove-all-methods-setup (quote ~mm)))
|
||||||
|
|
||||||
|
;; methods/get-method take the multimethod VALUE (Clojure semantics); the setup
|
||||||
|
;; maps it back to its var via the registry, so a bare multifn ref works from a
|
||||||
|
;; compiled fn in any namespace (jolt multimethod table-visibility fix).
|
||||||
(defmacro get-method [mm dval]
|
(defmacro get-method [mm dval]
|
||||||
`(get-method-setup (quote ~mm) ~dval))
|
`(get-method-setup ~mm ~dval))
|
||||||
|
|
||||||
(defmacro methods [mm]
|
(defmacro methods [mm]
|
||||||
`(methods-setup (quote ~mm)))
|
`(methods-setup ~mm))
|
||||||
|
|
||||||
;; prefers reads the store off the VAR (the multifn value can't carry it) —
|
;; prefers reads the store off the VAR (the multifn value can't carry it) —
|
||||||
;; same symbol-passing shape as the other multimethod table ops.
|
;; same symbol-passing shape as the other multimethod table ops.
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,13 @@
|
||||||
; …) won't work, but file/reader/writer/resource/copy/slurp do.
|
; …) won't work, but file/reader/writer/resource/copy/slurp do.
|
||||||
|
|
||||||
(defn file
|
(defn file
|
||||||
"A file path. With a parent and child, joins them with '/'."
|
"A java.io.File. With a parent and child, joins them with '/'. Returns a
|
||||||
([path] (str path))
|
:jolt/file value (instance? File true) with the File method surface; str/slurp
|
||||||
([parent child] (str parent "/" child)))
|
and io/reader coerce it back to its path."
|
||||||
|
([path] (__make-file path))
|
||||||
|
([parent child] (__make-file parent child)))
|
||||||
|
|
||||||
(defn as-file [x] (str x))
|
(defn as-file [x] (if (__file? x) x (__make-file x)))
|
||||||
|
|
||||||
(defn reader [x]
|
(defn reader [x]
|
||||||
(cond
|
(cond
|
||||||
|
|
|
||||||
|
|
@ -541,7 +541,10 @@
|
||||||
result)
|
result)
|
||||||
(do (var result @{}) (when m (each k (keys m) (put result k (get m k))))
|
(do (var result @{}) (when m (each k (keys m) (put result k (get m k))))
|
||||||
(var i 0) (while (< i (length kvs)) (let [k (kvs i) v (kvs (+ i 1))] (put result k v) (+= i 2)))
|
(var i 0) (while (< i (length kvs)) (let [k (kvs i) v (kvs (+ i 1))] (put result k v) (+= i 2)))
|
||||||
(if (struct? m) (table/to-struct result) result))))))
|
# nil assocs to a fresh immutable map ((assoc nil :a 1) => {:a 1}); a
|
||||||
|
# raw table here would not count?/seq like a Clojure map (assoc-in into
|
||||||
|
# an absent key recurses through nil — migratus's migration maps).
|
||||||
|
(if (or (struct? m) (nil? m)) (table/to-struct result) result))))))
|
||||||
|
|
||||||
(defn core-dissoc [m & ks]
|
(defn core-dissoc [m & ks]
|
||||||
(cond
|
(cond
|
||||||
|
|
@ -1656,6 +1659,8 @@
|
||||||
(if (v :ns) (string (v :ns) "/" (v :name)) (v :name))
|
(if (v :ns) (string (v :ns) "/" (v :name)) (v :name))
|
||||||
(and (struct? v) (= :jolt/uuid (v :jolt/type))) (v :str)
|
(and (struct? v) (= :jolt/uuid (v :jolt/type))) (v :str)
|
||||||
(and (struct? v) (= :jolt/inst (v :jolt/type))) (inst->rfc3339 v)
|
(and (struct? v) (= :jolt/inst (v :jolt/type))) (inst->rfc3339 v)
|
||||||
|
# a java.io.File renders as its path (Clojure's File.toString)
|
||||||
|
(and (table? v) (= :jolt/file (get v :jolt/type))) (get v :path)
|
||||||
(= :jolt/namespace (get v :jolt/type)) (ns-display-name v)
|
(= :jolt/namespace (get v :jolt/type)) (ns-display-name v)
|
||||||
(and (table? v) (= :jolt/var (get v :jolt/type))) (var-display v)
|
(and (table? v) (= :jolt/var (get v :jolt/type))) (var-display v)
|
||||||
(number? v) (fmt-number v)
|
(number? v) (fmt-number v)
|
||||||
|
|
@ -1744,6 +1749,19 @@
|
||||||
(defn core-jdbc-make-stmt [w]
|
(defn core-jdbc-make-stmt [w]
|
||||||
@{:jolt/type :jolt/jdbc-stmt :exec (get w :exec) :cmds @[]})
|
@{:jolt/type :jolt/jdbc-stmt :exec (get w :exec) :cmds @[]})
|
||||||
|
|
||||||
|
# java.io.File model (jolt-hjw). io/file and (File. …) build a tagged :jolt/file
|
||||||
|
# value so (instance? File x) works and migratus's File-vs-jar branching takes
|
||||||
|
# the filesystem path. The File method surface + nio glob live in javatime; here
|
||||||
|
# are the constructor/predicate builtins and the path coercion str/slurp use.
|
||||||
|
(defn core-file-path
|
||||||
|
"The path string of a :jolt/file, or (string x) for anything else."
|
||||||
|
[x]
|
||||||
|
(if (and (table? x) (= :jolt/file (get x :jolt/type))) (get x :path) (string x)))
|
||||||
|
(defn core-make-file [path &opt child]
|
||||||
|
(def base (core-file-path path))
|
||||||
|
@{:jolt/type :jolt/file :path (if child (string base "/" (core-file-path child)) base)})
|
||||||
|
(defn core-file? [x] (and (table? x) (= :jolt/file (get x :jolt/type))))
|
||||||
|
|
||||||
# newline lives in the Clojure collection tier (core/20-coll.clj).
|
# newline lives in the Clojure collection tier (core/20-coll.clj).
|
||||||
|
|
||||||
# Clojure 1.11 string->scalar parsers: nil on malformed input, throw on a
|
# Clojure 1.11 string->scalar parsers: nil on malformed input, throw on a
|
||||||
|
|
@ -1787,6 +1805,7 @@
|
||||||
# bodies, and the jolt Ring adapter hands those over as StringReaders.
|
# bodies, and the jolt Ring adapter hands those over as StringReaders.
|
||||||
(defn core-slurp [src & opts]
|
(defn core-slurp [src & opts]
|
||||||
(cond
|
(cond
|
||||||
|
(core-file? src) (string (slurp (core-file-path src)))
|
||||||
(and (table? src) (string? (get src :s)) (number? (get src :pos)))
|
(and (table? src) (string? (get src :s)) (number? (get src :pos)))
|
||||||
(let [s (src :s) p (src :pos)]
|
(let [s (src :s) p (src :pos)]
|
||||||
(put src :pos (length s))
|
(put src :pos (length s))
|
||||||
|
|
@ -1799,7 +1818,7 @@
|
||||||
(when (and (= :append (in opts i)) (in opts (+ i 1))) (set a true))
|
(when (and (= :append (in opts i)) (in opts (+ i 1))) (set a true))
|
||||||
(+= i 2))
|
(+= i 2))
|
||||||
a))
|
a))
|
||||||
(def f (file/open path (if append? :a :w)))
|
(def f (file/open (core-file-path path) (if append? :a :w)))
|
||||||
(file/write f (str-render-one content))
|
(file/write f (str-render-one content))
|
||||||
(file/close f)
|
(file/close f)
|
||||||
nil)
|
nil)
|
||||||
|
|
@ -2731,6 +2750,8 @@
|
||||||
"__jdbc-wrap-conn" core-jdbc-wrap-conn
|
"__jdbc-wrap-conn" core-jdbc-wrap-conn
|
||||||
"__jdbc-conn-raw" core-jdbc-conn-raw
|
"__jdbc-conn-raw" core-jdbc-conn-raw
|
||||||
"__jdbc-make-stmt" core-jdbc-make-stmt
|
"__jdbc-make-stmt" core-jdbc-make-stmt
|
||||||
|
"__make-file" core-make-file
|
||||||
|
"__file?" core-file?
|
||||||
"__pr-str1" core-pr-str1
|
"__pr-str1" core-pr-str1
|
||||||
"__make-uuid" make-uuid
|
"__make-uuid" make-uuid
|
||||||
"compare" core-compare
|
"compare" core-compare
|
||||||
|
|
|
||||||
|
|
@ -1197,6 +1197,13 @@
|
||||||
(ns-unmap ns (if (and (struct? sym) (= :symbol (sym :jolt/type))) (sym :name) (string sym))))))
|
(ns-unmap ns (if (and (struct? sym) (= :symbol (sym :jolt/type))) (sym :name) (string sym))))))
|
||||||
nil)
|
nil)
|
||||||
|
|
||||||
|
# Multimethod value -> its var. methods/get-method take the multimethod VALUE
|
||||||
|
# (Clojure semantics) and recover the var (hence :jolt/methods) through this,
|
||||||
|
# which works from a compiled fn in any namespace — resolving the symbol at call
|
||||||
|
# time in the current ns did not (a bare multifn ref in its defining ns saw an
|
||||||
|
# empty table once defmethods lived in other namespaces; migratus hit this).
|
||||||
|
(def multi-registry @{})
|
||||||
|
|
||||||
(defn defmulti-setup
|
(defn defmulti-setup
|
||||||
"(defmulti name dispatch & opts) — intern a multimethod var. A fn; name arrives
|
"(defmulti name dispatch & opts) — intern a multimethod var. A fn; name arrives
|
||||||
quoted, dispatch + opts (:default key, :hierarchy h) arrive evaluated. The
|
quoted, dispatch + opts (:default key, :hierarchy h) arrive evaluated. The
|
||||||
|
|
@ -1292,6 +1299,7 @@
|
||||||
(put v :jolt/dispatch-cache dispatch-cache)
|
(put v :jolt/dispatch-cache dispatch-cache)
|
||||||
(put v :jolt/default default-key)
|
(put v :jolt/default default-key)
|
||||||
(when hierarchy (put v :jolt/hierarchy hierarchy))
|
(when hierarchy (put v :jolt/hierarchy hierarchy))
|
||||||
|
(put multi-registry mm-fn v)
|
||||||
(var-get v))
|
(var-get v))
|
||||||
|
|
||||||
(defn defmethod-setup
|
(defn defmethod-setup
|
||||||
|
|
@ -1301,9 +1309,11 @@
|
||||||
[ctx mm-sym dispatch-val impl]
|
[ctx mm-sym dispatch-val impl]
|
||||||
(def mm-var
|
(def mm-var
|
||||||
(or (resolve-var ctx @{} mm-sym)
|
(or (resolve-var ctx @{} mm-sym)
|
||||||
(let [ns (ctx-find-ns ctx (ctx-current-ns ctx))]
|
(let [ns (ctx-find-ns ctx (ctx-current-ns ctx))
|
||||||
(def v (ns-intern ns (mm-sym :name) (fn [& args] nil)))
|
stub (fn [& args] nil)]
|
||||||
|
(def v (ns-intern ns (mm-sym :name) stub))
|
||||||
(put v :jolt/methods @{})
|
(put v :jolt/methods @{})
|
||||||
|
(put multi-registry stub v)
|
||||||
v)))
|
v)))
|
||||||
(def methods (or (get mm-var :jolt/methods) (let [m @{}] (put mm-var :jolt/methods m) m)))
|
(def methods (or (get mm-var :jolt/methods) (let [m @{}] (put mm-var :jolt/methods m) m)))
|
||||||
# nil is a legal dispatch value (ring's body-string keys a method on it);
|
# nil is a legal dispatch value (ring's body-string keys a method on it);
|
||||||
|
|
@ -1470,8 +1480,10 @@
|
||||||
found
|
found
|
||||||
(when auto-create?
|
(when auto-create?
|
||||||
(def ns (ctx-find-ns ctx (ctx-current-ns ctx)))
|
(def ns (ctx-find-ns ctx (ctx-current-ns ctx)))
|
||||||
(def nv (ns-intern ns (mm-sym :name) (fn [& args] nil)))
|
(def stub (fn [& args] nil))
|
||||||
|
(def nv (ns-intern ns (mm-sym :name) stub))
|
||||||
(put nv :jolt/methods @{})
|
(put nv :jolt/methods @{})
|
||||||
|
(put multi-registry stub nv)
|
||||||
nv))))
|
nv))))
|
||||||
(def clear-dispatch-cache! (fn [mm-var]
|
(def clear-dispatch-cache! (fn [mm-var]
|
||||||
(let [dc (get mm-var :jolt/dispatch-cache)]
|
(let [dc (get mm-var :jolt/dispatch-cache)]
|
||||||
|
|
@ -1510,16 +1522,21 @@
|
||||||
(fn [mm-sym]
|
(fn [mm-sym]
|
||||||
(def mm-var (mm-var-of mm-sym false))
|
(def mm-var (mm-var-of mm-sym false))
|
||||||
(or (and mm-var (get mm-var :jolt/prefers)) {})))
|
(or (and mm-var (get mm-var :jolt/prefers)) {})))
|
||||||
|
# methods/get-method receive the multimethod VALUE (Clojure semantics): map it
|
||||||
|
# back to its var via multi-registry. A symbol arg still works (mm-var-of), for
|
||||||
|
# any caller that passes one.
|
||||||
|
(def mm-var-of-val (fn [mm]
|
||||||
|
(if (function? mm) (get multi-registry mm) (mm-var-of mm false))))
|
||||||
(ns-intern core "get-method-setup"
|
(ns-intern core "get-method-setup"
|
||||||
(fn [mm-sym dval]
|
(fn [mm dval]
|
||||||
(def dval (if (nil? dval) :jolt/nil-sentinel dval))
|
(def dval (if (nil? dval) :jolt/nil-sentinel dval))
|
||||||
(def mm-var (mm-var-of mm-sym false))
|
(def mm-var (mm-var-of-val mm))
|
||||||
(when mm-var
|
(when mm-var
|
||||||
(let [methods (get mm-var :jolt/methods)]
|
(let [methods (get mm-var :jolt/methods)]
|
||||||
(or (get methods dval) (get methods :default))))))
|
(or (get methods dval) (get methods :default))))))
|
||||||
(ns-intern core "methods-setup"
|
(ns-intern core "methods-setup"
|
||||||
(fn [mm-sym]
|
(fn [mm]
|
||||||
(def mm-var (mm-var-of mm-sym false))
|
(def mm-var (mm-var-of-val mm))
|
||||||
(when mm-var
|
(when mm-var
|
||||||
# a jolt map, not the live host table (and phm so vector dispatch
|
# a jolt map, not the live host table (and phm so vector dispatch
|
||||||
# values look up by value, same reason build-eval-map promotes)
|
# values look up by value, same reason build-eval-map promotes)
|
||||||
|
|
@ -1588,6 +1605,10 @@
|
||||||
# branch, so the wrapped conn must answer true here.
|
# branch, so the wrapped conn must answer true here.
|
||||||
"Connection" (and (table? val) (= :jolt/jdbc-conn (get val :jolt/type)))
|
"Connection" (and (table? val) (= :jolt/jdbc-conn (get val :jolt/type)))
|
||||||
"java.sql.Connection" (and (table? val) (= :jolt/jdbc-conn (get val :jolt/type)))
|
"java.sql.Connection" (and (table? val) (= :jolt/jdbc-conn (get val :jolt/type)))
|
||||||
|
# java.io.File model (jolt-hjw): io/file and (File. …) build :jolt/file,
|
||||||
|
# so migratus's (instance? File migration-dir) takes the filesystem path.
|
||||||
|
"File" (and (table? val) (= :jolt/file (get val :jolt/type)))
|
||||||
|
"java.io.File" (and (table? val) (= :jolt/file (get val :jolt/type)))
|
||||||
# JVM char[] class — (Class/forName "[C"); jolt char arrays are Janet
|
# JVM char[] class — (Class/forName "[C"); jolt char arrays are Janet
|
||||||
# arrays of char structs
|
# arrays of char structs
|
||||||
"[C" (and (array? val)
|
"[C" (and (array? val)
|
||||||
|
|
|
||||||
|
|
@ -537,6 +537,15 @@
|
||||||
# that walks Thread/currentThread doesn't crash.
|
# that walks Thread/currentThread doesn't crash.
|
||||||
(register-tagged-methods! :jolt/thread
|
(register-tagged-methods! :jolt/thread
|
||||||
@{"getContextClassLoader" (fn [self] @{:jolt/type :jolt/classloader})})
|
@{"getContextClassLoader" (fn [self] @{:jolt/type :jolt/classloader})})
|
||||||
|
# ClassLoader degrade (jolt-hjw): there is no classpath, so getResource returns
|
||||||
|
# nil and migratus's find-migration-dir falls through to the filesystem branch
|
||||||
|
# (resources/<dir>). getSystemClassLoader yields the same stub.
|
||||||
|
(each nm ["ClassLoader" "java.lang.ClassLoader"]
|
||||||
|
(register-class-statics! nm @{"getSystemClassLoader" (fn [] @{:jolt/type :jolt/classloader})}))
|
||||||
|
(register-tagged-methods! :jolt/classloader
|
||||||
|
@{"getResource" (fn [self path] nil)
|
||||||
|
"getResources" (fn [self path] nil)
|
||||||
|
"getResourceAsStream" (fn [self path] nil)})
|
||||||
# next.jdbc host shims (paired with the __jdbc-* builtins in core.janet and the
|
# next.jdbc host shims (paired with the __jdbc-* builtins in core.janet and the
|
||||||
# instance? Connection case in evaluator.janet). The wrapped connection carries
|
# instance? Connection case in evaluator.janet). The wrapped connection carries
|
||||||
# a clj :exec callback (run one SQL string) and a :close callback.
|
# a clj :exec callback (run one SQL string) and a :close callback.
|
||||||
|
|
@ -563,7 +572,85 @@
|
||||||
(def out @[])
|
(def out @[])
|
||||||
(each c (get self :cmds) (array/push out ((get self :exec) c)))
|
(each c (get self :cmds) (array/push out ((get self :exec) c)))
|
||||||
out)
|
out)
|
||||||
"close" (fn [self] nil)}))
|
"close" (fn [self] nil)})
|
||||||
|
# java.io.File model (jolt-hjw). A :jolt/file carries its path; io/file and the
|
||||||
|
# File. ctor build it (see core.janet's __make-file). The method surface below
|
||||||
|
# is backed by os/ and file/. listFiles returns child :jolt/file values so
|
||||||
|
# file-seq (File-aware) yields :jolt/file leaves migratus can call methods on.
|
||||||
|
(defn- jfile-path [x]
|
||||||
|
(if (and (table? x) (= :jolt/file (get x :jolt/type))) (get x :path) (string x)))
|
||||||
|
(defn- make-jfile [path &opt child]
|
||||||
|
@{:jolt/type :jolt/file
|
||||||
|
:path (if child (string (jfile-path path) "/" (jfile-path child)) (jfile-path path))})
|
||||||
|
(defn- last-slash [p]
|
||||||
|
(var idx nil) (var i 0)
|
||||||
|
(while (< i (length p)) (when (= (p i) 47) (set idx i)) (++ i))
|
||||||
|
idx)
|
||||||
|
(defn- jfile-name [p]
|
||||||
|
(if-let [i (last-slash p)] (string/slice p (+ i 1)) p))
|
||||||
|
(defn- jfile-abs [p]
|
||||||
|
(if (string/has-prefix? "/" p) p (string (os/cwd) "/" p)))
|
||||||
|
(each nm ["File" "java.io.File"]
|
||||||
|
(register-class-ctor! nm make-jfile))
|
||||||
|
(register-tagged-methods! :jolt/file
|
||||||
|
@{"getPath" (fn [self] (get self :path))
|
||||||
|
"toString" (fn [self] (get self :path))
|
||||||
|
"getName" (fn [self] (jfile-name (get self :path)))
|
||||||
|
"getParent" (fn [self] (let [p (get self :path)]
|
||||||
|
(if-let [i (last-slash p)] (string/slice p 0 i) nil)))
|
||||||
|
"getAbsolutePath" (fn [self] (jfile-abs (get self :path)))
|
||||||
|
"getCanonicalPath" (fn [self] (jfile-abs (get self :path)))
|
||||||
|
"getAbsoluteFile" (fn [self] (make-jfile (jfile-abs (get self :path))))
|
||||||
|
"exists" (fn [self] (not (nil? (os/stat (get self :path)))))
|
||||||
|
"isFile" (fn [self] (= :file (os/stat (get self :path) :mode)))
|
||||||
|
"isDirectory" (fn [self] (= :directory (os/stat (get self :path) :mode)))
|
||||||
|
"canRead" (fn [self] (not (nil? (os/stat (get self :path)))))
|
||||||
|
# listFiles: child File values, or nil when not a directory (Clojure null)
|
||||||
|
"listFiles" (fn [self]
|
||||||
|
(let [p (get self :path)]
|
||||||
|
(when (= :directory (os/stat p :mode))
|
||||||
|
(map (fn [e] (make-jfile p e)) (os/dir p)))))
|
||||||
|
"list" (fn [self]
|
||||||
|
(let [p (get self :path)]
|
||||||
|
(when (= :directory (os/stat p :mode)) (os/dir p))))
|
||||||
|
"toPath" (fn [self] @{:jolt/type :jolt/nio-path :s (get self :path)})
|
||||||
|
"toURI" (fn [self] (string "file:" (jfile-abs (get self :path))))
|
||||||
|
"toURL" (fn [self] @{:jolt/type :jolt/url :url (string "file:" (jfile-abs (get self :path)))})
|
||||||
|
"delete" (fn [self] (let [r (protect (os/rm (get self :path)))] (truthy? (r 0))))
|
||||||
|
"mkdir" (fn [self] (truthy? ((protect (os/mkdir (get self :path))) 0)))
|
||||||
|
"mkdirs" (fn [self] (truthy? ((protect (os/mkdir (get self :path))) 0)))
|
||||||
|
"createNewFile" (fn [self]
|
||||||
|
(let [p (get self :path)]
|
||||||
|
(if (os/stat p) false
|
||||||
|
(do (def f (file/open p :w)) (file/close f) true))))
|
||||||
|
"equals" (fn [self o] (and (table? o) (= (get self :path) (get o :path))))
|
||||||
|
"hashCode" (fn [self] (hash (get self :path)))})
|
||||||
|
# java.nio.file degrade for migratus's script-excluded? glob check: just enough
|
||||||
|
# of Path / FileSystem / PathMatcher to match a filename against a glob, with a
|
||||||
|
# simple recursive * / ? matcher (no path-segment semantics — filenames only).
|
||||||
|
(defn- glob-matches? [glob s]
|
||||||
|
(defn m [gi si]
|
||||||
|
(cond
|
||||||
|
(= gi (length glob)) (= si (length s))
|
||||||
|
(= (glob gi) 42) # *
|
||||||
|
(or (m (+ gi 1) si)
|
||||||
|
(and (< si (length s)) (m gi (+ si 1))))
|
||||||
|
(and (< si (length s))
|
||||||
|
(or (= (glob gi) 63) (= (glob gi) (s si)))) # ? or literal
|
||||||
|
(m (+ gi 1) (+ si 1))
|
||||||
|
false))
|
||||||
|
(m 0 0))
|
||||||
|
(register-tagged-methods! :jolt/nio-path
|
||||||
|
@{"getFileSystem" (fn [self] @{:jolt/type :jolt/nio-fs})
|
||||||
|
"toString" (fn [self] (get self :s))})
|
||||||
|
(register-tagged-methods! :jolt/nio-fs
|
||||||
|
@{"getPath" (fn [self s & _] @{:jolt/type :jolt/nio-path :s s})
|
||||||
|
"getPathMatcher" (fn [self spec]
|
||||||
|
@{:jolt/type :jolt/nio-matcher
|
||||||
|
:glob (if (string/has-prefix? "glob:" spec)
|
||||||
|
(string/slice spec 5) spec)})})
|
||||||
|
(register-tagged-methods! :jolt/nio-matcher
|
||||||
|
@{"matches" (fn [self path] (glob-matches? (get self :glob) (get path :s)))}))
|
||||||
|
|
||||||
(install!)
|
(install!)
|
||||||
(install-io!)
|
(install-io!)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue