├── .gitignore ├── deps.edn ├── README.md ├── LICENSE ├── test └── kwill │ └── malli_eql_test.clj └── src └── kwill └── malli_eql.clj /.gitignore: -------------------------------------------------------------------------------- 1 | .nrepl-port 2 | *.iml 3 | .idea 4 | target 5 | .cpcache 6 | *.pom 7 | -------------------------------------------------------------------------------- /deps.edn: -------------------------------------------------------------------------------- 1 | {:deps {metosin/malli {:mvn/version "0.8.4"}} 2 | :aliases {:build {:deps {io.github.seancorfield/build-clj {:git/tag "v0.7.0" :git/sha "5d2cb60"}} 3 | :ns-default build}}} 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # malli-eql 2 | 3 | A small, utility library unifying [Malli](https://github.com/metosin/malli) and [EQL](https://edn-query-language.org/). 4 | 5 | ## Dependency Information 6 | 7 | ```clojure 8 | dev.kwill/malli-eql {:mvn/version "0.1.3"} 9 | ``` 10 | 11 | ## Usage 12 | 13 | ```clojure 14 | (require '[kwill.malli-eql :as malli-eql]) 15 | 16 | (malli-eql/->eql 17 | [:map 18 | [:a int?] 19 | [:b [:map 20 | [:c int?]]]]) 21 | => [:a {:b [:c]}] 22 | ``` 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Kenny Williams 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /test/kwill/malli_eql_test.clj: -------------------------------------------------------------------------------- 1 | (ns kwill.malli-eql-test 2 | (:require 3 | [clojure.test :refer :all] 4 | [kwill.malli-eql :as malli-eql] 5 | [malli.core :as m] 6 | [malli.util :as mu])) 7 | 8 | (def registry (merge (m/default-schemas) (mu/schemas))) 9 | 10 | (deftest ->eql-test 11 | (is (= [] (malli-eql/->eql [:map])) 12 | "empty map is empty eql") 13 | (is (= [:a] (malli-eql/->eql [:map [:a int?]])) 14 | "simple map") 15 | (is (= '[(:a {:foo "bar"})] 16 | (malli-eql/->eql [:map [:a {:foo "bar"} int?]])) 17 | "simple map with options") 18 | (is (= [:a] 19 | (malli-eql/->eql [:map [:a {:foo "bar"} int?]] 20 | {::malli-eql/get-parameters (constantly nil)})) 21 | "simple map, ignoring parameters") 22 | (is (= [:aa 23 | :a 24 | {:b [:c]} 25 | {:seq [:c]} 26 | :d] 27 | (malli-eql/->eql 28 | [:or 29 | [:map [:aa int?]] 30 | [:merge 31 | [:map 32 | [:a int?] 33 | [:b [:map 34 | [:c int?]]] 35 | [:seq [:map 36 | [:c int?]]]] 37 | [:map 38 | [:d int?]]]] 39 | {:registry registry})) 40 | "simple map")) 41 | -------------------------------------------------------------------------------- /src/kwill/malli_eql.clj: -------------------------------------------------------------------------------- 1 | (ns kwill.malli-eql 2 | (:require 3 | [malli.core :as m])) 4 | 5 | (def default-hierarchy 6 | (-> (make-hierarchy) 7 | (derive :merge :map) 8 | (derive :map-of :map) 9 | (derive :vector :sequential) 10 | (derive :catn :sequential))) 11 | 12 | (defn default-*-like? 13 | [?schema opts schema-type] 14 | (isa? default-hierarchy (m/type ?schema opts) schema-type)) 15 | 16 | (defn default-map-like? 17 | [?schema opts] 18 | (default-*-like? ?schema opts :map)) 19 | 20 | (defn default-sequential-like? 21 | [?schema opts] 22 | (default-*-like? ?schema opts :sequential)) 23 | 24 | (defn ->eql 25 | "Converts a schema to EQL. Optional takes an options map that gets passed to 26 | all Malli API calls. Additionally, you can pass some parameters unique to this 27 | library. 28 | ::map-like? - Returns true if a schema can be treated like a :map type schema. 29 | ::sequential-like? - Returns true if a schema can be treated like a 30 | :sequential type schema. 31 | ::get-parameters - Function passed a map schema's properties are returns the 32 | EQL parameters to include. By default, uses the Malli properties map, if 33 | available. 34 | ::defaultf - Function called with the schema and options map when no builtin 35 | handler can process the schema type. Returns EQL for the schema." 36 | ([?schema] (->eql ?schema nil)) 37 | ([?schema {::keys [map-like? sequential-like? get-parameters defaultf] 38 | :or {map-like? default-map-like? 39 | sequential-like? default-sequential-like? 40 | get-parameters :properties 41 | defaultf (constantly nil)} 42 | :as options}] 43 | (let [f (fn convert [?schema] 44 | (let [s (m/deref (m/schema ?schema options)) 45 | schema-type (m/type s options)] 46 | (cond 47 | (map-like? s options) 48 | (mapv (fn [[k properties v]] 49 | (let [parameters (get-parameters {:properties properties}) 50 | k-eql (if parameters 51 | (list k parameters) 52 | k)] 53 | (if-let [sub (convert v)] 54 | {k-eql sub} 55 | k-eql))) (m/children s options)) 56 | (sequential-like? s options) 57 | (convert (first (m/children s options))) 58 | (contains? #{:or} schema-type) 59 | (into [] (mapcat (fn [child-schema] (convert child-schema))) (m/children s options)) 60 | :else (defaultf ?schema options))))] 61 | (f ?schema)))) 62 | 63 | ;; README Examples 64 | (comment 65 | (->eql 66 | [:map 67 | [:a int?] 68 | [:b [:map 69 | [:c int?]]]]) 70 | ) 71 | 72 | (comment 73 | (def registry (merge (m/default-schemas) (malli.util/schemas))) 74 | (m/children [:or [:sequential [:map [:a int?]]] [:sequential [:map [:a int?]]]]) 75 | (m/deref [:sequential [:map [:a int?]]]) 76 | (m/type [:or int?]) 77 | (->eql 78 | [:or 79 | [:map [:aa int?]] 80 | [:merge 81 | [:map 82 | [:a int?] 83 | [:b [:map 84 | [:c int?]]] 85 | [:seq [:map 86 | [:c int?]]]] 87 | [:map 88 | [:d int?]]]] 89 | {:registry registry}) 90 | ) 91 | --------------------------------------------------------------------------------