Skip to content

Commit

Permalink
Add support for Clojure CLR
Browse files Browse the repository at this point in the history
  • Loading branch information
brandoncorrea committed Oct 27, 2024
1 parent 7c36bc6 commit ebac1a4
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 60 deletions.
13 changes: 12 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ jobs:
distribution: 'zulu'
java-version: '8'

- name: Prepare dotnet
uses: xt0rted/[email protected]

- name: Prepare Clojure CLR
run: |
dotnet tool install --global Clojure.Main --version 1.12.0-alpha10
dotnet tool install --global Clojure.Cljr --version 0.1.0-alpha4
- name: Install clojure tools
uses: DeLaGuardo/[email protected]
with:
Expand All @@ -25,5 +33,8 @@ jobs:
key: cljdeps-${{ hashFiles('project.clj') }}
restore-keys: cljdeps-

- name: Run tests
- name: Run clj and cljs tests
run: lein test-all

- name: Run cljr tests
run: cljr -X:test
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ Or to your Leiningen project file:

[dev.weavejester/medley "1.8.1"]

Or to your deps-clr.edn file:

io.github.weavejester/medley {:git/tag "FIXME" :git/sha "FIXME"}

## Documentation

* [API Docs](http://weavejester.github.io/medley/medley.core.html)
Expand Down
6 changes: 6 additions & 0 deletions deps-clr.edn
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{:paths ["src"]
:aliases
{:test {:extra-paths ["test"]
:extra-deps {io.github.dmiller/test-runner {:git/tag "v0.5.1clr" :git/sha "814e06f"}}
:exec-fn cognitect.test-runner.api/test
:exec-args {:dirs ["test"]}}}}
92 changes: 55 additions & 37 deletions src/medley/core.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@
(dissoc-in m ks))))

(defn- editable? [coll]
#?(:clj (instance? clojure.lang.IEditableCollection coll)
:cljs (satisfies? cljs.core/IEditableCollection coll)))
#?(:cljs (satisfies? cljs.core/IEditableCollection coll)
:default (instance? clojure.lang.IEditableCollection coll)))

(defn- assoc-some-transient! [m k v]
(if (nil? v) m (assoc! m k v)))
Expand Down Expand Up @@ -102,8 +102,8 @@
(defn map-entry
"Create a map entry for a key and value pair."
[k v]
#?(:clj (clojure.lang.MapEntry. k v)
:cljs (cljs.core/MapEntry. k v nil)))
#?(:cljs (cljs.core/MapEntry. k v nil)
:default (clojure.lang.MapEntry. k v)))

(defn map-kv
"Maps a function over the key/value pairs of an associative collection. Expects
Expand Down Expand Up @@ -184,21 +184,21 @@

(defn queue
"Creates an empty persistent queue, or one populated with a collection."
([] #?(:clj clojure.lang.PersistentQueue/EMPTY
:cljs cljs.core/PersistentQueue.EMPTY))
([] #?(:cljs cljs.core/PersistentQueue.EMPTY
:default clojure.lang.PersistentQueue/EMPTY))
([coll] (into (queue) coll)))

(defn queue?
"Returns true if x implements clojure.lang.PersistentQueue."
[x]
(instance? #?(:clj clojure.lang.PersistentQueue
:cljs cljs.core/PersistentQueue) x))
(instance? #?(:cljs cljs.core/PersistentQueue
:default clojure.lang.PersistentQueue) x))

(defn boolean?
"Returns true if x is a boolean."
[x]
#?(:clj (instance? Boolean x)
:cljs (or (true? x) (false? x))))
#?(:cljs (or (true? x) (false? x))
:default (instance? Boolean x)))

(defn least
"Return the least argument (as defined by the compare function) in O(n) time."
Expand Down Expand Up @@ -322,12 +322,14 @@
(persistent!
(reduce (fn [m v]
(let [k (keyf v)]
(assoc! m k #?(:clj (if-let [kv (find m k)]
(collatef (val kv) v)
(initf v))
:cljs (if (contains? m k)
(collatef (get m k) v)
(initf v))))))
(assoc! m k #?(:cljs
(if (contains? m k)
(collatef (get m k) v)
(initf v))
:default
(if-let [kv (find m k)]
(collatef (val kv) v)
(initf v))))))
(transient {})
coll))))

