(defn ^:skip-wiki tuple-impl
"Do not call this directly, use 'tuple'"
([forms preds] (tuple-impl forms preds nil))
([forms preds gfn]
(let [specs (delay (mapv specize preds forms))
cnt (count preds)]
(reify
Specize
(specize* [s] s)
(specize* [s _] s)
Spec
(conform* [_ x]
(let [specs @specs]
(if-not (c/and (vector? x)
(= (count x) cnt))
::invalid
(loop [ret x, i 0]
(if (= i cnt)
ret
(let [v (x i)
cv (conform* (specs i) v)]
(if (invalid? cv)
::invalid
(recur (if (identical? cv v) ret (assoc ret i cv))
(inc i)))))))))
(unform* [_ x]
(c/assert (c/and (vector? x)
(= (count x) (count preds))))
(loop [ret x, i 0]
(if (= i (count x))
ret
(let [cv (x i)
v (unform (preds i) cv)]
(recur (if (identical? cv v) ret (assoc ret i v))
(inc i))))))
(explain* [_ path via in x]
(cond
(not (vector? x))
[{:path path :pred 'vector? :val x :via via :in in}]
(not= (count x) (count preds))
[{:path path :pred `(= (count ~'%) ~(count preds)) :val x :via via :in in}]
:else
(apply concat
(map (fn [i form pred]
(let [v (x i)]
(when-not (pvalid? pred v)
(explain-1 form pred (conj path i) via (conj in i) v))))
(range (count preds)) forms preds))))
(gen* [_ overrides path rmap]
(if gfn
(gfn)
(let [gen (fn [i p f]
(gensub p overrides (conj path i) rmap f))
gs (map gen (range (count preds)) preds forms)]
(when (every? identity gs)
(apply gen/tuple gs)))))
(with-gen* [_ gfn] (tuple-impl forms preds gfn))
(describe* [_] `(tuple ~@forms))))))