 ;; coercion
 ;;
 
-(defrecord Coercer [decoder encoder validator explainer])
+(defprotocol Coercer
+  (-decode [this value])
+  (-encode [this value])
+  (-validate [this value])
+  (-explain [this value]))
 
 (defprotocol TransformationProvider
   (-transformer [this options]))


(defn- -coercer [schema type transformers f encoder opts]
  (if schema
    (let [->coercer (fn [t] (if t (->Coercer (m/decoder schema opts t)
                                             (m/encoder schema opts t)
                                             (m/validator schema opts)
                                             (m/explainer schema opts))))
          {:keys [formats default]} (transformers type)
          default-coercer (->coercer default)
          encode (or encoder (fn [value _format] value))

                            default-coercer (constantly default-coercer))]
      (if get-coercer
        (if (= f :decode)
          ;; decode: decode -> validate
          (fn [value format]
            (if-let [coercer (get-coercer format)]
              (let [transformed (-decode coercer value)]
                (if (-validate coercer transformed)
                  transformed
                  (let [error (-explain coercer transformed)]
                    (coercion/map->CoercionError
                      (assoc error :transformed transformed)))))
              value))
          ;; encode: decode -> validate -> encode
          (fn [value format]
            (if-let [coercer (get-coercer format)]
              (let [transformed (-decode coercer value)]
                (if (-validate coercer transformed)
                  (encode transformed format)
                  (let [error (-explain coercer transformed)]
                    (coercion/map->CoercionError
                      (assoc error :transformed transformed)))))
              value)))))))
