-
Notifications
You must be signed in to change notification settings - Fork 1
/
humanize.clj
96 lines (73 loc) · 2.88 KB
/
humanize.clj
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
(ns common-clj.humanize
(:require [clojure.core.match :refer [match]]
[schema.core :as s]
[common-clj.lib.utils :refer [map-vals vectorize]]
[schema.utils :refer [named-error-explain validation-error-explain]])
(:import [schema.utils NamedError ValidationError]))
(defn humanize
[x]
(let [String java.lang.String
Number java.lang.Number
LocalDate java.time.LocalDate]
(match
x
['not ['pos? num]]
(str num " is not positive.")
['not ['instance? Number not-num]]
(str "'" not-num "' is not a number.")
['not ['instance? String not-string]]
(str "'" not-string "' is not a string.")
['not ['instance? LocalDate not-local-date]]
(str "'" not-local-date "' is not a valid date")
['not [['between min max] given]]
(str "The value must be between " min " and " max ". But given: " given)
['not [(enum :guard set?) given]]
(str "The value must be one of the following: " enum ". But given: " given)
['not ['pos-int? num]]
(str num " is not a positive integer.")
['not ['valid-email email]]
(str email " is not a valid email.")
['named inner name]
(humanize inner)
:else
(str x))))
(defn explain
([errors] (explain errors identity))
([errors translator]
(cond
(map? errors)
(map-vals #(explain % translator) errors)
(or (seq? errors)
(coll? errors))
(mapv #(explain % translator) errors)
(instance? NamedError errors)
(translator (vectorize (named-error-explain errors)))
(instance? ValidationError errors)
(translator (vectorize (validation-error-explain errors)))
:else
errors)))
(defn check
([schema x] (check schema x identity))
([schema x translator]
(some-> (s/check schema x) (explain translator))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Example usage:
#_(s/defschema PosNum
(s/both s/Num (s/pred pos? 'pos?)))
#_(def my-map-schema {:x PosNum :y s/Num :z {:zz s/Str}})
#_(check {:a s/Str} {:a 1 :b 2} humanize);; => "4 is not a string but it should be"
#_(check PosNum -1 humanize) ;; => -1 is not positive but it should be
#_(check my-map-schema {:x 1 :y -2 :z {:zz 3}} humanize) ;; => {:z {:zz "'3' is not a string but it should be."}}
;; Or without translator:
#_(check PosNum 0);; => [not [pos? 0]]
#_(check PosNum "fo");; => [not [instance? java.lang.Number 0]]
;; Or an example of a custom predicate which carries
;; the data over to name of the predicate:
#_(defn between
"showing that we can store arbitrary information about the schema in the name"
[min max]
(s/both s/Num
(s/pred #(<= min % max)
`(~'between ~min ~max))))
#_(check (between 3 6) 7 humanize) ;; => "The value must be between 3 and 6. But given: 8"
#_(explain (s/check {:a s/Str :b s/Int} {:a 0 :b "a"}))