Укажите коллекцию, которая должна иметь ровно одно из значений - PullRequest
1 голос
/ 11 марта 2019

Имея эту карту:

{:id 5
 :fields [{:field :a :val 1}
          {:field :c :val 3}
          {:field :b :val 2}
          {:field :d :val 4}
          ...]}

и отдельные спецификации для каждого значения поля, такие как (s/def ::field-a ...), (s/def ::field-b ...), (s/def ::field-c ...),

как мне указать, чтобы :fields на карте содержал каждое (и ровно одно) из {:field a ...}, {:field b ...}, {:field c ...} (без учета порядка) и, возможно, другие значения?

1 Ответ

2 голосов
/ 11 марта 2019

Да, это немного объемно, я знаю, но в любом случае:

(s/def ::id int?)
(s/def ::val int?)
(s/def ::field keyword?)
(s/def ::some string?)

(s/def ::abstract-field (s/keys :req-un [::field ::val]))

(s/def ::field-a (s/and (s/keys :req-un [::field ::val]) #(= (:field %) :a)))
(s/def ::field-b (s/and (s/keys :req-un [::field ::val]) #(= (:field %) :b)))
(s/def ::field-c (s/and (s/keys :req-un [::field ::val]) #(= (:field %) :c)))
(s/def ::field-d (s/and (s/keys :req-un [::field ::val]) #(= (:field %) :d)))

(s/def ::fields (s/and
                    (s/+ (s/alt :required (s/or :a ::field-a
                                                :b ::field-b
                                                :c ::field-c
                                                :d ::field-d)
                                :rest ::abstract-field))
                    (fn [x] (= [:a :b :c :d]
                               (into [] (sort (map (comp first second) (filter #(= :required (first %)) x))))))))

(let [x1 [{:field :b :val 2}
          {:field :a :val 1}
          ;{:field :a :val 1}
          {:field :d :val 4}
          {:field :e :val 4}
          {:field :c :val 3}
          ]]
    (s/explain ::fields x1)
    (s/conform ::fields x1))
...