diff --git a/src/taoensso/encore/dispatch.cljc b/src/taoensso/encore/dispatch.cljc new file mode 100644 index 00000000..ef5e002f --- /dev/null +++ b/src/taoensso/encore/dispatch.cljc @@ -0,0 +1,110 @@ +(ns ^:no-doc taoensso.encore.dispatch + "Alpha, subject to change without notice! + Low-level toolkit for building dispatch handlers. + Used by Telemere, Timbre, Tufte, etc." + {:added "vX.Y.Z (YYYY-MM-DD)"} + (:require + [clojure.string :as str] + [taoensso.encore :as enc :refer [have have?]] + [taoensso.encore.ctx-filter :as cf] + [taoensso.encore.runner :as runner])) + +(comment + (remove-ns 'taoensso.encore.dispatch) + (:api (enc/interns-overview))) + +(defn wrap-handler + "Wraps given handler-fn to add common handler-level functionality." + [handler-fn + {:keys + [#?(:clj async) sample rate-limit, + ns-filter kind-filter id-filter level-filter + ;; error-fn backp-fn ; TODO + ]}] + + (let [;; rl-error (enc/limiter {} [[1 (enc/ms :mins 15)]]) + ;; rl-backp (enc/limiter {} [[1 (enc/ms :mins 15)]]) + + sample-rate (when sample (enc/as-pnum! sample)) + rate-limiter (when-let [spec rate-limit] (enc/limiter {} spec)) + ctx-filter + (when (or ns-filter kind-filter id-filter level-filter) + (cf/ctx-filter ns-filter kind-filter id-filter level-filter)) + + wrapped-handler-fn + (fn wrapped-handler-fn [handler-arg] + (enc/try* + (enc/cond + (if sample-rate (< (Math/random) ^double sample-rate) false) :noop + (if rate-limiter (if (rate-limiter nil) true false) false) :noop + (if ctx-filter + ^boolean (cf/filter? handler-arg ctx-filter) + ;; ^boolean + ;; (ctx-filter + ;; (.-ns handler-val) (.-kind handler-val) + ;; (.-id handler-val) (.-level handler-val)) + false) + + :else (do (handler-fn handler-arg) nil)) + + (catch :any t + ;; TODO + ;; (when error-fn (error-fn t)) + ;; (when-not (some? (rl-error :TODO)) + ;; (comment "TODO Encore error signal with `t`")) + nil)))] + + #?(:cljs wrapped-handler-fn + :clj + (if-not async + wrapped-handler-fn + (let [runner (runner/runner (have map? async))] + (fn wrapped-handler-fn* [handler-arg] + (when-let [back-pressure? (runner (fn [] (wrapped-handler-fn handler-arg)))] + ;; TODO + ;; (when backp-fn (backp-fn)) + ;; (when-not (some? (rl-backp :TODO)) + ;; (comment "TODO Encore warn signal")) + ))))))) + +(defn -registered-handlers [handlers] (enc/map-vals meta handlers)) + +#?(:clj + (defmacro registered-handlers + "Returns { } for all registered handlers." + [handlers-var] `(registered-handlers* ~handlers-var))) + +#?(:clj + (defmacro handler-register! + "Registers given (handler-fn [handler-arg]), and returns + { } for all handlers now registered." + [handlers-var handler-id handler-fn dispatch-opts] + `(let [wrapped-handler-fn# (with-meta (wrap-handler ~handler-fn ~dispatch-opts) (or ~dispatch-opts {}))] + (registered-handlers* + #?(:cljs (set! ~handlers-var (assoc ~handlers-var ~handler-id wrapped-handler-fn#)) + :clj (alter-var-root #'~handlers-var (fn [m#] (assoc m# ~handler-id wrapped-handler-fn#)))))))) + +#?(:clj + (defmacro handler-unregister! + "Unregisters handler with given id, and returns { } + for all handlers still registered." + [handlers-var handler-id] + `(registered-handlers* + #?(:cljs (set! ~handlers-var (dissoc ~handlers-var ~handler-id)) + :clj (alter-var-root #'~handlers-var (fn [m#] (dissoc m# ~handler-id))))))) + +#?(:clj + (defmacro with-handler + "Low-level util. Executes form with given pre-wrapped handler-fn registered." + [handlers-var handler-id wrapped-handler-fn form] + `(binding [~handlers-var (assoc ~handlers-var ~handler-id ~wrapped-handler-fn)] + ~form))) + +(comment + (def *handlers* nil) + (handler-register! *handlers* :hid1 (fn [x]) {}) + (registered-handlers *handlers*)) + +(enc/def* dispatch-opts-docs + "TODO" + nil)