Compare commits
No commits in common. "master" and "1.1.3" have entirely different histories.
17 changed files with 150 additions and 422 deletions
37
.github/workflows/ci.yml
vendored
37
.github/workflows/ci.yml
vendored
|
|
@ -1,37 +0,0 @@
|
||||||
name: CI
|
|
||||||
|
|
||||||
on: [push, pull_request]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
|
|
||||||
test:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
with:
|
|
||||||
fetch_depth: 0
|
|
||||||
|
|
||||||
- name: Setup Babashka
|
|
||||||
uses: turtlequeue/setup-babashka@v1.3.0
|
|
||||||
with:
|
|
||||||
babashka-version: 0.7.8
|
|
||||||
|
|
||||||
- name: Prepare java
|
|
||||||
uses: actions/setup-java@v2
|
|
||||||
with:
|
|
||||||
distribution: 'zulu'
|
|
||||||
java-version: '8'
|
|
||||||
|
|
||||||
- name: Install clojure tools
|
|
||||||
uses: DeLaGuardo/setup-clojure@4.0
|
|
||||||
with:
|
|
||||||
lein: 2.9.1
|
|
||||||
|
|
||||||
- name: Run clj tests
|
|
||||||
run: bb test:clj
|
|
||||||
|
|
||||||
- name: Run cljs tests
|
|
||||||
run: bb test:cljs
|
|
||||||
|
|
||||||
- name: Run babashka tests
|
|
||||||
run: bb test:bb
|
|
||||||
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -11,5 +11,3 @@ pom.xml.asc
|
||||||
.lein-failures
|
.lein-failures
|
||||||
.cljs_node_repl
|
.cljs_node_repl
|
||||||
out/
|
out/
|
||||||
.cpcache
|
|
||||||
.cache
|
|
||||||
|
|
|
||||||
8
.travis.yml
Normal file
8
.travis.yml
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
language: clojure
|
||||||
|
lein: 2.9.1
|
||||||
|
cache:
|
||||||
|
directories:
|
||||||
|
- $HOME/.m2
|
||||||
|
script:
|
||||||
|
- lein test
|
||||||
|
- lein doo node test-build once
|
||||||
|
|
@ -1,11 +1,4 @@
|
||||||
## 1.1.4
|
## 1.1.3
|
||||||
|
|
||||||
* Add arglist metadata to navs (thanks @phronmophobic)
|
|
||||||
* Improve before-index performance by 150x on lists and 5x on vectors (thanks @jeff303)
|
|
||||||
* Bug fix: BEFORE-ELEM, AFTER-ELEM, FIRST, LAST, BEGINNING, and END on subvecs now produce vector type in cljs
|
|
||||||
* Add compatibility with [babashka](https://babashka.org/)
|
|
||||||
|
|
||||||
## 1.1.3 - 2019-10-13
|
|
||||||
|
|
||||||
* Better AOT behavior: path functions for inline caching and protpath extensions no longer write eval'd class files. You can force path functions to not override `*compile-files*` by binding `com.rpl.specter.impl/*path-compile-files*` to `true`.
|
* Better AOT behavior: path functions for inline caching and protpath extensions no longer write eval'd class files. You can force path functions to not override `*compile-files*` by binding `com.rpl.specter.impl/*path-compile-files*` to `true`.
|
||||||
* Bug fix: fix throw-illegal in cljs
|
* Bug fix: fix throw-illegal in cljs
|
||||||
|
|
|
||||||
15
DEVELOPER.md
15
DEVELOPER.md
|
|
@ -1,7 +1,7 @@
|
||||||
# Running Clojure tests
|
# Running Clojure tests
|
||||||
|
|
||||||
```
|
```
|
||||||
lein do clean, test
|
lein cleantest
|
||||||
```
|
```
|
||||||
|
|
||||||
# Running ClojureScript tests
|
# Running ClojureScript tests
|
||||||
|
|
@ -10,16 +10,3 @@ lein do clean, test
|
||||||
lein javac
|
lein javac
|
||||||
lein doo node test-build once
|
lein doo node test-build once
|
||||||
```
|
```
|
||||||
|
|
||||||
# Running benchmarks
|
|
||||||
## All benchmarks
|
|
||||||
```
|
|
||||||
scripts/run-benchmarks
|
|
||||||
```
|
|
||||||
## Individual benchmark(s)
|
|
||||||
Specify the benchmark names as command line args. They will likely each need quoted because they contain spaces.
|
|
||||||
Order is ignored.
|
|
||||||
```
|
|
||||||
scripts/run-benchmarks "prepend to a vector" "filter a sequence"
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
|
||||||
20
README.md
20
README.md
|
|
@ -1,4 +1,4 @@
|
||||||
# Specter
|
# Specter [](http://travis-ci.org/redplanetlabs/specter)
|
||||||
|
|
||||||
Specter rejects Clojure's restrictive approach to immutable data structure manipulation, instead exposing an elegant API to allow any sort of manipulation imaginable. Specter especially excels at querying and transforming nested and recursive data, important use cases that are very complex to handle with vanilla Clojure.
|
Specter rejects Clojure's restrictive approach to immutable data structure manipulation, instead exposing an elegant API to allow any sort of manipulation imaginable. Specter especially excels at querying and transforming nested and recursive data, important use cases that are very complex to handle with vanilla Clojure.
|
||||||
|
|
||||||
|
|
@ -322,22 +322,6 @@ Specter supports ClojureScript! However, some of the differences between Clojure
|
||||||
# Future work
|
# Future work
|
||||||
- Integrate Specter with other kinds of data structures, such as graphs. Desired navigations include: reduction in topological order, navigate to outgoing/incoming nodes, to a subgraph (with metadata indicating how to attach external edges on transformation), to node attributes, to node values, to specific nodes.
|
- Integrate Specter with other kinds of data structures, such as graphs. Desired navigations include: reduction in topological order, navigate to outgoing/incoming nodes, to a subgraph (with metadata indicating how to attach external edges on transformation), to node attributes, to node values, to specific nodes.
|
||||||
|
|
||||||
# clj-kondo
|
|
||||||
|
|
||||||
When using Specter in a project with [clj-kondo](https://github.com/clj-kondo/clj-kondo), a lot of the vars will be considered unresolved because internally Specter defines them with macros. The following configuration snippet will resolve these issues if you include it in your `.clj-kondo/config.edn` file.
|
|
||||||
|
|
||||||
```clojure
|
|
||||||
{:lint-as {com.rpl.specter/defcollector clojure.core/defn
|
|
||||||
com.rpl.specter/defdynamicnav clojure.core/defn
|
|
||||||
com.rpl.specter/defmacroalias clojure.core/def
|
|
||||||
com.rpl.specter/defnav clojure.core/defn
|
|
||||||
com.rpl.specter/defrichnav clojure.core/defn}}
|
|
||||||
```
|
|
||||||
|
|
||||||
# Babashka
|
|
||||||
|
|
||||||
This library is compatible with [babashka](https://babashka.org/) as of specter 1.1.4 and babashka 0.7.8.
|
|
||||||
|
|
||||||
# License
|
# License
|
||||||
|
|
||||||
Copyright 2015-2020 Red Planet Labs, Inc. Specter is licensed under Apache License v2.0.
|
Copyright 2015-2019 Red Planet Labs, Inc. Specter is licensed under Apache License v2.0.
|
||||||
|
|
|
||||||
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
||||||
1.1.5-SNAPSHOT
|
1.1.3
|
||||||
|
|
|
||||||
19
bb.edn
19
bb.edn
|
|
@ -1,19 +0,0 @@
|
||||||
{:paths ["src/clj"]
|
|
||||||
:tasks
|
|
||||||
{test:clj {:doc "Run clj tests with leiningen"
|
|
||||||
:task (shell "lein test")}
|
|
||||||
|
|
||||||
test:cljs {:doc "Run cljs tests with leiningen"
|
|
||||||
:task (shell "lein doo node test-build once")}
|
|
||||||
|
|
||||||
test:bb {:doc "Run bb tests"
|
|
||||||
:extra-paths ["test"]
|
|
||||||
:extra-deps {org.clojure/test.check {:mvn/version "0.9.0"}
|
|
||||||
io.github.cognitect-labs/test-runner
|
|
||||||
{:git/tag "v0.5.0" :git/sha "b3fd0d2"}
|
|
||||||
org.clojure/tools.namespace {:git/url "https://github.com/babashka/tools.namespace"
|
|
||||||
:git/sha "3625153ee66dfcec2ba600851b5b2cbdab8fae6c"}}
|
|
||||||
:requires ([cognitect.test-runner :as tr])
|
|
||||||
:task (apply tr/-main
|
|
||||||
"-d" "test"
|
|
||||||
*command-line-args*)}}}
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
:test-paths ["test", "target/test-classes"]
|
:test-paths ["test", "target/test-classes"]
|
||||||
:auto-clean false
|
:auto-clean false
|
||||||
:dependencies [[riddley "0.1.12"]]
|
:dependencies [[riddley "0.1.12"]]
|
||||||
:plugins [[lein-codox "0.10.7"]
|
:plugins [[lein-codox "0.9.5"]
|
||||||
[lein-doo "0.1.7"]]
|
[lein-doo "0.1.7"]]
|
||||||
:codox {:source-paths ["target/classes" "src/clj"]
|
:codox {:source-paths ["target/classes" "src/clj"]
|
||||||
:namespaces [com.rpl.specter
|
:namespaces [com.rpl.specter
|
||||||
|
|
@ -36,8 +36,4 @@
|
||||||
[criterium "0.4.4"]]}
|
[criterium "0.4.4"]]}
|
||||||
:test {:dependencies [[org.clojure/clojure "1.7.0"]]}}
|
:test {:dependencies [[org.clojure/clojure "1.7.0"]]}}
|
||||||
|
|
||||||
:deploy-repositories
|
|
||||||
[["clojars" {:url "https://repo.clojars.org"
|
|
||||||
:sign-releases false}]]
|
|
||||||
|
|
||||||
:aliases {"deploy" ["do" "clean," "deploy" "clojars"]})
|
:aliases {"deploy" ["do" "clean," "deploy" "clojars"]})
|
||||||
|
|
|
||||||
|
|
@ -19,14 +19,11 @@
|
||||||
(println (pretty-float3 t) "\t\t" (pretty-float3 (/ t best-time 1.0)) "\t\t" k))))
|
(println (pretty-float3 t) "\t\t" (pretty-float3 (/ t best-time 1.0)) "\t\t" k))))
|
||||||
|
|
||||||
(defmacro run-benchmark [name & exprs]
|
(defmacro run-benchmark [name & exprs]
|
||||||
(let [only-benchmarks (set (filter some? *command-line-args*))
|
(let [afn-map (->> exprs shuffle (map (fn [e] [`(quote ~e) `(fn [] ~e)])) (into {}))]
|
||||||
all-benchmarks? (empty? only-benchmarks)]
|
`(do
|
||||||
(if (or all-benchmarks? (contains? only-benchmarks name))
|
(println "Benchmark:" ~name)
|
||||||
(let [afn-map (->> exprs shuffle (map (fn [e] [`(quote ~e) `(fn [] ~e)])) (into {}))]
|
(compare-benchmark ~afn-map)
|
||||||
`(do
|
(println "\n********************************\n"))))
|
||||||
(println "Benchmark:" ~name)
|
|
||||||
(compare-benchmark ~afn-map)
|
|
||||||
(println "\n********************************\n"))))))
|
|
||||||
|
|
||||||
(defn specter-dynamic-nested-get [data a b c]
|
(defn specter-dynamic-nested-get [data a b c]
|
||||||
(select-any (keypath a b c) data))
|
(select-any (keypath a b c) data))
|
||||||
|
|
@ -338,20 +335,3 @@
|
||||||
(transform (walker number?) inc data)
|
(transform (walker number?) inc data)
|
||||||
(transform (walker-old number?) inc data)
|
(transform (walker-old number?) inc data)
|
||||||
))
|
))
|
||||||
|
|
||||||
(let [size 1000
|
|
||||||
middle-idx (/ size 2)
|
|
||||||
v -1
|
|
||||||
rng (range size)
|
|
||||||
data-vec (vec rng)
|
|
||||||
data-lst (apply list rng)]
|
|
||||||
(run-benchmark "before-index vs. srange in middle (vector)"
|
|
||||||
(setval (before-index middle-idx) v data-vec)
|
|
||||||
(setval (srange middle-idx middle-idx) [v] data-vec))
|
|
||||||
(run-benchmark "before-index vs. srange in middle (list)"
|
|
||||||
(setval (before-index middle-idx) v data-lst)
|
|
||||||
(setval (srange middle-idx middle-idx) [v] data-lst))
|
|
||||||
(run-benchmark "before-index at 0 vs. srange vs. cons (list)"
|
|
||||||
(setval (before-index 0) v data-lst)
|
|
||||||
(setval (srange 0 0) [v] data-lst)
|
|
||||||
(cons v data-lst)))
|
|
||||||
|
|
|
||||||
|
|
@ -5,4 +5,4 @@ lein version
|
||||||
echo
|
echo
|
||||||
lein show-profiles bench
|
lein show-profiles bench
|
||||||
echo
|
echo
|
||||||
java -server -XX:MaxInlineSize=100 -cp "$(lein with-profile bench classpath)" clojure.main scripts/benchmarks.clj "$@"
|
java -server -XX:MaxInlineSize=100 -cp "$(lein with-profile bench classpath)" clojure.main scripts/benchmarks.clj
|
||||||
|
|
|
||||||
|
|
@ -115,32 +115,7 @@
|
||||||
(providepath ~self-sym ~path)
|
(providepath ~self-sym ~path)
|
||||||
~self-sym)))))
|
~self-sym)))))
|
||||||
|
|
||||||
;; copied from clojure.core
|
;; copied from tools.macro to avoid the dependency
|
||||||
(def
|
|
||||||
^{:private true}
|
|
||||||
sigs
|
|
||||||
(fn [fdecl]
|
|
||||||
(let [asig
|
|
||||||
(fn [fdecl]
|
|
||||||
(let [arglist (first fdecl)
|
|
||||||
;; elide implicit macro args
|
|
||||||
arglist (if (= '&form (first arglist))
|
|
||||||
(subvec arglist 2 (count arglist))
|
|
||||||
arglist)
|
|
||||||
body (next fdecl)]
|
|
||||||
(if (map? (first body))
|
|
||||||
(if (next body)
|
|
||||||
(with-meta arglist (conj (if (meta arglist) (meta arglist) {}) (first body)))
|
|
||||||
arglist)
|
|
||||||
arglist)))]
|
|
||||||
(if (seq? (first fdecl))
|
|
||||||
(loop [ret [] fdecls fdecl]
|
|
||||||
(if fdecls
|
|
||||||
(recur (conj ret (asig (first fdecls))) (next fdecls))
|
|
||||||
(seq ret)))
|
|
||||||
(list (asig fdecl))))))
|
|
||||||
|
|
||||||
;; partially copied from clojure.core/defn
|
|
||||||
(defn- name-with-attributes
|
(defn- name-with-attributes
|
||||||
"To be used in macro definitions.
|
"To be used in macro definitions.
|
||||||
Handles optional docstrings and attribute maps for a name to be defined
|
Handles optional docstrings and attribute maps for a name to be defined
|
||||||
|
|
@ -151,31 +126,20 @@
|
||||||
macro argument list. The return value is a vector containing the name
|
macro argument list. The return value is a vector containing the name
|
||||||
with its extended metadata map and the list of unprocessed macro
|
with its extended metadata map and the list of unprocessed macro
|
||||||
arguments."
|
arguments."
|
||||||
[name fdecl]
|
[name macro-args]
|
||||||
(let [m (if (string? (first fdecl))
|
(let [[docstring macro-args] (if (string? (first macro-args))
|
||||||
{:doc (first fdecl)}
|
[(first macro-args) (next macro-args)]
|
||||||
{})
|
[nil macro-args])
|
||||||
|
[attr macro-args] (if (map? (first macro-args))
|
||||||
fdecl (if (string? (first fdecl))
|
[(first macro-args) (next macro-args)]
|
||||||
(next fdecl)
|
[{} macro-args])
|
||||||
fdecl)
|
attr (if docstring
|
||||||
m (if (map? (first fdecl))
|
(assoc attr :doc docstring)
|
||||||
(conj m (first fdecl))
|
attr)
|
||||||
m)
|
attr (if (meta name)
|
||||||
fdecl (if (map? (first fdecl))
|
(conj (meta name) attr)
|
||||||
(next fdecl)
|
attr)]
|
||||||
fdecl)
|
[(with-meta name attr) macro-args]))
|
||||||
fdecl (if (vector? (first fdecl))
|
|
||||||
(list fdecl)
|
|
||||||
fdecl)
|
|
||||||
m (if (map? (last fdecl))
|
|
||||||
(conj m (last fdecl))
|
|
||||||
m)
|
|
||||||
fdecl (if (map? (last fdecl))
|
|
||||||
(butlast fdecl)
|
|
||||||
fdecl)
|
|
||||||
m (conj {:arglists (list 'quote (sigs fdecl))} m)]
|
|
||||||
[(with-meta name m) fdecl]))
|
|
||||||
|
|
||||||
(defmacro dynamicnav [& args]
|
(defmacro dynamicnav [& args]
|
||||||
`(vary-meta (wrap-dynamic-nav (fn ~@args)) assoc :dynamicnav true))
|
`(vary-meta (wrap-dynamic-nav (fn ~@args)) assoc :dynamicnav true))
|
||||||
|
|
@ -1010,10 +974,11 @@
|
||||||
NONE)
|
NONE)
|
||||||
(transform* [this vals structure next-fn]
|
(transform* [this vals structure next-fn]
|
||||||
(let [v (next-fn vals NONE)]
|
(let [v (next-fn vals NONE)]
|
||||||
(if
|
(if (identical? NONE v)
|
||||||
(identical? NONE v)
|
structure
|
||||||
structure
|
;; TODO: make a more efficient impl
|
||||||
(n/insert-before-idx structure index v)))))
|
(setval (srange index index) [v] structure)
|
||||||
|
))))
|
||||||
|
|
||||||
(defrichnav
|
(defrichnav
|
||||||
^{:doc "Navigates to the index of the sequence if within 0 and size. Transforms move element
|
^{:doc "Navigates to the index of the sequence if within 0 and size. Transforms move element
|
||||||
|
|
@ -1308,7 +1273,7 @@
|
||||||
ns (namespace structure)]
|
ns (namespace structure)]
|
||||||
(cond (keyword? structure) (keyword ns new-name)
|
(cond (keyword? structure) (keyword ns new-name)
|
||||||
(symbol? structure) (symbol ns new-name)
|
(symbol? structure) (symbol ns new-name)
|
||||||
:else (throw (ex-info "NAME can only be used on symbols or keywords" {:structure structure}))
|
:else (i/throw-illegal "NAME can only be used on symbols or keywords - " structure)
|
||||||
))))
|
))))
|
||||||
|
|
||||||
(defnav ^{:doc "Navigates to the namespace portion of the keyword or symbol"}
|
(defnav ^{:doc "Navigates to the namespace portion of the keyword or symbol"}
|
||||||
|
|
@ -1321,8 +1286,7 @@
|
||||||
new-ns (next-fn (namespace structure))]
|
new-ns (next-fn (namespace structure))]
|
||||||
(cond (keyword? structure) (keyword new-ns name)
|
(cond (keyword? structure) (keyword new-ns name)
|
||||||
(symbol? structure) (symbol new-ns name)
|
(symbol? structure) (symbol new-ns name)
|
||||||
:else (throw (ex-info "NAMESPACE can only be used on symbols or keywords"
|
:else (i/throw-illegal "NAMESPACE can only be used on symbols or keywords - " structure)
|
||||||
{:structure structure}))
|
|
||||||
))))
|
))))
|
||||||
|
|
||||||
(defdynamicnav
|
(defdynamicnav
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
#?(:cljs (:require-macros
|
#?(:cljs (:require-macros
|
||||||
[com.rpl.specter.util-macros
|
[com.rpl.specter.util-macros
|
||||||
:refer [doseqres mk-comp-navs mk-late-fn mk-late-fn-records]]
|
:refer [doseqres mk-comp-navs mk-late-fn mk-late-fn-records]]
|
||||||
))
|
[com.rpl.specter.impl :refer [throw-illegal]]))
|
||||||
;; workaround for cljs bug that emits warnings for vars named the same as a
|
;; workaround for cljs bug that emits warnings for vars named the same as a
|
||||||
;; private var in cljs.core (in this case `NONE`, added as private var to
|
;; private var in cljs.core (in this case `NONE`, added as private var to
|
||||||
;; cljs.core with 1.9.562)
|
;; cljs.core with 1.9.562)
|
||||||
|
|
@ -15,11 +15,10 @@
|
||||||
#?(:clj [clojure.pprint :as pp])
|
#?(:clj [clojure.pprint :as pp])
|
||||||
[clojure.string :as s]
|
[clojure.string :as s]
|
||||||
[clojure.walk :as walk]
|
[clojure.walk :as walk]
|
||||||
#?(:bb [clojure.walk :as riddley]
|
#?(:clj [riddley.walk :as riddley]))
|
||||||
:clj [riddley.walk :as riddley]))
|
|
||||||
|
#?(:clj (:import [com.rpl.specter Util MutableCell])))
|
||||||
|
|
||||||
#?@(:bb []
|
|
||||||
:clj [(:import [com.rpl.specter Util MutableCell])]))
|
|
||||||
|
|
||||||
(def NONE ::NONE)
|
(def NONE ::NONE)
|
||||||
|
|
||||||
|
|
@ -51,6 +50,24 @@
|
||||||
([a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 & r] v)))
|
([a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 & r] v)))
|
||||||
|
|
||||||
|
|
||||||
|
#?(:clj
|
||||||
|
(defmacro throw* [etype & args]
|
||||||
|
`(throw (new ~etype (smart-str ~@args)))))
|
||||||
|
|
||||||
|
#?(
|
||||||
|
:clj
|
||||||
|
(defmacro throw-illegal [& args]
|
||||||
|
(let [platform (if (contains? &env :locals) :cljs :clj)]
|
||||||
|
(if (= platform :clj)
|
||||||
|
`(throw* IllegalArgumentException ~@args)
|
||||||
|
`(com.rpl.specter.impl/throw-illegal* ~@args)
|
||||||
|
)))
|
||||||
|
|
||||||
|
:cljs
|
||||||
|
(defn throw-illegal* [& args]
|
||||||
|
(throw (js/Error. (apply str args)))))
|
||||||
|
|
||||||
|
|
||||||
;; need to get the expansion function like this so that
|
;; need to get the expansion function like this so that
|
||||||
;; this code compiles in a clojure environment where cljs.analyzer
|
;; this code compiles in a clojure environment where cljs.analyzer
|
||||||
;; namespace does not exist
|
;; namespace does not exist
|
||||||
|
|
@ -72,7 +89,7 @@
|
||||||
|
|
||||||
:cljs
|
:cljs
|
||||||
(defn clj-macroexpand-all [form]
|
(defn clj-macroexpand-all [form]
|
||||||
(throw (ex-info "not implemented" {}))))
|
(throw-illegal "not implemented")))
|
||||||
|
|
||||||
|
|
||||||
#?(
|
#?(
|
||||||
|
|
@ -81,11 +98,9 @@
|
||||||
|
|
||||||
:cljs
|
:cljs
|
||||||
(defn intern* [ns name val]
|
(defn intern* [ns name val]
|
||||||
(throw (ex-info "intern not supported in ClojureScript" {}))))
|
(throw-illegal "intern not supported in ClojureScript")))
|
||||||
|
|
||||||
#?(:bb
|
#?(
|
||||||
(defmacro fast-object-array [i]
|
|
||||||
`(object-array ~i))
|
|
||||||
:clj
|
:clj
|
||||||
(defmacro fast-object-array [i]
|
(defmacro fast-object-array [i]
|
||||||
`(com.rpl.specter.Util/makeObjectArray ~i)))
|
`(com.rpl.specter.Util/makeObjectArray ~i)))
|
||||||
|
|
@ -104,8 +119,7 @@
|
||||||
(if (= platform :cljs)
|
(if (= platform :cljs)
|
||||||
`(p/select* ~this ~@args)
|
`(p/select* ~this ~@args)
|
||||||
`(let [~hinted ~this]
|
`(let [~hinted ~this]
|
||||||
(#?(:bb p/select*
|
(.select* ~hinted ~@args)))))
|
||||||
:clj .select*) ~hinted ~@args)))))
|
|
||||||
:cljs
|
:cljs
|
||||||
(defn exec-select* [this vals structure next-fn]
|
(defn exec-select* [this vals structure next-fn]
|
||||||
(p/select* ^not-native this vals structure next-fn)))
|
(p/select* ^not-native this vals structure next-fn)))
|
||||||
|
|
@ -119,8 +133,7 @@
|
||||||
(if (= platform :cljs)
|
(if (= platform :cljs)
|
||||||
`(p/transform* ~this ~@args)
|
`(p/transform* ~this ~@args)
|
||||||
`(let [~hinted ~this]
|
`(let [~hinted ~this]
|
||||||
(#?(:bb p/transform*
|
(.transform* ~hinted ~@args)))))
|
||||||
:clj .transform*) ~hinted ~@args)))))
|
|
||||||
|
|
||||||
:cljs
|
:cljs
|
||||||
(defn exec-transform* [this vals structure next-fn]
|
(defn exec-transform* [this vals structure next-fn]
|
||||||
|
|
@ -139,9 +152,7 @@
|
||||||
(defn- coerce-object [this]
|
(defn- coerce-object [this]
|
||||||
(cond (rich-nav? this) this
|
(cond (rich-nav? this) this
|
||||||
(satisfies? p/ImplicitNav this) (p/implicit-nav this)
|
(satisfies? p/ImplicitNav this) (p/implicit-nav this)
|
||||||
:else (throw (ex-info "Not a navigator"
|
:else (throw-illegal "Not a navigator: " this " " (pr-str (type this)))))
|
||||||
{:this this
|
|
||||||
:type-str (pr-str (type this))}))))
|
|
||||||
|
|
||||||
|
|
||||||
(defprotocol CoercePath
|
(defprotocol CoercePath
|
||||||
|
|
@ -219,19 +230,13 @@
|
||||||
(set_cell [cell x])))
|
(set_cell [cell x])))
|
||||||
|
|
||||||
|
|
||||||
#?(:bb
|
#?(:cljs
|
||||||
(defrecord MutableCell [x])
|
|
||||||
:cljs
|
|
||||||
(deftype MutableCell [^:volatile-mutable q]
|
(deftype MutableCell [^:volatile-mutable q]
|
||||||
PMutableCell
|
PMutableCell
|
||||||
(set_cell [this x] (set! q x))))
|
(set_cell [this x] (set! q x))))
|
||||||
|
|
||||||
|
|
||||||
#?(:bb
|
#?(
|
||||||
(defn mutable-cell
|
|
||||||
([] (mutable-cell nil))
|
|
||||||
([v] (MutableCell. (volatile! v))))
|
|
||||||
|
|
||||||
:clj
|
:clj
|
||||||
(defn mutable-cell
|
(defn mutable-cell
|
||||||
([] (mutable-cell nil))
|
([] (mutable-cell nil))
|
||||||
|
|
@ -243,10 +248,7 @@
|
||||||
([init] (MutableCell. init))))
|
([init] (MutableCell. init))))
|
||||||
|
|
||||||
|
|
||||||
#?(:bb
|
#?(
|
||||||
(defn set-cell! [^MutableCell c v]
|
|
||||||
(vreset! (:x c) v))
|
|
||||||
|
|
||||||
:clj
|
:clj
|
||||||
(defn set-cell! [^MutableCell c v]
|
(defn set-cell! [^MutableCell c v]
|
||||||
(.set c v))
|
(.set c v))
|
||||||
|
|
@ -256,10 +258,7 @@
|
||||||
(set_cell cell val)))
|
(set_cell cell val)))
|
||||||
|
|
||||||
|
|
||||||
#?(:bb
|
#?(
|
||||||
(defn get-cell [^MutableCell c]
|
|
||||||
@(:x c))
|
|
||||||
|
|
||||||
:clj
|
:clj
|
||||||
(defn get-cell [^MutableCell c]
|
(defn get-cell [^MutableCell c]
|
||||||
(.get c))
|
(.get c))
|
||||||
|
|
@ -307,10 +306,8 @@
|
||||||
(defn do-compiled-traverse* [apath structure]
|
(defn do-compiled-traverse* [apath structure]
|
||||||
(reify #?(:clj clojure.lang.IReduce :cljs cljs.core/IReduce)
|
(reify #?(:clj clojure.lang.IReduce :cljs cljs.core/IReduce)
|
||||||
(#?(:clj reduce :cljs -reduce)
|
(#?(:clj reduce :cljs -reduce)
|
||||||
[this afn]
|
[this afn]
|
||||||
#?(:bb (reduce afn (afn) this)
|
(#?(:clj .reduce :cljs -reduce) this afn (afn)))
|
||||||
:default
|
|
||||||
(#?(:clj .reduce :cljs -reduce) this afn (afn))))
|
|
||||||
(#?(:clj reduce :cljs -reduce)
|
(#?(:clj reduce :cljs -reduce)
|
||||||
[this afn start]
|
[this afn start]
|
||||||
(let [cell (mutable-cell start)]
|
(let [cell (mutable-cell start)]
|
||||||
|
|
@ -327,9 +324,6 @@
|
||||||
))))
|
))))
|
||||||
|
|
||||||
#?(
|
#?(
|
||||||
:bb
|
|
||||||
(defn- call-reduce-interface [^clojure.lang.IReduce traverser afn start]
|
|
||||||
(reduce afn start traverser))
|
|
||||||
:clj
|
:clj
|
||||||
(defn- call-reduce-interface [^clojure.lang.IReduce traverser afn start]
|
(defn- call-reduce-interface [^clojure.lang.IReduce traverser afn start]
|
||||||
(.reduce traverser afn start)
|
(.reduce traverser afn start)
|
||||||
|
|
@ -344,10 +338,8 @@
|
||||||
(let [traverser (do-compiled-traverse* apath structure)]
|
(let [traverser (do-compiled-traverse* apath structure)]
|
||||||
(reify #?(:clj clojure.lang.IReduce :cljs cljs.core/IReduce)
|
(reify #?(:clj clojure.lang.IReduce :cljs cljs.core/IReduce)
|
||||||
(#?(:clj reduce :cljs -reduce)
|
(#?(:clj reduce :cljs -reduce)
|
||||||
[this afn]
|
[this afn]
|
||||||
#?(:bb (reduce afn (afn) this)
|
(#?(:clj .reduce :cljs -reduce) this afn (afn)))
|
||||||
:default
|
|
||||||
(#?(:clj .reduce :cljs -reduce) this afn (afn))))
|
|
||||||
(#?(:clj reduce :cljs -reduce)
|
(#?(:clj reduce :cljs -reduce)
|
||||||
[this afn start]
|
[this afn start]
|
||||||
(let [res (call-reduce-interface traverser afn start)]
|
(let [res (call-reduce-interface traverser afn start)]
|
||||||
|
|
@ -384,8 +376,7 @@
|
||||||
(let [curr (get-cell res)]
|
(let [curr (get-cell res)]
|
||||||
(if (identical? curr NONE)
|
(if (identical? curr NONE)
|
||||||
(set-cell! res structure)
|
(set-cell! res structure)
|
||||||
(throw (ex-info "More than one element found in structure"
|
(throw-illegal "More than one element found in structure: " structure))))]
|
||||||
{:structure structure})))))]
|
|
||||||
|
|
||||||
(compiled-traverse* path result-fn structure)
|
(compiled-traverse* path result-fn structure)
|
||||||
(let [ret (get-cell res)]
|
(let [ret (get-cell res)]
|
||||||
|
|
@ -400,12 +391,11 @@
|
||||||
(let [curr (get-cell res)]
|
(let [curr (get-cell res)]
|
||||||
(if (identical? curr NONE)
|
(if (identical? curr NONE)
|
||||||
(set-cell! res structure)
|
(set-cell! res structure)
|
||||||
(throw (ex-info "More than one element found in structure"
|
(throw-illegal "More than one element found in structure: " structure))))]
|
||||||
{:structure structure})))))]
|
|
||||||
(compiled-traverse* path result-fn structure)
|
(compiled-traverse* path result-fn structure)
|
||||||
(let [ret (get-cell res)]
|
(let [ret (get-cell res)]
|
||||||
(if (identical? NONE ret)
|
(if (identical? NONE ret)
|
||||||
(throw (ex-info "Found no elements for select-one!" {:structure structure})))
|
(throw-illegal "Found no elements for select-one! on " structure))
|
||||||
ret)))
|
ret)))
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -823,8 +813,7 @@
|
||||||
(defn dynamic-val-code [code possible-params]
|
(defn dynamic-val-code [code possible-params]
|
||||||
(let [[i] (keep-indexed (fn [i v] (if (= v code) i)) possible-params)]
|
(let [[i] (keep-indexed (fn [i v] (if (= v code) i)) possible-params)]
|
||||||
(if (nil? i)
|
(if (nil? i)
|
||||||
(throw (ex-info "Could not find code in possible params"
|
(throw-illegal "Could not find " code " in possible params " possible-params))
|
||||||
{:code code :possible-params possible-params})))
|
|
||||||
(maybe-direct-nav
|
(maybe-direct-nav
|
||||||
(->LocalParam i)
|
(->LocalParam i)
|
||||||
(direct-nav? code)))))
|
(direct-nav? code)))))
|
||||||
|
|
@ -996,9 +985,9 @@
|
||||||
|
|
||||||
|
|
||||||
(defn- multi-transform-error-fn [& nav]
|
(defn- multi-transform-error-fn [& nav]
|
||||||
(throw
|
(throw-illegal
|
||||||
(ex-info "All navigation in multi-transform must end in 'terminal' navigators"
|
"All navigation in multi-transform must end in 'terminal' "
|
||||||
{:nav nav})))
|
"navigators. Instead navigated to: " nav))
|
||||||
|
|
||||||
(defn compiled-multi-transform* [path structure]
|
(defn compiled-multi-transform* [path structure]
|
||||||
(compiled-transform* path multi-transform-error-fn structure))
|
(compiled-transform* path multi-transform-error-fn structure))
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@
|
||||||
(defn- determine-params-impls [impls]
|
(defn- determine-params-impls [impls]
|
||||||
(let [grouped (->> impls (map (fn [[n & body]] [n body])) (into {}))]
|
(let [grouped (->> impls (map (fn [[n & body]] [n body])) (into {}))]
|
||||||
(if-not (= #{'select* 'transform*} (-> grouped keys set))
|
(if-not (= #{'select* 'transform*} (-> grouped keys set))
|
||||||
(throw (ex-info "defnav must implement select* and transform*"
|
(i/throw-illegal "defnav must implement select* and transform*, instead got "
|
||||||
{:methods (keys grouped)})))
|
(keys grouped)))
|
||||||
grouped))
|
grouped))
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -39,16 +39,11 @@
|
||||||
(let [helpers (for [[mname [_ & mparams] & mbody] impls]
|
(let [helpers (for [[mname [_ & mparams] & mbody] impls]
|
||||||
`(defn ~(helper-name name mname) [~@params ~@mparams] ~@mbody))
|
`(defn ~(helper-name name mname) [~@params ~@mparams] ~@mbody))
|
||||||
decls (for [[mname & _] impls]
|
decls (for [[mname & _] impls]
|
||||||
`(declare ~(helper-name name mname)))
|
`(declare ~(helper-name name mname)))]
|
||||||
name-with-meta (vary-meta name
|
|
||||||
assoc :arglists (list 'quote (list params)))]
|
|
||||||
`(do
|
`(do
|
||||||
~@decls
|
~@decls
|
||||||
~@helpers
|
~@helpers
|
||||||
(def ~name-with-meta (nav ~params ~@impls)))))
|
(def ~name (nav ~params ~@impls)))))
|
||||||
|
|
||||||
(defmacro defrichnav [name params & impls]
|
(defmacro defrichnav [name params & impls]
|
||||||
(let [name-with-meta (vary-meta name
|
`(def ~name (richnav ~params ~@impls)))
|
||||||
assoc :arglists (list 'quote (list params)))]
|
|
||||||
`(def ~name-with-meta
|
|
||||||
(richnav ~params ~@impls))))
|
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,7 @@
|
||||||
#?(:clj (:use [com.rpl.specter.macros :only [defnav defrichnav]]
|
#?(:clj (:use [com.rpl.specter.macros :only [defnav defrichnav]]
|
||||||
[com.rpl.specter.util-macros :only [doseqres]]))
|
[com.rpl.specter.util-macros :only [doseqres]]))
|
||||||
(:require [com.rpl.specter.impl :as i]
|
(:require [com.rpl.specter.impl :as i]
|
||||||
#?@(:bb []
|
#?(:clj [clojure.core.reducers :as r])))
|
||||||
:clj [[clojure.core.reducers :as r]])))
|
|
||||||
|
|
||||||
|
|
||||||
(defn not-selected?*
|
(defn not-selected?*
|
||||||
|
|
@ -95,13 +94,6 @@
|
||||||
(comp (map next-fn)
|
(comp (map next-fn)
|
||||||
(filter not-NONE?))
|
(filter not-NONE?))
|
||||||
structure))
|
structure))
|
||||||
|
|
||||||
#?(:clj String :cljs string)
|
|
||||||
(all-transform [structure next-fn]
|
|
||||||
(apply str (into []
|
|
||||||
(comp (map next-fn)
|
|
||||||
(filter not-NONE?))
|
|
||||||
structure)))
|
|
||||||
|
|
||||||
#?(:clj clojure.lang.PersistentHashSet :cljs cljs.core/PersistentHashSet)
|
#?(:clj clojure.lang.PersistentHashSet :cljs cljs.core/PersistentHashSet)
|
||||||
(all-transform [structure next-fn]
|
(all-transform [structure next-fn]
|
||||||
|
|
@ -111,10 +103,7 @@
|
||||||
structure))
|
structure))
|
||||||
|
|
||||||
#?(:clj clojure.lang.PersistentArrayMap)
|
#?(:clj clojure.lang.PersistentArrayMap)
|
||||||
#?(:bb
|
#?(:clj
|
||||||
(all-transform [structure next-fn]
|
|
||||||
(non-transient-map-all-transform structure next-fn {}))
|
|
||||||
:clj
|
|
||||||
(all-transform [structure next-fn]
|
(all-transform [structure next-fn]
|
||||||
(let [k-it (.keyIterator structure)
|
(let [k-it (.keyIterator structure)
|
||||||
v-it (.valIterator structure)
|
v-it (.valIterator structure)
|
||||||
|
|
@ -196,13 +185,10 @@
|
||||||
|
|
||||||
|
|
||||||
:else
|
:else
|
||||||
#?(:bb (into empty-structure
|
(->> structure
|
||||||
(comp (map next-fn) (filter not-NONE?))
|
(r/map next-fn)
|
||||||
structure)
|
(r/filter not-NONE?)
|
||||||
:clj (->> structure
|
(into empty-structure))))))
|
||||||
(r/map next-fn)
|
|
||||||
(r/filter not-NONE?)
|
|
||||||
(into empty-structure)))))))
|
|
||||||
|
|
||||||
|
|
||||||
#?(:cljs default)
|
#?(:cljs default)
|
||||||
|
|
@ -269,10 +255,7 @@
|
||||||
|
|
||||||
|
|
||||||
#?(:clj clojure.lang.PersistentArrayMap)
|
#?(:clj clojure.lang.PersistentArrayMap)
|
||||||
#?(:bb
|
#?(:clj
|
||||||
(map-vals-transform [structure next-fn]
|
|
||||||
(map-vals-non-transient-transform structure {} next-fn))
|
|
||||||
:clj
|
|
||||||
(map-vals-transform [structure next-fn]
|
(map-vals-transform [structure next-fn]
|
||||||
(let [k-it (.keyIterator structure)
|
(let [k-it (.keyIterator structure)
|
||||||
v-it (.valIterator structure)
|
v-it (.valIterator structure)
|
||||||
|
|
@ -299,10 +282,7 @@
|
||||||
array
|
array
|
||||||
)]
|
)]
|
||||||
(clojure.lang.PersistentArrayMap. array)))))
|
(clojure.lang.PersistentArrayMap. array)))))
|
||||||
#?(:bb
|
#?(:clj
|
||||||
(map-keys-transform [structure next-fn]
|
|
||||||
(map-keys-non-transient-transform structure {} next-fn))
|
|
||||||
:clj
|
|
||||||
(map-keys-transform [structure next-fn]
|
(map-keys-transform [structure next-fn]
|
||||||
(let [k-it (.keyIterator structure)
|
(let [k-it (.keyIterator structure)
|
||||||
v-it (.valIterator structure)
|
v-it (.valIterator structure)
|
||||||
|
|
@ -469,24 +449,6 @@
|
||||||
(prepend-one [structure elem]
|
(prepend-one [structure elem]
|
||||||
(into [elem] structure))
|
(into [elem] structure))
|
||||||
|
|
||||||
#?(:cljs cljs.core/Subvec)
|
|
||||||
#?(:cljs
|
|
||||||
(append-all [structure elements]
|
|
||||||
(reduce conj structure elements)))
|
|
||||||
#?(:cljs
|
|
||||||
(prepend-all [structure elements]
|
|
||||||
(let [ret (transient [])]
|
|
||||||
(as-> ret <>
|
|
||||||
(reduce conj! <> elements)
|
|
||||||
(reduce conj! <> structure)
|
|
||||||
(persistent! <>)))))
|
|
||||||
#?(:cljs
|
|
||||||
(append-one [structure elem]
|
|
||||||
(conj structure elem)))
|
|
||||||
#?(:cljs
|
|
||||||
(prepend-one [structure elem]
|
|
||||||
(into [elem] structure)))
|
|
||||||
|
|
||||||
|
|
||||||
#?(:clj Object :cljs default)
|
#?(:clj Object :cljs default)
|
||||||
(append-all [structure elements]
|
(append-all [structure elements]
|
||||||
|
|
@ -512,9 +474,6 @@
|
||||||
(defprotocol FastEmpty
|
(defprotocol FastEmpty
|
||||||
(fast-empty? [s]))
|
(fast-empty? [s]))
|
||||||
|
|
||||||
(defprotocol InsertBeforeIndex
|
|
||||||
(insert-before-idx [aseq idx val]))
|
|
||||||
|
|
||||||
(defnav PosNavigator [getter updater]
|
(defnav PosNavigator [getter updater]
|
||||||
(select* [this structure next-fn]
|
(select* [this structure next-fn]
|
||||||
(if-not (fast-empty? structure)
|
(if-not (fast-empty? structure)
|
||||||
|
|
@ -525,17 +484,6 @@
|
||||||
structure
|
structure
|
||||||
(updater structure next-fn))))
|
(updater structure next-fn))))
|
||||||
|
|
||||||
#?(:bb
|
|
||||||
(defn vec-count [v]
|
|
||||||
(count v))
|
|
||||||
|
|
||||||
:clj
|
|
||||||
(defn vec-count [^clojure.lang.IPersistentVector v]
|
|
||||||
(.length v))
|
|
||||||
|
|
||||||
:cljs
|
|
||||||
(defn vec-count [v]
|
|
||||||
(count v)))
|
|
||||||
|
|
||||||
(defn- update-first-list [l afn]
|
(defn- update-first-list [l afn]
|
||||||
(let [newf (afn (first l))
|
(let [newf (afn (first l))
|
||||||
|
|
@ -551,39 +499,17 @@
|
||||||
(if (nil? bl) '() bl)
|
(if (nil? bl) '() bl)
|
||||||
(concat bl [lastl]))))
|
(concat bl [lastl]))))
|
||||||
|
|
||||||
(defn- update-first-vector [v afn]
|
#?(
|
||||||
(let [val (nth v 0)
|
:clj
|
||||||
newv (afn val)]
|
(defn vec-count [^clojure.lang.IPersistentVector v]
|
||||||
(if (identical? i/NONE newv)
|
(.length v))
|
||||||
(subvec v 1)
|
|
||||||
(assoc v 0 newv)
|
|
||||||
)))
|
|
||||||
|
|
||||||
(defn- update-last-vector [v afn]
|
:cljs
|
||||||
;; type-hinting vec-count to ^int caused weird errors with case
|
(defn vec-count [v]
|
||||||
(let [c (int (vec-count v))]
|
(count v)))
|
||||||
(case c
|
|
||||||
1 (let [[e] v
|
|
||||||
newe (afn e)]
|
|
||||||
(if (identical? i/NONE newe)
|
|
||||||
[]
|
|
||||||
[newe]))
|
|
||||||
2 (let [[e1 e2] v
|
|
||||||
newe (afn e2)]
|
|
||||||
(if (identical? i/NONE newe)
|
|
||||||
[e1]
|
|
||||||
[e1 newe]))
|
|
||||||
(let [i (dec c)
|
|
||||||
newe (afn (nth v i))]
|
|
||||||
(if (identical? i/NONE newe)
|
|
||||||
(pop v)
|
|
||||||
(assoc v i newe))))))
|
|
||||||
|
|
||||||
|
|
||||||
#?(:bb
|
#?(
|
||||||
(defn transient-vec-count [v]
|
|
||||||
(count v))
|
|
||||||
|
|
||||||
:clj
|
:clj
|
||||||
(defn transient-vec-count [^clojure.lang.ITransientVector v]
|
(defn transient-vec-count [^clojure.lang.ITransientVector v]
|
||||||
(.count v))
|
(.count v))
|
||||||
|
|
@ -596,18 +522,32 @@
|
||||||
(extend-protocol UpdateExtremes
|
(extend-protocol UpdateExtremes
|
||||||
#?(:clj clojure.lang.IPersistentVector :cljs cljs.core/PersistentVector)
|
#?(:clj clojure.lang.IPersistentVector :cljs cljs.core/PersistentVector)
|
||||||
(update-first [v afn]
|
(update-first [v afn]
|
||||||
(update-first-vector v afn))
|
(let [val (nth v 0)
|
||||||
|
newv (afn val)]
|
||||||
|
(if (identical? i/NONE newv)
|
||||||
|
(subvec v 1)
|
||||||
|
(assoc v 0 newv)
|
||||||
|
)))
|
||||||
|
|
||||||
(update-last [v afn]
|
(update-last [v afn]
|
||||||
(update-last-vector v afn))
|
;; type-hinting vec-count to ^int caused weird errors with case
|
||||||
|
(let [c (int (vec-count v))]
|
||||||
#?(:cljs cljs.core/Subvec)
|
(case c
|
||||||
#?(:cljs
|
1 (let [[e] v
|
||||||
(update-first [v afn]
|
newe (afn e)]
|
||||||
(update-first-vector v afn)))
|
(if (identical? i/NONE newe)
|
||||||
#?(:cljs
|
[]
|
||||||
(update-last [v afn]
|
[newe]))
|
||||||
(update-last-vector v afn)))
|
2 (let [[e1 e2] v
|
||||||
|
newe (afn e2)]
|
||||||
|
(if (identical? i/NONE newe)
|
||||||
|
[e1]
|
||||||
|
[e1 newe]))
|
||||||
|
(let [i (dec c)
|
||||||
|
newe (afn (nth v i))]
|
||||||
|
(if (identical? i/NONE newe)
|
||||||
|
(pop v)
|
||||||
|
(assoc v i newe))))))
|
||||||
|
|
||||||
#?(:clj String :cljs string)
|
#?(:clj String :cljs string)
|
||||||
(update-first [s afn]
|
(update-first [s afn]
|
||||||
|
|
@ -751,36 +691,3 @@
|
||||||
((:end-fn end-fn) structure start)
|
((:end-fn end-fn) structure start)
|
||||||
(end-fn structure)
|
(end-fn structure)
|
||||||
))
|
))
|
||||||
|
|
||||||
(defn- insert-before-index-list [lst idx val]
|
|
||||||
;; an implementation that is most efficient for list style structures
|
|
||||||
(let [[front back] (split-at idx lst)]
|
|
||||||
(concat front (cons val back))))
|
|
||||||
|
|
||||||
(extend-protocol InsertBeforeIndex
|
|
||||||
nil
|
|
||||||
(insert-before-idx [_ idx val]
|
|
||||||
(if (= 0 idx)
|
|
||||||
(list val)
|
|
||||||
(throw (ex-info "For a nil structure, can only insert before index 0"
|
|
||||||
{:insertion-index idx}))))
|
|
||||||
|
|
||||||
#?(:clj java.lang.String :cljs string)
|
|
||||||
(insert-before-idx [aseq idx val]
|
|
||||||
(apply str (insert-before-index-list aseq idx val)))
|
|
||||||
|
|
||||||
#?(:clj clojure.lang.LazySeq :cljs cljs.core/LazySeq)
|
|
||||||
(insert-before-idx [aseq idx val]
|
|
||||||
(insert-before-index-list aseq idx val))
|
|
||||||
|
|
||||||
#?(:clj clojure.lang.IPersistentVector :cljs cljs.core/PersistentVector)
|
|
||||||
(insert-before-idx [aseq idx val]
|
|
||||||
(let [front (subvec aseq 0 idx)
|
|
||||||
back (subvec aseq idx)]
|
|
||||||
(into (conj front val) back)))
|
|
||||||
|
|
||||||
#?(:clj clojure.lang.IPersistentList :cljs cljs.core/List)
|
|
||||||
(insert-before-idx [aseq idx val]
|
|
||||||
(cond (= idx 0)
|
|
||||||
(cons val aseq)
|
|
||||||
:else (insert-before-index-list aseq idx val))))
|
|
||||||
|
|
|
||||||
|
|
@ -65,4 +65,4 @@
|
||||||
`(defn ~'late-fn [~f ~args]
|
`(defn ~'late-fn [~f ~args]
|
||||||
(case (count ~args)
|
(case (count ~args)
|
||||||
~@(apply concat cases)
|
~@(apply concat cases)
|
||||||
(throw (ex-info "Cannot have late function with more than 18 args" {}))))))
|
(com.rpl.specter.impl/throw-illegal "Cannot have late function with more than 18 args")))))
|
||||||
|
|
|
||||||
|
|
@ -1320,13 +1320,9 @@
|
||||||
(deftest traversed-test
|
(deftest traversed-test
|
||||||
(is (= 10 (select-any (s/traversed s/ALL +) [1 2 3 4]))))
|
(is (= 10 (select-any (s/traversed s/ALL +) [1 2 3 4]))))
|
||||||
|
|
||||||
(defn- predand= [pred v1 v2]
|
(defn- predand= [pred ret v]
|
||||||
(and (pred v1)
|
(and (pred ret)
|
||||||
(pred v2)
|
(= ret v)))
|
||||||
(= v1 v2)))
|
|
||||||
|
|
||||||
(defn listlike? [v]
|
|
||||||
(or (list? v) (seq? v)))
|
|
||||||
|
|
||||||
(deftest nthpath-test
|
(deftest nthpath-test
|
||||||
(is (predand= vector? [1 2 -3 4] (transform (s/nthpath 2) - [1 2 3 4])))
|
(is (predand= vector? [1 2 -3 4] (transform (s/nthpath 2) - [1 2 3 4])))
|
||||||
|
|
@ -1338,7 +1334,7 @@
|
||||||
|
|
||||||
(deftest remove-with-NONE-test
|
(deftest remove-with-NONE-test
|
||||||
(is (predand= vector? [1 2 3] (setval [s/ALL nil?] s/NONE [1 2 nil 3 nil])))
|
(is (predand= vector? [1 2 3] (setval [s/ALL nil?] s/NONE [1 2 nil 3 nil])))
|
||||||
(is (predand= listlike? '(1 2 3) (setval [s/ALL nil?] s/NONE '(1 2 nil 3 nil))))
|
(is (predand= list? '(1 2 3) (setval [s/ALL nil?] s/NONE '(1 2 nil 3 nil))))
|
||||||
(is (= {:b 2} (setval :a s/NONE {:a 1 :b 2})))
|
(is (= {:b 2} (setval :a s/NONE {:a 1 :b 2})))
|
||||||
(is (= {:b 2} (setval (s/must :a) s/NONE {:a 1 :b 2})))
|
(is (= {:b 2} (setval (s/must :a) s/NONE {:a 1 :b 2})))
|
||||||
(is (predand= vector? [1 3] (setval (s/keypath 1) s/NONE [1 2 3])))
|
(is (predand= vector? [1 3] (setval (s/keypath 1) s/NONE [1 2 3])))
|
||||||
|
|
@ -1440,15 +1436,6 @@
|
||||||
(is (= "abq" (setval s/LAST "q" "abc")))
|
(is (= "abq" (setval s/LAST "q" "abc")))
|
||||||
)
|
)
|
||||||
|
|
||||||
(defn whitespace? [char]
|
|
||||||
(re-matches #"\s" (str char)))
|
|
||||||
|
|
||||||
(deftest string-transform-test
|
|
||||||
(is (= "123" (transform s/ALL identity "123")))
|
|
||||||
(is (= "123" (transform [s/ALL whitespace?] s/NONE "1 2 3")))
|
|
||||||
#?(:clj (is (= "123" (setval [s/ALL #(Character/isWhitespace %)] s/NONE "1 2 3"))))
|
|
||||||
#?(:clj (is (= "123" (transform [(s/filterer #(Character/isWhitespace %))] s/NONE "1 2 3")))))
|
|
||||||
|
|
||||||
(deftest regex-navigation-test
|
(deftest regex-navigation-test
|
||||||
;; also test regexes as implicit navs
|
;; also test regexes as implicit navs
|
||||||
(is (= (select #"t" "test") ["t" "t"]))
|
(is (= (select #"t" "test") ["t" "t"]))
|
||||||
|
|
@ -1467,11 +1454,11 @@
|
||||||
|
|
||||||
(deftest single-value-none-navigators-test
|
(deftest single-value-none-navigators-test
|
||||||
(is (predand= vector? [1 2 3] (setval s/AFTER-ELEM 3 [1 2])))
|
(is (predand= vector? [1 2 3] (setval s/AFTER-ELEM 3 [1 2])))
|
||||||
(is (predand= listlike? '(1 2 3) (setval s/AFTER-ELEM 3 '(1 2))))
|
(is (predand= list? '(1 2 3) (setval s/AFTER-ELEM 3 '(1 2))))
|
||||||
(is (predand= listlike? '(1) (setval s/AFTER-ELEM 1 nil)))
|
(is (predand= list? '(1) (setval s/AFTER-ELEM 1 nil)))
|
||||||
(is (predand= vector? [3 1 2] (setval s/BEFORE-ELEM 3 [1 2])))
|
(is (predand= vector? [3 1 2] (setval s/BEFORE-ELEM 3 [1 2])))
|
||||||
(is (predand= listlike? '(3 1 2) (setval s/BEFORE-ELEM 3 '(1 2))))
|
(is (predand= list? '(3 1 2) (setval s/BEFORE-ELEM 3 '(1 2))))
|
||||||
(is (predand= listlike? '(1) (setval s/BEFORE-ELEM 1 nil)))
|
(is (predand= list? '(1) (setval s/BEFORE-ELEM 1 nil)))
|
||||||
(is (= #{1 2 3} (setval s/NONE-ELEM 3 #{1 2})))
|
(is (= #{1 2 3} (setval s/NONE-ELEM 3 #{1 2})))
|
||||||
(is (= #{1} (setval s/NONE-ELEM 1 nil)))
|
(is (= #{1} (setval s/NONE-ELEM 1 nil)))
|
||||||
)
|
)
|
||||||
|
|
@ -1515,7 +1502,7 @@
|
||||||
[l (limit-size 10 (gen/not-empty (gen/list gen/int)))]
|
[l (limit-size 10 (gen/not-empty (gen/list gen/int)))]
|
||||||
(let [newl (setval s/FIRST s/NONE l)]
|
(let [newl (setval s/FIRST s/NONE l)]
|
||||||
(and (= newl (rest l))
|
(and (= newl (rest l))
|
||||||
(listlike? newl)
|
(list? newl)
|
||||||
))))
|
))))
|
||||||
|
|
||||||
(defspec remove-last-vector
|
(defspec remove-last-vector
|
||||||
|
|
@ -1622,18 +1609,14 @@
|
||||||
|
|
||||||
(deftest before-index-test
|
(deftest before-index-test
|
||||||
(let [data [1 2 3]
|
(let [data [1 2 3]
|
||||||
datal '(1 2 3)
|
datal '(1 2 3)]
|
||||||
data-str "abcdef"]
|
|
||||||
(is (predand= vector? [:a 1 2 3] (setval (s/before-index 0) :a data)))
|
(is (predand= vector? [:a 1 2 3] (setval (s/before-index 0) :a data)))
|
||||||
(is (predand= vector? [1 2 3] (setval (s/before-index 1) s/NONE data)))
|
(is (predand= vector? [1 2 3] (setval (s/before-index 1) s/NONE data)))
|
||||||
(is (predand= vector? [1 :a 2 3] (setval (s/before-index 1) :a data)))
|
(is (predand= vector? [1 :a 2 3] (setval (s/before-index 1) :a data)))
|
||||||
(is (predand= vector? [1 2 3 :a] (setval (s/before-index 3) :a data)))
|
(is (predand= vector? [1 2 3 :a] (setval (s/before-index 3) :a data)))
|
||||||
; ensure inserting at index 0 in nil structure works, as in previous impl
|
(is (predand= list? '(:a 1 2 3) (setval (s/before-index 0) :a datal)))
|
||||||
(is (predand= listlike? '(:a) (setval (s/before-index 0) :a nil)))
|
(is (predand= list? '(1 :a 2 3) (setval (s/before-index 1) :a datal)))
|
||||||
(is (predand= listlike? '(:a 1 2 3) (setval (s/before-index 0) :a datal)))
|
(is (predand= list? '(1 2 3 :a) (setval (s/before-index 3) :a datal)))
|
||||||
(is (predand= listlike? '(1 :a 2 3) (setval (s/before-index 1) :a datal)))
|
|
||||||
(is (predand= listlike? '(1 2 3 :a) (setval (s/before-index 3) :a datal)))
|
|
||||||
(is (predand= string? "abcxdef" (setval (s/before-index 3) (char \x) data-str)))
|
|
||||||
))
|
))
|
||||||
|
|
||||||
(deftest index-nav-test
|
(deftest index-nav-test
|
||||||
|
|
@ -1646,12 +1629,12 @@
|
||||||
(is (predand= vector? [1 2 4 5 6 3] (setval (s/index-nav 2) 5 data)))
|
(is (predand= vector? [1 2 4 5 6 3] (setval (s/index-nav 2) 5 data)))
|
||||||
(is (predand= vector? [6 1 2 3 4 5] (setval (s/index-nav 5) 0 data)))
|
(is (predand= vector? [6 1 2 3 4 5] (setval (s/index-nav 5) 0 data)))
|
||||||
|
|
||||||
(is (predand= listlike? '(3 1 2 4 5 6) (setval (s/index-nav 2) 0 datal)))
|
(is (predand= list? '(3 1 2 4 5 6) (setval (s/index-nav 2) 0 datal)))
|
||||||
(is (predand= listlike? '(1 3 2 4 5 6) (setval (s/index-nav 2) 1 datal)))
|
(is (predand= list? '(1 3 2 4 5 6) (setval (s/index-nav 2) 1 datal)))
|
||||||
(is (predand= listlike? '(1 2 3 4 5 6) (setval (s/index-nav 2) 2 datal)))
|
(is (predand= list? '(1 2 3 4 5 6) (setval (s/index-nav 2) 2 datal)))
|
||||||
(is (predand= listlike? '(1 2 4 5 3 6) (setval (s/index-nav 2) 4 datal)))
|
(is (predand= list? '(1 2 4 5 3 6) (setval (s/index-nav 2) 4 datal)))
|
||||||
(is (predand= listlike? '(1 2 4 5 6 3) (setval (s/index-nav 2) 5 datal)))
|
(is (predand= list? '(1 2 4 5 6 3) (setval (s/index-nav 2) 5 datal)))
|
||||||
(is (predand= listlike? '(6 1 2 3 4 5) (setval (s/index-nav 5) 0 datal)))
|
(is (predand= list? '(6 1 2 3 4 5) (setval (s/index-nav 5) 0 datal)))
|
||||||
))
|
))
|
||||||
|
|
||||||
(deftest indexed-vals-test
|
(deftest indexed-vals-test
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue