|
378 | 378 | (explain* pred path (if-let [name (spec-name pred)] (conj via name) via) in v) |
379 | 379 | [{:path path :pred form :val v :via via :in in}]))) |
380 | 380 |
|
| 381 | +(declare ^{:arglists '([s] [min-count s])} or-k-gen |
| 382 | + ^{:arglists '([s])} and-k-gen) |
| 383 | + |
| 384 | +(defn- k-gen |
| 385 | + "returns a generator for form f, which can be a keyword or a list |
| 386 | + starting with 'or or 'and." |
| 387 | + [f] |
| 388 | + (cond |
| 389 | + (keyword? f) (gen/return f) |
| 390 | + (= 'or (first f)) (or-k-gen 1 (rest f)) |
| 391 | + (= 'and (first f)) (and-k-gen (rest f)))) |
| 392 | + |
| 393 | +(defn- or-k-gen |
| 394 | + "returns a tuple generator made up of generators for a random subset |
| 395 | + of min-count (default 0) to all elements in s." |
| 396 | + ([s] (or-k-gen 0 s)) |
| 397 | + ([min-count s] |
| 398 | + (gen/bind (gen/tuple |
| 399 | + (gen/choose min-count (count s)) |
| 400 | + (gen/shuffle (map k-gen s))) |
| 401 | + (fn [[n gens]] |
| 402 | + (apply gen/tuple (take n gens)))))) |
| 403 | + |
| 404 | +(defn- and-k-gen |
| 405 | + "returns a tuple generator made up of generators for every element |
| 406 | + in s." |
| 407 | + [s] |
| 408 | + (apply gen/tuple (map k-gen s))) |
| 409 | + |
381 | 410 | (defn ^:skip-wiki map-spec-impl |
382 | 411 | "Do not call this directly, use 'spec' with a map argument" |
383 | 412 | [{:keys [req-un opt-un keys-pred pred-exprs opt-keys req-specs req req-keys opt-specs pred-forms opt gfn] |
|
438 | 467 | (if gfn |
439 | 468 | (gfn) |
440 | 469 | (let [rmap (inck rmap id) |
441 | | - gen (fn [k s] (gensub s overrides (conj path k) rmap k)) |
| 470 | + rgen (fn [k s] [k (gensub s overrides (conj path k) rmap k)]) |
442 | 471 | ogen (fn [k s] |
443 | 472 | (when-not (recur-limit? rmap id path k) |
444 | 473 | [k (gen/delay (gensub s overrides (conj path k) rmap k))])) |
445 | | - req-gens (map gen req-keys req-specs) |
446 | | - opt-gens (remove nil? (map ogen opt-keys opt-specs))] |
447 | | - (when (every? identity (concat req-gens opt-gens)) |
448 | | - (let [reqs (zipmap req-keys req-gens) |
449 | | - opts (into {} opt-gens)] |
450 | | - (gen/bind (gen/choose 0 (count opts)) |
451 | | - #(let [args (concat (seq reqs) (when (seq opts) (shuffle (seq opts))))] |
452 | | - (->> args |
453 | | - (take (c/+ % (count reqs))) |
454 | | - (apply concat) |
455 | | - (apply gen/hash-map))))))))) |
| 474 | + reqs (map rgen req-keys req-specs) |
| 475 | + opts (remove nil? (map ogen opt-keys opt-specs))] |
| 476 | + (when (every? identity (concat (map second reqs) (map second opts))) |
| 477 | + (gen/bind |
| 478 | + (gen/tuple |
| 479 | + (and-k-gen req) |
| 480 | + (or-k-gen opt) |
| 481 | + (and-k-gen req-un) |
| 482 | + (or-k-gen opt-un)) |
| 483 | + (fn [[req-ks opt-ks req-un-ks opt-un-ks]] |
| 484 | + (let [qks (flatten (concat req-ks opt-ks)) |
| 485 | + unqks (map (comp keyword name) (flatten (concat req-un-ks opt-un-ks)))] |
| 486 | + (->> (into reqs opts) |
| 487 | + (filter #((set (concat qks unqks)) (first %))) |
| 488 | + (apply concat) |
| 489 | + (apply gen/hash-map))))))))) |
456 | 490 | (with-gen* [_ gfn] (map-spec-impl (assoc argm :gfn gfn))) |
457 | 491 | (describe* [_] (cons `keys |
458 | 492 | (cond-> [] |
|
0 commit comments