diff --git a/src/com/rpl/specter.cljc b/src/com/rpl/specter.cljc index d10027b..1719e04 100644 --- a/src/com/rpl/specter.cljc +++ b/src/com/rpl/specter.cljc @@ -201,3 +201,9 @@ ([cond-fn if-path] (cond-path cond-fn if-path)) ([cond-fn if-path else-path] (cond-path cond-fn if-path nil else-path))) + +(defn multi-path + "A path that branches on multiple paths. For updates, + applies updates to the paths in order." + [& paths] + (i/->MultiPath (->> paths (map comp-paths*) doall))) diff --git a/src/com/rpl/specter/impl.cljc b/src/com/rpl/specter/impl.cljc index f1dffaa..f777f77 100644 --- a/src/com/rpl/specter/impl.cljc +++ b/src/com/rpl/specter/impl.cljc @@ -606,3 +606,21 @@ structure ))) +(deftype MultiPath [paths]) + +(extend-protocol p/StructurePath + MultiPath + (select* [this structure next-fn] + (->> (field this 'paths) + (mapcat #(compiled-select* % structure)) + (mapcat next-fn) + doall + )) + (transform* [this structure next-fn] + (reduce + (fn [structure selector] + (compiled-transform* selector next-fn structure)) + structure + (field this 'paths)) + )) + diff --git a/test/clj/com/rpl/specter/core_test.clj b/test/clj/com/rpl/specter/core_test.clj index 0fc9f54..00d857e 100644 --- a/test/clj/com/rpl/specter/core_test.clj +++ b/test/clj/com/rpl/specter/core_test.clj @@ -106,9 +106,11 @@ (defspec transform-filterer-all-equivalency (prop/for-all - [v (gen/vector gen/int)] - (let [v2 (transform [(filterer odd?) ALL] inc v) - v3 (transform [ALL odd?] inc v)] + [v (gen/vector gen/int) + pred (gen/elements [even? odd?]) + updater (gen/elements [inc dec])] + (let [v2 (transform [(filterer pred) ALL] updater v) + v3 (transform [ALL pred] updater v)] (= v2 v3)) )) @@ -133,12 +135,13 @@ (defspec transform-last-compound (for-all+ - [v (gen/such-that #(some odd? %) (gen/vector gen/int))] - (let [v2 (transform [(filterer odd?) LAST] inc v) + [pred (gen/elements [odd? even?]) + v (gen/such-that #(some pred %) (gen/vector gen/int))] + (let [v2 (transform [(filterer pred) LAST] inc v) differing-elems (differing-elements v v2)] (and (= (count v2) (count v)) (= (count differing-elems) 1) - (every? even? (drop (first differing-elems) v2)) + (every? (complement pred) (drop (first differing-elems) v2)) )))) ;; max sizes prevent too much data from being generated and keeps test from taking forever @@ -341,3 +344,20 @@ (= (select (if-path [k1 pred] k2 k3) m) (select k m)) )))) + +(defspec multi-path-test + (for-all+ + [k1 (max-size 3 gen/keyword) + k2 (max-size 3 gen/keyword) + m (max-size 5 + (gen-map-with-keys + gen/keyword + gen/int + k1 + k2)) + ] + (= (transform (multi-path k1 k2) inc m) + (->> m + (transform k1 inc) + (transform k2 inc))) + ))