Skip to content

Commit 314dd98

Browse files
anmonteiroswannodette
authored andcommitted
CLJS-2052: Port new spec.alpha enhancements
1 parent c567440 commit 314dd98

File tree

3 files changed

+124
-83
lines changed

3 files changed

+124
-83
lines changed

src/main/cljs/cljs/spec/alpha.cljc

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,7 @@
321321
"Returns a regex op that matches zero or one value matching
322322
pred. Produces a single value (not a collection) if matched."
323323
[pred-form]
324-
`(maybe-impl ~pred-form '~pred-form))
324+
`(maybe-impl ~pred-form '~(res &env pred-form)))
325325

326326
(defmacro alt
327327
"Takes key+pred pairs, e.g.
@@ -386,7 +386,7 @@
386386
387387
Optionally takes :gen generator-fn, which must be a fn of no args
388388
that returns a test.check generator."
389-
[& {:keys [args ret fn gen]}]
389+
[& {:keys [args ret fn gen] :or {ret `any}}]
390390
(let [env &env]
391391
`(fspec-impl (spec ~args) '~(res env args)
392392
(spec ~ret) '~(res env ret)
@@ -421,7 +421,7 @@
421421
by calling get-spec with the var or full-qualified symbol.
422422
423423
Once registered, function specs are included in doc, checked by
424-
instrument, tested by the runner clojure.spec.test/run-tests, and (if
424+
instrument, tested by the runner clojure.spec.test.alpha/run-tests, and (if
425425
a macro) used to explain errors during macroexpansion.
426426
427427
Note that :fn specs require the presence of :args and :ret specs to
@@ -479,8 +479,8 @@
479479
(gen/large-integer* {:min st# :max et#}))))))
480480

481481
(defmacro int-in
482-
"Returns a spec that validates longs in the range from start
483-
(inclusive) to end (exclusive)."
482+
"Returns a spec that validates fixed precision integers in the
483+
range from start (inclusive) to end (exclusive)."
484484
[start end]
485485
`(spec (and c/int? #(int-in-range? ~start ~end %))
486486
:gen #(gen/large-integer* {:min ~start :max (dec ~end)})))
@@ -529,8 +529,10 @@
529529
`(get-spec '~(:name (resolve &env sym)))
530530
fspec)
531531
f# ~sym]
532-
(for [args# (gen/sample (gen (:args fspec#)) ~n)]
533-
[args# (apply f# args#)])))))
532+
(if-let [arg-spec# (c/and fspec# (:args fspec#))]
533+
(for [args# (gen/sample (gen arg-spec#) ~n)]
534+
[args# (apply f# args#)])
535+
(throw (js/Error. "No :args spec found, can't generate")))))))
534536

535537
(defmacro ^:private init-compile-asserts []
536538
(let [compile-asserts (not (-> env/*compiler* deref :options :elide-asserts))]

src/main/cljs/cljs/spec/alpha.cljs

Lines changed: 50 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,9 @@
200200
(defn explain-data* [spec path via in x]
201201
(when-let [probs (explain* (specize spec) path via in x)]
202202
(when-not (empty? probs)
203-
{::problems probs})))
203+
{::problems probs
204+
::spec spec
205+
::value x})))
204206

205207
(defn explain-data
206208
"Given a spec and a value x which ought to conform, returns nil if x
@@ -215,32 +217,33 @@
215217
"Default printer for explain-data. nil indicates a successful validation."
216218
[ed]
217219
(if ed
218-
(print
219-
(with-out-str
220-
;;(prn {:ed ed})
221-
(doseq [{:keys [path pred val reason via in] :as prob} (::problems ed)]
222-
(when-not (empty? in)
223-
(print "In:" (pr-str in) ""))
224-
(print "val: ")
225-
(pr val)
226-
(print " fails")
227-
(when-not (empty? via)
228-
(print " spec:" (pr-str (last via))))
229-
(when-not (empty? path)
230-
(print " at:" (pr-str path)))
231-
(print " predicate: ")
232-
(pr (abbrev pred))
233-
(when reason (print ", " reason))
234-
(doseq [[k v] prob]
235-
(when-not (#{:path :pred :val :reason :via :in} k)
236-
(print "\n\t" (pr-str k) " ")
237-
(pr v)))
238-
(newline))
239-
(doseq [[k v] ed]
240-
(when-not (#{::problems} k)
241-
(print (pr-str k) " ")
242-
(pr v)
243-
(newline)))))
220+
(let [problems (sort-by #(- (count (:path %))) (::problems ed))]
221+
(print
222+
(with-out-str
223+
;;(prn {:ed ed})
224+
(doseq [{:keys [path pred val reason via in] :as prob} problems]
225+
(when-not (empty? in)
226+
(print "In:" (pr-str in) ""))
227+
(print "val: ")
228+
(pr val)
229+
(print " fails")
230+
(when-not (empty? via)
231+
(print " spec:" (pr-str (last via))))
232+
(when-not (empty? path)
233+
(print " at:" (pr-str path)))
234+
(print " predicate: ")
235+
(pr (abbrev pred))
236+
(when reason (print ", " reason))
237+
(doseq [[k v] prob]
238+
(when-not (#{:path :pred :val :reason :via :in} k)
239+
(print "\n\t" (pr-str k) " ")
240+
(pr v)))
241+
(newline))
242+
(doseq [[k v] ed]
243+
(when-not (#{::problems} k)
244+
(print (pr-str k) " ")
245+
(pr v)
246+
(newline))))))
244247
(println "Success!")))
245248

246249
(def ^:dynamic *explain-out* explain-printer)
@@ -371,7 +374,7 @@
371374
(let [pred (maybe-spec pred)]
372375
(if (spec? pred)
373376
(explain* pred path (if-let [name (spec-name pred)] (conj via name) via) in v)
374-
[{:path path :pred (abbrev form) :val v :via via :in in}])))
377+
[{:path path :pred form :val v :via via :in in}])))
375378

376379
(defn ^:skip-wiki map-spec-impl
377380
"Do not call this directly, use 'spec' with a map argument"
@@ -417,7 +420,7 @@
417420
[{:path path :pred 'map? :val x :via via :in in}]
418421
(let [reg (registry)]
419422
(apply concat
420-
(when-let [probs (->> (map (fn [pred form] (when-not (pred x) (abbrev form)))
423+
(when-let [probs (->> (map (fn [pred form] (when-not (pred x) form))
421424
pred-exprs pred-forms)
422425
(keep identity)
423426
seq)]
@@ -482,7 +485,7 @@
482485
x))
483486
(explain* [_ path via in x]
484487
(when (invalid? (dt pred x form cpred?))
485-
[{:path path :pred (abbrev form) :val x :via via :in in}]))
488+
[{:path path :pred form :val x :via via :in in}]))
486489
(gen* [_ _ _ _] (if gfn
487490
(gfn)
488491
(gen/gen-for-pred pred)))
@@ -518,7 +521,7 @@
518521
path (conj path dv)]
519522
(if-let [pred (predx x)]
520523
(explain-1 form pred path via in x)
521-
[{:path path :pred (abbrev form) :val x :reason "no method" :via via :in in}])))
524+
[{:path path :pred form :val x :reason "no method" :via via :in in}])))
522525
(gen* [_ overrides path rmap]
523526
(if gfn
524527
(gfn)
@@ -863,7 +866,15 @@
863866
(c/or (nil? vseq) (= i limit)) x
864867
(valid? spec v) (recur (inc i) vs)
865868
:else ::invalid)))))))
866-
(unform* [_ x] x)
869+
(unform* [_ x]
870+
(if conform-all
871+
(let [spec @spec
872+
[init add complete] (cfns x)]
873+
(loop [ret (init x), i 0, [v & vs :as vseq] (seq x)]
874+
(if (>= i (c/count x))
875+
(complete ret)
876+
(recur (add ret i v (unform* spec v)) (inc i) vs))))
877+
x))
867878
(explain* [_ path via in x]
868879
(c/or (coll-prob x kind kind-form distinct count min-count max-count
869880
path via in)
@@ -1089,7 +1100,7 @@
10891100
(case op
10901101
::accept nil
10911102
nil p
1092-
::amp (list* 'clojure.spec/& (op-describe p1) forms)
1103+
::amp (list* 'clojure.spec.alpha/& (op-describe p1) forms)
10931104
::pcat (if rep+
10941105
(list `+ rep+)
10951106
(cons `cat (mapcat vector (c/or (seq ks) (repeat :_)) forms)))
@@ -1106,7 +1117,7 @@
11061117
insufficient (fn [path form]
11071118
[{:path path
11081119
:reason "Insufficient input"
1109-
:pred (abbrev form)
1120+
:pred form
11101121
:val ()
11111122
:via via
11121123
:in in}])]
@@ -1218,14 +1229,14 @@
12181229
(op-explain (op-describe p) p path via (conj in i) (seq data))
12191230
[{:path path
12201231
:reason "Extra input"
1221-
:pred (abbrev (op-describe re))
1232+
:pred (op-describe re)
12221233
:val data
12231234
:via via
12241235
:in (conj in i)}])
12251236
(c/or (op-explain (op-describe p) p path via (conj in i) (seq data))
12261237
[{:path path
12271238
:reason "Extra input"
1228-
:pred (abbrev (op-describe p))
1239+
:pred (op-describe p)
12291240
:val data
12301241
:via via
12311242
:in (conj in i)}]))))))
@@ -1247,7 +1258,7 @@
12471258
(explain* [_ path via in x]
12481259
(if (c/or (nil? x) (coll? x))
12491260
(re-explain path via in re (seq x))
1250-
[{:path path :pred (abbrev (op-describe re)) :val x :via via :in in}]))
1261+
[{:path path :pred (op-describe re) :val x :via via :in in}]))
12511262
(gen* [_ overrides path rmap]
12521263
(if gfn
12531264
(gfn)
@@ -1389,7 +1400,8 @@
13891400
(c/and (<= (inst-ms start) t) (< t (inst-ms end))))))
13901401

13911402
(defn int-in-range?
1392-
"Return true if start <= val and val < end"
1403+
"Return true if start <= val, val < end and val is a fixed
1404+
precision integer."
13931405
[start end val]
13941406
(cond
13951407
(integer? val) (c/and (<= start val) (< val end))

0 commit comments

Comments
 (0)