|
1 | 1 | (ns malli.json-schema.parse |
2 | 2 | (:require [malli.core :as m] |
| 3 | + [malli.util :as mu] |
3 | 4 | [clojure.string :as str])) |
4 | 5 |
|
5 | 6 | ;; Utility Functions |
|
19 | 20 | (cond |
20 | 21 | (-keys :type) (type->malli js-schema) |
21 | 22 |
|
| 23 | + (-keys :enum) (into [:enum] |
| 24 | + (:enum js-schema)) |
| 25 | + |
| 26 | + (-keys :const) [:enum (:const js-schema)] |
| 27 | + |
22 | 28 | ;; Aggregates |
23 | 29 | (-keys :oneOf) |
24 | 30 | (into |
|
44 | 50 | (-keys :$ref) ($ref (:$ref js-schema)) |
45 | 51 |
|
46 | 52 | :else |
47 | | - (throw (ex-info "Not supported" {:js-schema js-schema}))))) |
| 53 | + (throw (ex-info "Not supported" {:json-schema js-schema |
| 54 | + :reason ::schema-type}))))) |
48 | 55 |
|
49 | 56 | (defn properties->malli [{:keys [required]} [k v]] |
50 | 57 | (cond-> [k] |
|
53 | 60 |
|
54 | 61 | (defn object->malli [v] |
55 | 62 | (let [required (into #{} |
| 63 | + ;; TODO Should use the same fn as $ref |
56 | 64 | (map keyword) |
57 | 65 | (:required v)) |
58 | 66 | closed? (false? (:additionalProperties v))] |
|
62 | 70 | (map (partial properties->malli {:required required})) |
63 | 71 | (:properties v)))))) |
64 | 72 |
|
65 | | -(defmethod type->malli "string" [p] string?) |
66 | | -(defmethod type->malli "integer" [p] int?) |
67 | | -(defmethod type->malli "number" [p] |
68 | | - ;; TODO support decimal/double |
69 | | - number?) |
| 73 | +(defmethod type->malli "string" [{:keys [pattern minLength maxLength enum]}] |
| 74 | + ;; `format` metadata is deliberately not considered. |
| 75 | + ;; String enums are stricter, so they're also implemented here. |
| 76 | + (cond |
| 77 | + pattern [:re pattern] |
| 78 | + enum [:and |
| 79 | + :string |
| 80 | + (into [:enum] enum)] |
| 81 | + [:string (cond-> {} |
| 82 | + minLength (assoc :min minLength) |
| 83 | + maxLength (assoc :max maxLength))])) |
| 84 | + |
| 85 | +(defmethod type->malli "integer" [{:keys [minimum maximum exclusiveMinimum exclusiveMaximum multipleOf] |
| 86 | + :or {minimum Integer/MIN_VALUE |
| 87 | + maximum Integer/MAX_VALUE}}] |
| 88 | + ;; On draft 4, exclusive{Minimum,Maximum} is a boolean. |
| 89 | + ;; TODO Decide on whether draft 4 will be supported |
| 90 | + ;; TODO Implement exclusive{Minimum,Maximum} support |
| 91 | + ;; TODO Implement multipleOf support |
| 92 | + ;; TODO Wrap, when it makes sense, the values below with range checkers, i.e. [:< maximum] |
| 93 | + ;; TODO extract ranges logic and reuse with number |
| 94 | + (cond |
| 95 | + (pos? minimum) pos-int? |
| 96 | + (neg? maximum) neg-int? |
| 97 | + :else int?)) |
| 98 | + |
| 99 | +(defmethod type->malli "number" [p] number?) |
70 | 100 | (defmethod type->malli "boolean" [p] boolean?) |
71 | 101 | (defmethod type->malli "null" [p] nil?) |
72 | 102 | (defmethod type->malli "object" [p] (object->malli p)) |
|
76 | 106 | (map schema->malli) |
77 | 107 | items) |
78 | 108 | (map? items) [:vector (schema->malli items)] |
79 | | - :else (throw (ex-info "Can't produce malli schema" {:p p}))))) |
| 109 | + :else (throw (ex-info "Not Supported" {:json-schema p |
| 110 | + :reason ::array-items}))))) |
80 | 111 |
|
81 | 112 | (defn json-schema-document->malli [obj] |
82 | 113 | [:schema {:registry (into {} |
|
0 commit comments