(ns metabase.types.coercion-hierarchies (:require [clojure.set :as set])) | |
these need to be defonce so we don't drop our hierarchies, but defonce doesn't support docstrings: https://clojure.atlassian.net/browse/CLJ-1148 | |
Map of | (defonce ^:private strategy->allowed-base-types (atom {})) |
Map of coercion strategy -> resulting effective-type | (defonce ^:private strategy->effective-type (atom {})) |
Map of base-type -> #{strategy} which are not inheritable. Eg, binary fields are marked | (defonce ^:private non-descending-base-type->strategy (atom {})) |
Get a map of strategies -> allowed-base-types. These must live outside of the hierarchy. | (defn non-descending-strategies [] @non-descending-base-type->strategy) |
Gets the effective type for strategy. Essentially a getter over the private strategy->effective-type. | (defn effective-type-for-strategy [strategy] (get @strategy->effective-type strategy)) |
Ensure x is a sequential collection. Copied from metabase.util as that namespace is not amenable to cljc. | (defn- one-or-many [x] (if ((some-fn sequential? set? nil?) x) x [x])) |
Define the | (defn define-types! [coercion-strategy base-type-or-types effective-type] (let [base-types (set (one-or-many base-type-or-types))] (swap! strategy->allowed-base-types assoc coercion-strategy base-types)) (swap! strategy->effective-type assoc coercion-strategy effective-type)) |
Define coercion strategies that should not exist for all of the descendants of base-type-or-types. | (defn define-non-inheritable-type! [coercion-strategy base-type-or-types effective-type] (swap! non-descending-base-type->strategy (partial merge-with set/union) (zipmap (one-or-many base-type-or-types) (repeat #{coercion-strategy}))) (swap! strategy->effective-type assoc coercion-strategy effective-type)) |
(defn- build-hierarchy [pairs] (reduce (fn [h [tag parent]] (derive h tag parent)) #?(:clj @#'clojure.core/global-hierarchy :cljs @(#'clojure.core/get-global-hierarchy)) pairs)) | |
atom is nil => rebuild the hierarchy | |
(def ^:private base-type-hierarchy* (atom nil)) | |
The global hierarchy, with coercion strategies added as ancestors of their allowed base type(s). | (defn base-type-hierarchy [] (when-not @base-type-hierarchy* (locking base-type-hierarchy* (when-not @base-type-hierarchy* (reset! base-type-hierarchy* (build-hierarchy (for [[strategy base-types] @strategy->allowed-base-types base-type base-types] [base-type strategy])))))) @base-type-hierarchy*) |
(def ^:private effective-type-hierarchy* (atom nil)) | |
The global hierarchy, with coercion strategies added as children of their resulting effective type. | (defn effective-type-hierarchy [] (when-not @effective-type-hierarchy* (locking effective-type-hierarchy* (when-not @effective-type-hierarchy* (reset! effective-type-hierarchy* (build-hierarchy (seq @strategy->effective-type)))))) @effective-type-hierarchy*) |
rebuild coercion hierarchies if the global hierarchy changes | (add-watch #?(:clj #'clojure.core/global-hierarchy :cljs (#'clojure.core/get-global-hierarchy)) ::rebuild-hierarchies (fn [_key _ref old new] (when-not (= old new) (reset! base-type-hierarchy* nil) (reset! effective-type-hierarchy* nil)))) |
rebuild coercion hierarchies if the type map atoms change | |
(add-watch strategy->allowed-base-types ::rebuild-hierarchies (fn [_key _ref old new] (when-not (= old new) (reset! base-type-hierarchy* nil)))) | |
(add-watch strategy->effective-type ::rebuild-hierarchies (fn [_key _ref old new] (when-not (= old new) (reset! effective-type-hierarchy* nil)))) | |