-
-
Notifications
You must be signed in to change notification settings - Fork 48
Make parameter aliases work for all schemas #248
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 24 commits
7969487
c45fbc1
edab5db
09dfdeb
eacac72
04a7686
883dbb0
afd66f4
4c5b260
6d8d215
2ecf1fb
332d3b1
0579763
ddf76bc
726064c
34a97b3
b823695
ef5917e
7c0e4a2
b24d513
ffe9032
4e1bbb5
99b6a57
ba4bd43
1beecd3
f843ae5
4842d26
95bdbd9
367c7da
a5ad9f4
cbcb972
8ca01bc
ec32339
9c4fda4
71d6438
afb6984
b9d6df0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,50 +1,171 @@ | ||
| (ns martian.schema-tools | ||
| (:require [schema.core :as s #?@(:cljs [:refer [MapEntry EqSchema]])] | ||
| [schema.spec.core :as spec]) | ||
| #?(:clj (:import [schema.core MapEntry EqSchema]))) | ||
|
|
||
| ;; todo | ||
| ;; write some tests and lean on schema-tools.core where possible | ||
|
Comment on lines
-6
to
-7
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Did just that. |
||
|
|
||
| (defn with-paths [path schema] | ||
| (keep (fn [schema] | ||
| (cond (and (instance? MapEntry schema) | ||
| (instance? EqSchema (:key-schema schema))) | ||
| {:path (conj path (:v (:key-schema schema))) | ||
| :schema (:val-schema schema)} | ||
| (map? schema) | ||
| {:path path | ||
| :schema schema})) | ||
| (spec/subschemas (s/spec schema)))) | ||
| (:require [schema.core :as s] | ||
| [schema-tools.impl])) | ||
|
|
||
| (defn explicit-key [k] | ||
| (if (s/specific-key? k) (s/explicit-schema-key k) k)) | ||
|
Comment on lines
+6
to
+7
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The same helper as in the |
||
|
|
||
| (defn concrete-key? [k] | ||
| (or (keyword? k) | ||
| (s/specific-key? k) | ||
| (string? k))) | ||
|
|
||
| (defn- concat* [& xs] | ||
| (apply concat (remove nil? xs))) | ||
|
|
||
| (defprotocol KeyPaths | ||
| (-paths [schema path include-self?] | ||
| "Returns a sequence of path vectors found within the given prefix `path`. | ||
| If `include-self?` is true, includes `path` itself as the first element.")) | ||
|
||
|
|
||
| (extend-protocol KeyPaths | ||
| #?(:clj clojure.lang.APersistentMap | ||
| :cljs cljs.core.PersistentArrayMap) | ||
| (-paths [schema path include-self?] | ||
| (concat* | ||
| (when include-self? (list path)) | ||
| (mapcat (fn [[k v]] | ||
| (when (concrete-key? k) | ||
| (let [k' (explicit-key k) | ||
| path' (conj path k')] | ||
| (cons path' (-paths v path' false))))) | ||
| schema))) | ||
|
|
||
| ;; NB: Vector schemas are transparent (indices are ignored). | ||
| #?(:clj clojure.lang.APersistentVector | ||
| :cljs cljs.core.PersistentVector) | ||
| (-paths [schema path include-self?] | ||
| (concat* | ||
| (when include-self? (list path)) | ||
| (mapcat #(-paths % path false) schema))) | ||
|
|
||
| schema.core.NamedSchema | ||
| (-paths [schema path include-self?] | ||
| (let [inner-schema (:schema schema)] | ||
| (concat* | ||
| (when include-self? (list path)) | ||
| (-paths inner-schema (conj path :schema) true) | ||
| (-paths inner-schema path false)))) | ||
|
|
||
| schema.core.Maybe | ||
| (-paths [schema path include-self?] | ||
| (let [inner-schema (:schema schema)] | ||
| (concat* | ||
| (when include-self? (list path)) | ||
| (-paths inner-schema (conj path :schema) true) | ||
| (-paths inner-schema path false)))) | ||
|
|
||
| schema.core.Constrained | ||
| (-paths [schema path include-self?] | ||
| (let [inner-schema (:schema schema)] | ||
| (concat* | ||
| (when include-self? (list path)) | ||
| (-paths inner-schema (conj path :schema) true) | ||
| (-paths inner-schema path false)))) | ||
|
|
||
| schema.core.One | ||
| (-paths [schema path include-self?] | ||
| (let [inner-schema (:schema schema)] | ||
| (concat* | ||
| (when include-self? (list path)) | ||
| (-paths inner-schema (conj path :schema) true) | ||
| (-paths inner-schema path false)))) | ||
|
|
||
| schema.core.Record | ||
| (-paths [schema path include-self?] | ||
| (let [inner-schema (:schema schema)] | ||
| (concat* | ||
| (when include-self? (list path)) | ||
| (-paths inner-schema (conj path :schema) true) | ||
| (-paths inner-schema path false)))) | ||
|
|
||
| schema.core.Both | ||
| (-paths [schema path include-self?] | ||
| (let [inner-schemas (:schemas schema)] | ||
| (concat* | ||
| (when include-self? (list path)) | ||
| (mapcat #(-paths % (conj path :schemas) false) inner-schemas) | ||
| (mapcat #(-paths % path false) inner-schemas)))) | ||
|
|
||
| schema.core.Either | ||
| (-paths [schema path include-self?] | ||
| (let [inner-schemas (:schemas schema)] | ||
| (concat* | ||
| (when include-self? (list path)) | ||
| (mapcat #(-paths % (conj path :schemas) false) inner-schemas) | ||
| (mapcat #(-paths % path false) inner-schemas)))) | ||
|
|
||
| schema.core.CondPre | ||
| (-paths [schema path include-self?] | ||
| (let [inner-schemas (:schemas schema)] | ||
| (concat* | ||
| (when include-self? (list path)) | ||
| (mapcat #(-paths % (conj path :schemas) false) inner-schemas) | ||
| (mapcat #(-paths % path false) inner-schemas)))) | ||
|
|
||
| schema.core.ConditionalSchema | ||
| (-paths [schema path include-self?] | ||
| (let [inner-schemas (map second (:preds-and-schemas schema))] | ||
| (concat* | ||
| (when include-self? (list path)) | ||
| (mapcat #(-paths % (conj path :preds-and-schemas) false) inner-schemas) | ||
| (mapcat #(-paths % path false) inner-schemas)))) | ||
|
|
||
| schema_tools.impl.Default | ||
| (-paths [schema path include-self?] | ||
| (let [inner-schema (:schema schema)] | ||
| (concat* | ||
| (when include-self? (list path)) | ||
| (-paths inner-schema (conj path :schema) true) | ||
| (-paths inner-schema (conj path :value) true) | ||
| (-paths inner-schema path false)))) | ||
|
|
||
| #?(:clj Object :cljs default) | ||
| (-paths [_ path include-self?] | ||
| (when include-self? (list path))) | ||
|
|
||
| nil | ||
| (-paths [_ _ _] nil)) | ||
|
|
||
| (defn key-seqs | ||
| "Returns a collection of paths which would address all possible entries (using `get-in`) in data described by the schema" | ||
| "Returns a vec of unique key paths (key seqs) for `schema` and all subschemas | ||
| that will cover all possible entries in a data described by `schema` as well | ||
| as the `schema` itself." | ||
| [schema] | ||
| (when (map? schema) | ||
| (loop [paths [[]] | ||
| paths-and-schemas (with-paths [] schema)] | ||
| (if-let [{:keys [path schema]} (first paths-and-schemas)] | ||
| (recur (conj paths path) (concat (rest paths-and-schemas) | ||
| (with-paths path schema))) | ||
| paths)))) | ||
| (->> (-paths schema [] true) | ||
| (distinct) | ||
| (vec))) | ||
|
|
||
| ;; | ||
|
|
||
| (defn walk-with-path | ||
| "Identical to `clojure.walk/walk` except keeps track of the path through the data structure (as per `get-in`) | ||
| as it goes, calling `inner` and `outer` with two args: the path and form" | ||
| "Similar to the `schema-tools.walk/walk` except it keeps track of the `path` | ||
| through the data structure as it goes, calling `inner` and `outer` with two | ||
| args: the `path` and the `form`. It also does not preserve any metadata." | ||
| ([inner outer form] (walk-with-path inner outer [] form)) | ||
| ([inner outer path form] | ||
| (cond | ||
| (list? form) (outer path (apply list (map (partial inner path) form))) | ||
| (map-entry? form) | ||
| (outer path #?(:clj (clojure.lang.MapEntry. (inner path (key form)) (inner (conj path (key form)) (val form))) | ||
| :cljs (cljs.core/MapEntry. (inner path (key form)) (inner (conj path (key form)) (val form)) nil))) | ||
| (seq? form) (outer path (doall (map (partial inner path) form))) | ||
| (record? form) (outer path (reduce (fn [r x] (conj r (inner path x))) form form)) | ||
| (coll? form) (outer path (into (empty form) (map (partial inner path) form))) | ||
| (outer path [(inner path (key form)) | ||
| (inner (conj path (key form)) (val form))]) | ||
| (record? form) | ||
| (outer path (reduce (fn [r x] (conj r (inner path x))) form form)) | ||
| (list? form) | ||
| (outer path (apply list (map #(inner path %) form))) | ||
| (seq? form) | ||
| (outer path (doall (map #(inner path %) form))) | ||
| (coll? form) | ||
| (outer path (into (empty form) (map #(inner path %) form))) | ||
| :else (outer path form)))) | ||
|
Comment on lines
334
to
352
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Made the implementation of this function similar to
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I specifically checked that changing the order of clauses here does not affect the performance. |
||
|
|
||
| (defn postwalk-with-path [f path form] | ||
| (walk-with-path (partial postwalk-with-path f) f path form)) | ||
| (walk-with-path (fn [path form] (postwalk-with-path f path form)) | ||
| f | ||
| path | ||
| form)) | ||
|
|
||
| (defn prewalk-with-path [f path form] | ||
| (walk-with-path (partial prewalk-with-path f) (fn [_path form] form) path (f path form))) | ||
| (walk-with-path (fn [path form] (prewalk-with-path f path form)) | ||
| (fn [_path form] form) | ||
| path | ||
| (f path form))) | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is not possible since
schema-toolsdon't provide any utilities for traversing schemas withpath, while we heavily rely on this kind of traversal for both schemas and data. Other schema-related utilities were moved out of this namespace into themartian.schema-tools, leveragingschema-toolsto some degree.