;;; `boot repl` to go
(set-env! :dependencies '[[prismatic/schema "1.1.0"]])
(require '[schema.core :as s])
;;; Atomic values
(s/validate java.math.BigDecimal 99999999999999.999M)
(s/validate #"\A\d{4}\z" "0123")
(s/validate #"\A\d{4}\z" "01234") ;Value does not match !!
(s/validate (s/enum :a :b) :a)
(s/validate (s/enum :a :b) :c) ;Value does not match !!
(s/validate (s/maybe s/Keyword) :a)
(s/validate (s/maybe s/Keyword) nil)
(s/validate (s/maybe s/Keyword) 1) ;Value does not match !!
(s/validate (s/eq :a) :a)
(s/validate (s/eq :a) :b) ;Value does not match !!
;;; Map
(s/validate {:a s/Num} {:a 1})
(s/validate {:a s/Num} {:a "1"}) ;Value does not match !!
;; succeed
(s/validate {:a s/Any} {:a 1})
(s/validate {:a s/Any} {:a "a"})
;; {:a missing-required-key} !!
(s/validate {:a s/Any} {})
(s/validate {(s/required-key :a) s/Any} {})
;; {:b disallowed-key} !!
(s/validate {:a s/Any} {:a 1 :b 2})
;; succeed
(s/validate {(s/optional-key :a) s/Any} {})
(s/validate {(s/optional-key :a) s/Any} {:a 1})
;;; Seq
(s/validate [s/Num] [1])
(s/validate [s/Num] [1 2])
(s/validate [s/Num] [1 2 "3"]) ;Value does not match !!
(def seq-schema
[(s/one s/Str "s1") ;=> implies the first element.
s/Num ;=> implies all follewing elements.
])
(s/validate seq-schema ["1" 2])
(s/validate seq-schema ["1" 2 3])
(s/validate seq-schema ["1" 2 3 4])
(s/validate seq-schema ["1" 2 3 4 "5"]) ;Value does not match !!
(def valid-seq-schema
[(s/one s/Str "s1")
(s/one s/Num "ns")
(s/optional s/Keyword "k")])
(s/validate valid-seq-schema ["1" 3 :k]) ;=> ["1" 3 :k]
(s/validate valid-seq-schema ["1" 3]) ;=> ["1" 3]
(def invalid-seq-schema
[(s/one s/Str "s1")
(s/optional s/Keyword "k")
(s/one s/Num "ns")])
(s/validate invalid-seq-schema ["1" :k 3])
;; [#schema.core.One{:schema java.lang.String, :optional? false, :name "s"}
;; #schema.core.One{:schema Keyword, :optional? true, :name "k"}
;; #schema.core.One{:schema java.lang.Number, :optional? false, :name "s2"}]
;; is not a valid sequence schema; a valid sequence schema consists of
;; zero or more `one` elements, followed by zero or more `optional`
;; elements, followed by an optional schema that will match the
;; remaining elements.
;;; Record
(s/defrecord SomeRec
[a :- Long
b :- [s/Str]])
(s/validate SomeRec (SomeRec. 1 ["a"]))
(s/validate SomeRec (SomeRec. "1" ["a"])) ;Value does not match !!
(s/validate SomeRec (SomeRec. 1 "a")) ;Value does not match !!
;;; AND
(let [schema (-> s/Int
(s/constrained #(<= 1 % 12) "月")
(s/constrained #(= 0 (mod % 3)) "3で割り切れる"))]
[(s/check schema 1)
(s/check schema 0)
(s/check schema 12)])
;; => [(not ("3で割り切れる" 1)) (not ("月" 0)) nil]
;;; OR
(let [schema (s/conditional integer? (-> s/Int (s/constrained #(<= 0 % 6) "曜日"))
keyword? (s/enum :* :sun :mon :tue :wed :thu :fri :sat))]
[(s/check schema 1)
(s/check schema :sun)
(s/check schema :foo)
(s/check schema "foo")])
;; => [nil nil (not (#{:wed :* :sat :tue :fri :sun :mon :thu} :foo)) (not (some-matching-condition? "foo"))]
;;; predicate
(let [schema (s/pred #(#{1 "1" :1} %) "1です")]
[(s/check schema 1)
(s/check schema "1")
(s/check schema :1)
(s/check schema "foo")])
;; => [nil nil nil (not ("1です" "foo"))]
;;; Function
(s/defn some-function :- s/Int
[a :- s/Num]
(+ a 1))
(s/with-fn-validation (some-function 1)) ;=> 2
(s/with-fn-validation (some-function "1")) ;=> Input to some-function does not match !!
(s/with-fn-validation (some-function 1.1)) ;=> Output of some-function does not match