diff --git a/CHANGES.md b/CHANGES.md index 9db70c3..919fc36 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,7 @@ +## 1.0.5-SNAPSHOT + +* Add `regex-nav` navigator for regexes, which navigate to every match in a string and support replacement with a new substring. + ## 1.0.4 * Add `indexed-vals` navigator, a variant of `INDEXED-VALS` that allows for a customized start index. diff --git a/src/clj/com/rpl/specter.cljc b/src/clj/com/rpl/specter.cljc index a571e88..a24871d 100644 --- a/src/clj/com/rpl/specter.cljc +++ b/src/clj/com/rpl/specter.cljc @@ -1068,6 +1068,13 @@ (swap! structure next-fn) structure))) +(defnav regex-nav [re] + (select* [this structure next-fn] + (doseqres NONE [s (re-seq re structure)] + (next-fn s))) + (transform* [this structure next-fn] + (clojure.string/replace structure re next-fn))) + (defdynamicnav selected? "Filters the current value based on whether a path finds anything. e.g. (selected? :vals ALL even?) keeps the current element only if an diff --git a/src/clj/com/rpl/specter/navs.cljc b/src/clj/com/rpl/specter/navs.cljc index 5cd904f..b9a9552 100644 --- a/src/clj/com/rpl/specter/navs.cljc +++ b/src/clj/com/rpl/specter/navs.cljc @@ -478,6 +478,7 @@ structure (updater structure next-fn)))) + (defn- update-first-list [l afn] (let [newf (afn (first l)) restl (rest l)] diff --git a/test/com/rpl/specter/core_test.cljc b/test/com/rpl/specter/core_test.cljc index 58c015c..6ab8dd6 100644 --- a/test/com/rpl/specter/core_test.cljc +++ b/test/com/rpl/specter/core_test.cljc @@ -1434,6 +1434,20 @@ (is (= "abq" (setval s/LAST "q" "abc"))) ) +(deftest regex-navigation-test + (is (= (select (s/regex-nav #"t") "test") ["t" "t"])) + (is (= (select [:a (s/regex-nav #"t")] {:a "test"}) ["t" "t"])) + (is (= (transform (s/regex-nav #"t") clojure.string/capitalize "test") "TesT")) + (is (= (transform [:a (s/regex-nav #"t")] clojure.string/capitalize {:a "test"}) {:a "TesT"})) + (is (= (transform (s/regex-nav #"\s+\w") clojure.string/triml "Hello World!") "HelloWorld!")) + (is (= (setval (s/regex-nav #"t") "z" "test") "zesz")) + (is (= (setval [:a (s/regex-nav #"t")] "z" {:a "test"}) {:a "zesz"})) + (is (= (transform (s/regex-nav #"aa*") (fn [s] (-> s count str)) "aadt") "2dt")) + (is (= (transform (s/regex-nav #"[Aa]+") (fn [s] (apply str (take (count s) (repeat "@")))) "Amsterdam Aardvarks") "@msterd@m @@rdv@rks")) + (is (= (select [(s/regex-nav #"(\S+):\ (\d+)") (s/nthpath 2)] "Mary: 1st George: 2nd Arthur: 3rd") ["1" "2" "3"])) + (is (= (transform (s/subselect (s/regex-nav #"\d\w+")) reverse "Mary: 1st George: 2nd Arthur: 3rd") "Mary: 3rd George: 2nd Arthur: 1st")) + ) + (deftest single-value-none-navigators-test (is (predand= vector? [1 2 3] (setval s/AFTER-ELEM 3 [1 2]))) (is (predand= list? '(1 2 3) (setval s/AFTER-ELEM 3 '(1 2)))) @@ -1459,10 +1473,10 @@ (defspec map-keys-all-first-equivalence-transform (for-all+ - [m (limit-size 10 (gen/map gen/int gen/keyword))] - (= (transform s/MAP-KEYS inc m) - (transform [s/ALL s/FIRST] inc m ) - ))) + [m (limit-size 10 (gen/map gen/int gen/keyword))] + (= (transform s/MAP-KEYS inc m) + (transform [s/ALL s/FIRST] inc m ) + ))) (defspec map-keys-all-first-equivalence-select (for-all+