https://github.com/kennyjwilli/specalli
Convert Clojure Specs to Malli schemas at runtime.
https://github.com/kennyjwilli/specalli
clojure-spec malli
Last synced: about 2 months ago
JSON representation
Convert Clojure Specs to Malli schemas at runtime.
- Host: GitHub
- URL: https://github.com/kennyjwilli/specalli
- Owner: kennyjwilli
- License: mit
- Created: 2022-09-28T18:15:48.000Z (over 2 years ago)
- Default Branch: main
- Last Pushed: 2022-09-28T18:25:56.000Z (over 2 years ago)
- Last Synced: 2025-03-31T18:52:01.363Z (3 months ago)
- Topics: clojure-spec, malli
- Language: Clojure
- Homepage:
- Size: 26.4 KB
- Stars: 4
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# specalli
Convert Clojure Specs to Malli schemas at runtime.
## Installation
```clojure
dev.kwill/specalli {:mvn/version "1.0.1"}
```## Usage
The library exposes a single API namespace `kwill.specalli` with the `mallize` function. The function takes a Clojure
Spec form and returns a Malli schema. A handful of examples are presented below. A more comprehensive set of examples
can be found in the test namespace `kwill.specalli-test`.```clojure
(require '[kwill.specalli :as specalli])(specalli/mallize `double?)
=> :double(specalli/mallize (s/double-in :min 1 :max 10))
=> [:double {:min 1, :max 10}](specalli/mallize
(s/cat
:int (s/+ int?)
:str (s/? string?)
:m (s/* map?)))
=> [:catn [:int [:+ :int]] [:str [:? :string]] [:m [:* :map]]]
```## Caveats
### Missing Coverage
- `s/keys*`
- `s/&`### Merging Maps
Malli does not support merging `:map` and `:multi` yet ([see #493](https://github.com/metosin/malli/issues/493)). If you
pass a Spec that tries to merge those, the library will return a schema that appears to support it, but it will not
work.### Runtime Conversion and Usage in ClojureScript
Certain Spec definitions are difficult to impossible to convert to a Schema at runtime without resolving a symbol or
evaluating a form. As such, dynamic conversions of those Specs are not possible in ClojureScript. A comprehensive list
of forms requiring symbol resolution or `eval` capabilities are listed below.#### Symbol or form in Spec option value
In some places, Spec will accept a symbol as the input to a Spec's option value. For example, you can use a top-level
var as the `:max` value with `s/double-in`.```clojure
(def the-max 10)
(s/double-in :max the-max)
```Or you can even use a form.
```clojure
(s/double-in :max (* 1024 8))
```Unfortunately, we need to pass the actual value to the Malli schema option value. This requires us to resolve or eval
the option value. Specs defined with a var or form will not work in ClojureScript.### Enumeration via symbol
Spec supports specifying an enumeration by defining a spec as a var pointing to a set literal. To convert a Spec at
runtime, we must know if the symbol returned by the Spec's form is a set. We cannot do this dynamic resolution in
ClojureScript, so we fall back to a `:fn` schema.```clojure
(def enum-set #{"a"})
=> #'user/enum-set(s/def ::enum my-set)
=> :user/enum(s/form ::enum)
=> user/my-set;; In CLJS only:
(specalli/mallize ::enum)
=> [:fn #{"a"}]
```#### `s/multi-spec`
Multispecs are defined using multimethods which are defined at runtime. We need to resolve the multimethod symbol to a
callable function, enumerate its methods, and execute the multimethod for each dispatch to create the `:multi` schema.