-
Notifications
You must be signed in to change notification settings - Fork 204
/
error.cljc
136 lines (122 loc) · 5.31 KB
/
error.cljc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
(ns malli.error
(:require [malli.core :as m]))
(def default-errors
{::unknown {:error/message {:en "unknown error"}}
::m/missing-key {:error/message {:en "missing required key"}}
::m/invalid-type {:error/message {:en "invalid type"}}
'any? {:error/message {:en "should be any"}}
'some? {:error/message {:en "shoud be some"}}
'number? {:error/message {:en "should be number"}}
'integer? {:error/message {:en "should be integer"}}
'int? {:error/message {:en "should be int"}}
'pos-int? {:error/message {:en "should be positive int"}}
'neg-int? {:error/message {:en "should be negative int"}}
'nat-int? {:error/message {:en "should be non-negative int"}}
'float? {:error/message {:en "should be float"}}
'double? {:error/message {:en "should be double"}}
'boolean? {:error/message {:en "should be boolean"}}
'string? {:error/message {:en "should be string"}}
'ident? {:error/message {:en "should be ident"}}
'simple-ident? {:error/message {:en "should be simple ident"}}
'qualified-ident? {:error/message {:en "should be qualified ident"}}
'keyword? {:error/message {:en "should be keyword"}}
'simple-keyword? {:error/message {:en "should be simple keyword"}}
'qualified-keyword? {:error/message {:en "should be qualified keyword"}}
'symbol? {:error/message {:en "should be symbol"}}
'simple-symbol? {:error/message {:en "should be simple symbol"}}
'qualified-symbol? {:error/message {:en "should be qualified symbol"}}
'uuid? {:error/message {:en "should be uuid"}}
'uri? {:error/message {:en "should be uri"}}
#?@(:clj ['decimal? {:error/message {:en "should be decimal"}}])
'inst? {:error/message {:en "should be inst"}}
'seqable? {:error/message {:en "should be seqable"}}
'indexed? {:error/message {:en "should be indexed"}}
'map? {:error/message {:en "should be map"}}
'vector? {:error/message {:en "should be vector"}}
'list? {:error/message {:en "should be list"}}
'seq? {:error/message {:en "should be seq"}}
'char? {:error/message {:en "should be char"}}
'set? {:error/message {:en "should be set"}}
'nil? {:error/message {:en "should be nil"}}
'false? {:error/message {:en "should be false"}}
'true? {:error/message {:en "should be true"}}
'zero? {:error/message {:en "should be zero"}}
#?@(:clj ['rational? {:error/message {:en "should be rational"}}])
'coll? {:error/message {:en "should be coll"}}
'empty? {:error/message {:en "should be empty"}}
'associative? {:error/message {:en "should be associative"}}
'sequential? {:error/message {:en "should be sequential"}}
#?@(:clj ['ratio? {:error/message {:en "should be ratio"}}])
#?@(:clj ['bytes? {:error/message {:en "should be bytes"}}])})
(defn- -maybe-localized [x locale]
(if (map? x) (get x locale) x))
(defn- -message [error x locale opts]
(or (if-let [fn (-maybe-localized (:error/fn x) locale)] ((m/eval fn) error opts))
(-maybe-localized (:error/message x) locale)))
(defn- -ensure [x k]
(if (sequential? x)
(let [size' (count x)]
(if (> k size') (into (vec x) (repeat (- (inc k) size') nil)) x))
x))
(defn- -get [x k]
(if (set? x) (-> x vec (get k)) (get x k)))
(defn- -put [x k v]
(if (set? x) (conj x v) (assoc x k v)))
(defn- -assoc-in [acc value [p & ps] error]
(cond
p (let [acc' (-ensure (or acc (empty value)) p)
value' (if ps (-assoc-in (-get acc p) (-get value p) ps error) error)]
(-put acc' p value'))
(map? value) (recur acc value [:malli/error] error)
acc acc
:else error))
(defn- -path [{:keys [schema]}
{:keys [locale default-locale]
:or {default-locale :en}}]
(let [properties (m/properties schema)]
(or (-maybe-localized (:error/path properties) locale)
(-maybe-localized (:error/path properties) default-locale))))
;;
;; public api
;;
(defn error-path
([error]
(error-path error nil))
([error opts]
(into (:in error) (-path error opts))))
(defn error-message
([error]
(error-message error nil))
([{:keys [schema type] :as error}
{:keys [errors locale default-locale]
:or {errors default-errors
default-locale :en} :as opts}]
(or (-message error (m/properties schema) locale opts)
(-message error (errors (m/name schema)) locale opts)
(-message error (errors type) locale opts)
(-message error (m/properties schema) default-locale opts)
(-message error (errors (m/name schema)) default-locale opts)
(-message error (errors type) default-locale opts)
(-message error (errors ::unknown) locale opts)
(-message error (errors ::unknown) default-locale opts))))
(defn with-error-message
([error]
(with-error-message error nil))
([error opts]
(assoc error :message (error-message error opts))))
(defn with-error-messages
([explanation]
(with-error-messages explanation nil))
([explanation {f :wrap :or {f identity} :as opts}]
(update explanation :errors (partial map #(f (with-error-message % opts))))))
(defn humanize
([explanation]
(humanize explanation nil))
([{:keys [value errors]} {f :wrap :or {f identity} :as opts}]
(if errors
(if (coll? value)
(reduce
(fn [acc error]
(-assoc-in acc value (error-path error opts) (f (with-error-message error opts))))
nil errors)
(f (with-error-message (first errors) opts))))))