Skip to content

Commit b2f68ae

Browse files
committed
cleaning up the read pipeline
1 parent 43bdf6c commit b2f68ae

File tree

3 files changed

+96
-118
lines changed

3 files changed

+96
-118
lines changed

src/main/clojure/clojure/tools/deps/edn.clj

Lines changed: 95 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -8,87 +8,126 @@
88

99
(ns clojure.tools.deps.edn
1010
(:require
11+
[clojure.edn :as edn]
1112
[clojure.java.io :as jio]
1213
[clojure.string :as str]
13-
[clojure.tools.deps.util.io :as io]
1414
[clojure.tools.deps.specs :as specs]
1515
[clojure.walk :as walk])
1616
(:import
17-
[java.io File]
17+
[java.io File PushbackReader]
1818
[clojure.lang EdnReader$ReaderException]
1919
))
2020

2121
(set! *warn-on-reflection* true)
2222

23-
;;;; deps.edn reading
23+
;;;; Read
24+
25+
(defonce ^:private nl (System/getProperty "line.separator"))
26+
27+
(defn- printerrln
28+
"println to *err*"
29+
[& msgs]
30+
(binding [*out* *err*
31+
*print-readably* nil]
32+
(pr (str (str/join " " msgs) nl))
33+
(flush)))
2434

2535
(defn- io-err
26-
^Throwable [fmt ^File f]
27-
(let [path (.getAbsolutePath f)]
28-
(ex-info (format fmt path) {:path path})))
29-
30-
(defn- slurp-edn-map
31-
"Read the file specified by the path-segments, slurp it, and read it as edn."
32-
[^File f]
33-
(let [val (try (io/slurp-edn f)
34-
(catch EdnReader$ReaderException e (throw (io-err (str (.getMessage e) " (%s)") f)))
35-
(catch RuntimeException t
36-
(if (str/starts-with? (.getMessage t) "EOF while reading")
37-
(throw (io-err "Error reading edn, delimiter unmatched (%s)" f))
38-
(throw (io-err (str "Error reading edn. " (.getMessage t) " (%s)") f)))))]
39-
(if (specs/valid-deps? val)
40-
val
41-
(throw (io-err (str "Error reading deps %s. " (specs/explain-deps val)) f)))))
42-
43-
;; all this canonicalization is deprecated and will eventually be removed
36+
"Helper function to construct an ex-info for an exception reading
37+
file at path with the message format fmt (which should have one
38+
variable for the path)."
39+
^Throwable [fmt & {:keys [path]}]
40+
(let [abs-path (.getAbsolutePath (jio/file path))]
41+
(ex-info (format fmt abs-path) {:path abs-path})))
42+
43+
(defn read-edn
44+
"Read edn from file f, which should contain exactly one edn value.
45+
If f exists but is blank, nil is returned.
46+
Throws if file is unreadable or contains multiple values.
47+
Opts:
48+
:path String path to file being read"
49+
[^File f & opts]
50+
(with-open [rdr (PushbackReader. (jio/reader f))]
51+
(let [EOF (Object.)
52+
val (try
53+
(let [val (edn/read {:default tagged-literal :eof EOF} rdr)]
54+
(if (identical? EOF val)
55+
nil ;; empty file
56+
(if (not (identical? EOF (edn/read {:eof EOF} rdr)))
57+
(throw (ex-info "Expected edn to contain a single value." {}))
58+
val)))
59+
(catch EdnReader$ReaderException e
60+
(throw (io-err (str (.getMessage e) " (%s)") opts)))
61+
(catch RuntimeException t
62+
(if (str/starts-with? (.getMessage t) "EOF while reading")
63+
(throw (io-err "Error reading edn, delimiter unmatched (%s)" opts))
64+
(throw (io-err (str "Error reading edn. " (.getMessage t) " (%s)") opts)))))])))
65+
66+
(defn validate
67+
"Validate a deps-edn map according to the specs, throw if invalid.
68+
Opts:
69+
:path String path to file being read"
70+
[deps-edn & opts]
71+
(if (specs/valid-deps? deps-edn)
72+
deps-edn
73+
(throw (io-err (str "Error reading deps %s. " (specs/explain-deps deps-edn)) opts))))
74+
75+
;;;; Canonicalize
4476