Expand Down Expand Up @@ -453,27 +455,31 @@
{:added "1.7.0"}
([pred]
(fn [rf]
(let [part #?(:clj (java.util.ArrayList.) :cljs (array-list))
(let [part #?(:clj (java.util.ArrayList.)
:cljr (System.Collections.ArrayList.)
:cljs (array-list))
prev (volatile! ::none)]
(fn
([] (rf))
([result]
(rf (if (.isEmpty part)
(rf (if #?(:cljr (zero? (.-Count part))
:default (.isEmpty part))
result
(let [v (vec (.toArray part))]
(.clear part)
(let [v (vec (#?(:cljr .ToArray :default .toArray) part))]
(#?(:cljr .Clear :default .clear) part)
(unreduced (rf result v))))))
([result input]
(let [p @prev]
(vreset! prev input)
(if (or (#?(:clj identical? :cljs keyword-identical?) p ::none)
(if (or (#?(:cljs keyword-identical? :default identical?) p ::none)
(not (pred p input)))
(do (.add part input) result)
(let [v (vec (.toArray part))]
(.clear part)
(do (#?(:cljr .Add :default .add) part input)
result)
(let [v (vec (#?(:cljr .ToArray :default .toArray) part))]
(#?(:cljr .Clear :default .clear) part)
(let [ret (rf result v)]
(when-not (reduced? ret)
(.add part input))
(#?(:cljr .Add :default .add) part input))
ret)))))))))
([pred coll]
(lazy-seq
Expand Down Expand Up @@ -601,14 +607,16 @@
This function therefore acts like an atomic `deref` then `swap!`."
{:arglists '([atom f & args])}
([atom f]
#?(:clj (loop []
(let [value @atom]
(if (compare-and-set! atom value (f value))
value
(recur))))
:cljs (let [value @atom]
(reset! atom (f value))
value)))
#?(:cljs
(let [value @atom]
(reset! atom (f value))
value)
:default
(loop []
(let [value @atom]
(if (compare-and-set! atom value (f value))
value
(recur))))))
([atom f & args]
(deref-swap! atom #(apply f % args))))

Expand All @@ -624,6 +632,7 @@
Clojure as well as ClojureScript."
[ex]
#?(:clj (when (instance? Throwable ex) (.getMessage ^Throwable ex))
:cljr (when (instance? Exception ex) (.-Message ^Exception ex))
:cljs (cljs.core/ex-message ex)))

(defn ex-cause
Expand All @@ -632,40 +641,49 @@
Clojure as well as ClojureScript."
[ex]
#?(:clj (when (instance? Throwable ex) (.getCause ^Throwable ex))
:cljr (when (instance? Exception ex) (.-InnerException ^Exception ex))
:cljs (cljs.core/ex-cause ex)))

(defn uuid?
"Returns true if the value is a UUID."
[x]
(instance? #?(:clj java.util.UUID :cljs cljs.core/UUID) x))
(instance? #?(:clj java.util.UUID
:cljr System.Guid
:cljs cljs.core/UUID) x))

(defn uuid
"Returns a UUID generated from the supplied string. Same as `cljs.core/uuid`
in ClojureScript, while in Clojure it returns a `java.util.UUID` object."
[s]
#?(:clj (java.util.UUID/fromString s)
:cljr (System.Guid. s)
:cljs (cljs.core/uuid s)))

(defn random-uuid
"Generates a new random UUID. Same as `cljs.core/random-uuid` except it works
for Clojure as well as ClojureScript."
[]
#?(:clj (java.util.UUID/randomUUID)
:cljr (System.Guid/NewGuid)
:cljs (cljs.core/random-uuid)))

(defn regexp?
"Returns true if the value is a regular expression."
{:added "1.4.0"}
[x]
(instance? #?(:clj java.util.regex.Pattern :cljs js/RegExp) x))
(instance? #?(:clj java.util.regex.Pattern
:cljr System.Text.RegularExpressions.Regex
:cljs js/RegExp) x))

(defn index-of
"Returns the index of the first occurrence of the item in the sequential
collection coll, or nil if not found."
{:added "1.9.0"}
[^java.util.List coll item]
#?(:clj [^java.util.List coll item]
:cljr [^System.Collections.IEnumerable coll item]
:default [coll item])
(when (some? coll)
(let [index (.indexOf coll item)]
(let [index (#?(:cljr .IndexOf :default .indexOf) coll item)]
(when-not (neg? index) index))))

(defn find-in
Expand Down
56 changes: 34 additions & 22 deletions test/medley/core_test.cljc
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
(ns medley.core-test
#?(:clj (:import [clojure.lang ArityException]))
(:require #?(:clj [clojure.test :refer :all]
:cljs [cljs.test :refer-macros [deftest is testing]])
#?@(:cljs []
:default [(:import [clojure.lang ArityException])])
(:require #?(:cljs [cljs.test :refer-macros [deftest is testing]]
:default [clojure.test :refer :all])
[medley.core :as m]))

(deftest test-find-first
Expand Down Expand Up @@ -162,17 +163,17 @@

(deftest test-queue
(testing "empty"
#?(:clj (is (instance? clojure.lang.PersistentQueue (m/queue)))
:cljs (is (instance? cljs.core.PersistentQueue (m/queue))))
#?(:cljs (is (instance? cljs.core.PersistentQueue (m/queue)))
:default (is (instance? clojure.lang.PersistentQueue (m/queue))))
(is (empty? (m/queue))))
(testing "not empty"
#?(:clj (is (instance? clojure.lang.PersistentQueue (m/queue [1 2 3])))
:cljs (is (instance? cljs.core.PersistentQueue (m/queue [1 2 3]))))
#?(:cljs (is (instance? cljs.core.PersistentQueue (m/queue [1 2 3])))
:default (is (instance? clojure.lang.PersistentQueue (m/queue [1 2 3]))))
(is (= (first (m/queue [1 2 3])) 1))))

(deftest test-queue?
#?(:clj (is (m/queue? clojure.lang.PersistentQueue/EMPTY))
:cljs (is (m/queue? cljs.core.PersistentQueue.EMPTY)))
#?(:cljs (is (m/queue? cljs.core.PersistentQueue.EMPTY))
:default (is (m/queue? clojure.lang.PersistentQueue/EMPTY)))
(is (not (m/queue? []))))

(deftest test-boolean?
Expand Down Expand Up @@ -241,9 +242,11 @@
(is (= (m/mapply foo 0 {:baz 1}) [0 1]))
(is (= (m/mapply foo 0 {:spam 1}) [0 nil]))
(is (= (m/mapply foo 0 nil) [0 nil]))
#?@(:clj [(is (thrown? ArityException (m/mapply foo {})))
(is (thrown? IllegalArgumentException (m/mapply foo 0)))]
:cljs [(is (thrown? js/Error (m/mapply foo 0)))])))
(is (thrown? #?(:clj IllegalArgumentException
:cljr ArgumentException
:cljs js/Error) (m/mapply foo 0)))
#?(:cljs (is (= (m/mapply foo {}) [nil nil]))
:default (is (thrown? ArityException (m/mapply foo {}))))))

(deftest test-collate-by
(is (= (m/collate-by identity conj vector [1 2 2 3 3])
Expand Down Expand Up @@ -451,10 +454,11 @@
(is (= (m/abs 2) 2))
(is (= (m/abs -2.1) 2.1))
(is (= (m/abs 1.8) 1.8))
#?@(:clj [(is (= (m/abs -1/3) 1/3))
(is (= (m/abs 1/2) 1/2))
(is (= (m/abs 3N) 3N))
(is (= (m/abs -4N) 4N))]))
#?@(:cljs []
:default [(is (= (m/abs -1/3) 1/3))
(is (= (m/abs 1/2) 1/2))
(is (= (m/abs 3N) 3N))
(is (= (m/abs -4N) 4N))]))

(deftest test-deref-swap!
(let [a (atom 0)]
Expand All @@ -472,12 +476,14 @@

(deftest test-ex-message
(is (= (m/ex-message (ex-info "foo" {})) "foo"))
(is (= (m/ex-message (new #?(:clj Exception :cljs js/Error) "bar")) "bar")))
(let [ex (new #?(:cljs js/Error :default Exception) "bar")]
(is (= (m/ex-message ex) "bar"))))

(deftest test-ex-cause
(let [cause (new #?(:clj Exception :cljs js/Error) "foo")]
(let [cause (new #?(:cljs js/Error :default Exception) "foo")]
(is (= (m/ex-cause (ex-info "foo" {} cause)) cause))
#?(:clj (is (= (m/ex-cause (Exception. "foo" cause)) cause)))))
#?@(:cljs []
:default [(is (= (m/ex-cause (Exception. "foo" cause)) cause))])))

(deftest test-uuid?
(let [x #uuid "d1a4adfa-d9cf-4aa5-9f05-a15365d1bfa6"]
Expand All @@ -488,14 +494,20 @@

(deftest test-uuid
(let [x (m/uuid "d1a4adfa-d9cf-4aa5-9f05-a15365d1bfa6")]
(is (instance? #?(:clj java.util.UUID :cljs cljs.core.UUID) x))
(is (instance? #?(:clj java.util.UUID
:cljr System.Guid
:cljs cljs.core.UUID) x))
(is (= x #uuid "d1a4adfa-d9cf-4aa5-9f05-a15365d1bfa6"))))

(deftest test-random-uuid
(let [x (m/random-uuid)
y (m/random-uuid)]
(is (instance? #?(:clj java.util.UUID :cljs cljs.core.UUID) x))
(is (instance? #?(:clj java.util.UUID :cljs cljs.core.UUID) y))
(is (instance? #?(:clj java.util.UUID
:cljr System.Guid
:cljs cljs.core.UUID) x))
(is (instance? #?(:clj java.util.UUID
:cljr System.Guid
:cljs cljs.core.UUID) y))
(is (not= x y))))

(deftest test-regexp?
Expand Down

0 comments on commit ebac1a4

Please sign in to comment.