ソースを参照

Move soft type checking to a separate namespace.

This will also help sharing the code with clojurescript.
Lucas Stadler 12 年 前
コミット
ee50681828
共有2 個のファイルを変更した72 個の追加70 個の削除を含む
  1. 0 70
      clj/clarity/clarity.clj
  2. 72 0
      clj/clarity/clarity/types.clj

+ 0 - 70
clj/clarity/clarity.clj

@ -79,73 +79,3 @@
79 79
                  (make-input val)])
80 80
               optional)
81 81
          '("}")))
82
83
(def datomic-attr-type
84
  '(HMap :mandatory {:db/id (Value "#db/id[db.part/db]")
85
                     :db/ident Keyword
86
                     :db/valueType (U (Value :db.type/keyword)
87
                                      (Value :db.type/string)
88
                                      (Value :db.type/boolean)
89
                                      (Value :db.type/long)
90
                                      (Value :db.type/bigint)
91
                                      (Value :db.type/float)
92
                                      (Value :db.type/double)
93
                                      (Value :db.type/bigdec)
94
                                      (Value :db.type/ref)
95
                                      (Value :db.type/instant)
96
                                      (Value :db.type/uuid)
97
                                      (Value :db.type/uri)
98
                                      (Value :db.type/bytes))
99
                     :db/cardinality (U (Value :db.cardinality/one)
100
                                        (Value :db.cardinality/many))
101
                     :db.install/_attribute (Value :db.part/db)}
102
         :optional {:db/doc String
103
                    :db/unique (Option (U (Value :db.unique/value)
104
                                          (Value :db.unique/identity)))
105
                    :db/index Boolean ; what about defaults?
106
                    :db/fulltext Boolean
107
                    :db/isComponent (Value :db.type/ref) ; FIXME: needs a custom type (dynamic even, because only valid refs should be allowed)
108
                    :db/noHistory Boolean}))
109
110
(defmulti friendly-check (fn [form type]
111
                           (if (seq? type)
112
                             (first type)
113
                             type)))
114
115
(defmethod friendly-check 'Value [val [_ expected]]
116
  (if (= val expected)
117
    true
118
    {:error (str "Expected value " expected ", but found " val ".")}))
119
120
(defmacro def-friendly-check [sym pred]
121
  `(defmethod friendly-check ~sym [val# _#]
122
     (if (~pred val#)
123
       true
124
       {:error (str "Expected value of type " (name ~sym) ", but got value " val# ".")})))
125
126
(def-friendly-check 'String string?)
127
(def-friendly-check 'Keyword keyword?)
128
(def-friendly-check 'Boolean #(instance? Boolean %))
129
130
(defmethod friendly-check 'U [val [_ & types]]
131
  (if (some true? (map (partial friendly-check val) types))
132
    true
133
    {:error (str "Expected one of " types ", but found " val ".")}))
134
135
(defn friendly-check-keys [m mandatory _]
136
  (map (fn [[key type]]
137
         (if-let [val (get m key)]
138
           (let [check (friendly-check val type)]
139
             (if (true? check)
140
               true
141
               {key (:error check)}))
142
           {key (str "No value found, but expected one of type " type ".")}))
143
       mandatory))
144
145
(defmethod friendly-check 'HMap [val [_ & {:keys [mandatory optional]}]]
146
  (if (map? val)
147
    (let [key-checks (friendly-check-keys val mandatory optional)]
148
      (if (every? true? key-checks)
149
        true
150
        {:error (filter (comp not true?) key-checks)}))
151
    {:error (str "Expected value of type Map, but got value " val ".")}))

+ 72 - 0
clj/clarity/clarity/types.clj

@ -0,0 +1,72 @@
1
(ns clarity.types
2
  "Soft type checks using the core.typed type-syntax.")
3
4
(defmulti friendly-check (fn [form type]
5
                           (if (seq? type)
6
                             (first type)
7
                             type)))
8
9
(defmethod friendly-check 'Value [val [_ expected]]
10
  (if (= val expected)
11
    true
12
    {:error (str "Expected value " expected ", but found " val ".")}))
13
14
(defn def-friendly-check [sym pred]
15
  (defmethod friendly-check sym [val _]
16
    (if (pred val)
17
      true
18
      {:error (str "Expected value of type " (name sym) ", but got value " val ".")})))
19
20
(def-friendly-check 'String string?)
21
(def-friendly-check 'Keyword keyword?)
22
(def-friendly-check 'Boolean #(or (true? %1) (false? %1)))
23
24
(defmethod friendly-check 'U [val [_ & types]]
25
  (if (some true? (map (partial friendly-check val) types))
26
    true
27
    {:error (str "Expected one of " types ", but found " val ".")}))
28
29
(defn friendly-check-keys [m mandatory _]
30
  (map (fn [[key type]]
31
         (if-let [val (get m key)]
32
           (let [check (friendly-check val type)]
33
             (if (true? check)
34
               true
35
               {key (:error check)}))
36
           {key (str "No value found, but expected one of type " type ".")}))
37
       mandatory))
38
39
(defmethod friendly-check 'HMap [val [_ & {:keys [mandatory optional]}]]
40
  (if (map? val)
41
    (let [key-checks (friendly-check-keys val mandatory optional)]
42
      (if (every? true? key-checks)
43
        true
44
        {:error (filter (comp not true?) key-checks)}))
45
    {:error (str "Expected value of type Map, but got value " val ".")}))
46
47
(def datomic-attr-type
48
  '(HMap :mandatory {:db/id (Value "#db/id[db.part/db]")
49
                     :db/ident Keyword
50
                     :db/valueType (U (Value :db.type/keyword)
51
                                      (Value :db.type/string)
52
                                      (Value :db.type/boolean)
53
                                      (Value :db.type/long)
54
                                      (Value :db.type/bigint)
55
                                      (Value :db.type/float)
56
                                      (Value :db.type/double)
57
                                      (Value :db.type/bigdec)
58
                                      (Value :db.type/ref)
59
                                      (Value :db.type/instant)
60
                                      (Value :db.type/uuid)
61
                                      (Value :db.type/uri)
62
                                      (Value :db.type/bytes))
63
                     :db/cardinality (U (Value :db.cardinality/one)
64
                                        (Value :db.cardinality/many))
65
                     :db.install/_attribute (Value :db.part/db)}
66
         :optional {:db/doc String
67
                    :db/unique (Option (U (Value :db.unique/value)
68
                                          (Value :db.unique/identity)))
69
                    :db/index Boolean ; what about defaults?
70
                    :db/fulltext Boolean
71
                    :db/isComponent (Value :db.type/ref) ; FIXME: needs a custom type (dynamic even, because only valid refs should be allowed)
72
                    :db/noHistory Boolean}))