4577
(defn- canonicalize-sym
46-
([s]
47-
(canonicalize-sym s nil))
48-
([s file-name]
49-
(if (simple-symbol? s)
50-
(let [cs (as-> (name s) n (symbol n n))]
51-
(io/printerrln "DEPRECATED: Libs must be qualified, change" s "=>" cs
52-
(if file-name (str "(" file-name ")") ""))
53-
cs)
54-
s)))
78+
[s & opts]
79+
(if (simple-symbol? s)
80+
(let [cs (as-> (name s) n (symbol n n))]
81+
(printerrln "DEPRECATED: Libs must be qualified, change" s "=>" cs
82+
(if-let [path (:path opts)] (str "(" path ")" "")))
83+
cs)
84+
s))
5585

5686
(defn- canonicalize-exclusions
57-
[{:keys [exclusions] :as coord} file-name]
87+
[{:keys [exclusions] :as coord} & opts]
5888
(if (seq (filter simple-symbol? exclusions))
59-
(assoc coord :exclusions (mapv #(canonicalize-sym % file-name) exclusions))
89+
(assoc coord :exclusions (mapv #(canonicalize-sym % opts) exclusions))
6090
coord))
6191

6292
(defn- canonicalize-dep-map
63-
[deps-map file-name]
93+
[deps-map & opts]
6494
(when deps-map
6595
(reduce-kv (fn [acc lib coord]
66-
(let [new-lib (if (simple-symbol? lib) (canonicalize-sym lib file-name) lib)
67-
new-coord (canonicalize-exclusions coord file-name)]
96+
(let [new-lib (if (simple-symbol? lib) (canonicalize-sym lib opts) lib)
97+
new-coord (canonicalize-exclusions coord opts)]
6898
(assoc acc new-lib new-coord)))
6999
{} deps-map)))
70100

71-
(defn- canonicalize-all-syms
72-
([deps-edn]
73-
(canonicalize-all-syms deps-edn nil))
74-
([deps-edn file-name]
75-
(walk/postwalk
76-
(fn [x]
77-
(if (map? x)
78-
(reduce (fn [xr k]
79-
(if-let [xm (get xr k)]
80-
(assoc xr k (canonicalize-dep-map xm file-name))
81-
xr))
82-
x #{:deps :default-deps :override-deps :extra-deps :classpath-overrides})
83-
x))
84-
deps-edn)))
85-
86-
(defn slurp-deps
87-
"Read a single deps.edn file from disk and canonicalize symbols,
88-
return a deps map. If the file doesn't exist, returns nil."
89-
[^File dep-file]
90-
(when (.exists dep-file)
91-
(-> dep-file slurp-edn-map (canonicalize-all-syms (.getPath dep-file)))))
101+
(defn canonicalize
102+
"Canonicalize a deps.edn map (convert simple lib symbols to qualified lib symbols).
103+
Opts:
104+
:path String path to file being read"
105+
[deps-edn & opts]
106+
(walk/postwalk
107+
(fn [x]
108+
(if (map? x)
109+
(reduce (fn [xr k]
110+
(if-let [xm (get xr k)]
111+
(assoc xr k (canonicalize-dep-map xm opts))
112+
xr))
113+
x #{:deps :default-deps :override-deps :extra-deps :classpath-overrides})
114+
x))
115+
deps-edn))
116+
117+
;;;; Read deps with validation and canonicalization
118+
119+
(defn read-deps
120+
"Corece f to a file with jio/file, then read, validate, and canonicalize
121+
the deps.edn. This is the primary entry point for reading.
122+
123+
Opts: none"
124+
[f & opts]
125+
(let [file (jio/file f)
126+
opts {:path (.getPath file)}]
127+
(when (.exists file)
128+
(-> file (read-edn opts) (validate opts) (canonicalize opts)))))
129+
130+
;;;; deps edn manipulation
92131

93132
(defn- merge-or-replace
94133
"If maps, merge, otherwise replace"

src/main/clojure/clojure/tools/deps/util/io.clj

Lines changed: 0 additions & 61 deletions
This file was deleted.

src/test/clojure/clojure/tools/deps/test_edn.clj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
[java.io File]))
77

88
(deftest test-slurp-deps-on-nonexistent-file
9-
(is (nil? (deps-edn/slurp-deps (File. "NONEXISTENT_FILE")))))
9+
(is (nil? (deps-edn/read-deps (File. "NONEXISTENT_FILE")))))
1010

1111
(deftest test-merge-or-replace
1212
(are [vals ret]

0 commit comments

Comments
 (0)