Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Idea: dev debug printing for schema creation errors #1089

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions src/malli/core.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,8 @@

(defn -fail!
([type] (-fail! type nil))
([type data] (throw (-exception type data))))
([type data] (throw (-exception type data)))
([type data options] (throw (-exception type data))))

(defn -safe-pred [f] #(try (boolean (f %)) (catch #?(:clj Exception, :cljs js/Error) _ false)))

Expand Down Expand Up @@ -267,14 +268,14 @@
(when-some [p (some-> registry (mr/-schema (c/type ?schema)))]
(when (schema? ?schema)
(when (= p (-parent ?schema))
(-fail! ::infinitely-expanding-schema {:schema ?schema})))
(-fail! ::infinitely-expanding-schema {:schema ?schema} options)))
(-into-schema p nil [?schema] options)))))

(defn- -lookup! [?schema ?form f rec options]
(or (and f (f ?schema) ?schema)
(if-let [?schema (-lookup ?schema options)]
(cond-> ?schema rec (recur ?form f rec options))
(-fail! ::invalid-schema {:schema ?schema, :form ?form}))))
(-fail! ::invalid-schema {:schema ?schema, :form ?form} options))))

(defn -properties-and-options [properties options f]
(if-let [r (:registry properties)]
Expand Down
109 changes: 102 additions & 7 deletions src/malli/dev.clj
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,113 @@
([text] (-log! text (pretty/-printer)))
([text printer] (pretty/-log! text printer)))

(def ^:private ^:dynamic *forms* nil)

(defn -capture-fail!
([] (-capture-fail! nil))
([{:keys [report] :or {report (pretty/reporter)}}]
(alter-var-root
#'m/-fail!
(fn [f] (-> (fn -fail!
([type] (-fail! type nil))
([type data] (let [e (m/-exception type data)]
(report type data)
(throw e))))
(with-meta {::original f}))))))
(fn [f]
(-> (fn -fail!
([type] (-fail! type nil))
([type data] (let [e (m/-exception type data)]
(report type data)
(throw e)))
([type data options]
(let [data (assoc data ::forms (some-> *forms* deref))
e (m/-exception type data)]
(report type data)
(throw e))))
(with-meta {::original (::original (meta f) f)}))))))

(defn -uncapture-fail! []
(alter-var-root #'m/-fail! (fn [f] (-> f meta ::original (or f)))))
(alter-var-root #'m/-fail! (fn [f] (::original (meta f) f))))

(def ^:dynamic *level* nil)

(defn- ensure-forms [f]
(binding [*forms* (or *forms* (atom []))
*level* (or (some-> *level* inc) 0)]
(f *forms* *level*)))

(defn -capture-schema-ctors! []
(alter-var-root #'m/schema
(fn [f]
(let [f (::original (meta f) f)]
(fn this
([?schema] (this ?schema nil))
([?schema options]
(ensure-forms
(fn [forms level]
(swap! forms conj {:level level :form ?schema})
(f ?schema options))))))))
(alter-var-root #'m/into-schema
(fn [f]
(let [f (::original (meta f) f)]
(fn this
([type properties children] (this type properties children nil))
([type properties children options]
(ensure-forms
(fn [forms level]
(swap! forms conj {:level level :form [type properties children]})
(f type properties children options))))))))
(alter-var-root #'m/-lookup!
(fn [-lookup!]
(let [-lookup! (::original (meta -lookup!) -lookup!)]
(fn [?schema ?form f rec options]
(ensure-forms
(fn [forms level]
(swap! forms conj {:level level :form ?schema})
(-lookup! ?schema ?form f rec options)))))))
(alter-var-root #'m/-lookup
(fn [f]
(let [f (::original (meta f) f)]
(fn [?schema options]
(ensure-forms
(fn [forms level]
(swap! forms conj {:level level :form ?schema})
(f ?schema options))))))))

(defn -uncapture-schema-ctors! []
(alter-var-root #'m/-lookup (fn [f] (::original (meta f) f)))
(alter-var-root #'m/-lookup! (fn [f] (::original (meta f) f)))
(alter-var-root #'m/into-schema (fn [f] (::original (meta f) f)))
(alter-var-root #'m/schema (fn [f] (::original (meta f) f))))

(comment
(-capture-fail!)
(-capture-schema-ctors!)
(m/schema [:map [:a [:tuple nil nil]]])
;; -- Schema Creation Error ---------------------------------------- Thread:1583 --
;;
;; Invalid Schema
;;
;; nil
;;
;; Surrounding Syntax
;;
;; [:map [:a [:tuple nil nil]]]
;; :map
;; :map
;; [#IntoSchema{:type :map} nil [[:a [:tuple nil nil]]]]
;; #IntoSchema{:type :map}
;; [:tuple nil nil]
;; :tuple
;; :tuple
;; [#IntoSchema{:type :tuple} nil [nil]]
;; #IntoSchema{:type :tuple}
;; nil
;; nil
;; nil
;;
;; More information
;;
;; https://cljdoc.org/d/metosin/malli/CURRENT
;;
;; --------------------------------------------------------------------------------

)

;;
;; Public API
Expand All @@ -33,6 +126,7 @@
(remove-watch @#'m/-function-schemas* ::watch)
(->> (mi/unstrument!) (count) (format "unstrumented %d function vars") (-log!))
(clj-kondo/save! {})
(-uncapture-schema-ctors!)
(-uncapture-fail!)
(-log! "dev-mode stopped"))

Expand All @@ -45,6 +139,7 @@
([options]
(with-out-str (stop!))
(-capture-fail! options)
(-capture-schema-ctors!)
(mi/collect! {:ns (all-ns)})
(let [watch (bound-fn [_ _ old new]
(->> (for [[n d] (:clj new)
Expand Down
12 changes: 10 additions & 2 deletions src/malli/dev/pretty.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -92,17 +92,25 @@
(v/-block "Function Schema" (v/-visit schema printer) printer) :break :break
(v/-block "More information" (v/-link "https://cljdoc.org/d/metosin/malli/CURRENT/doc/function-schemas" printer) printer)]})

(defmethod v/-format ::m/invalid-ref [_ {:keys [ref]} printer]
(defmethod v/-format ::m/invalid-ref [_ {:malli.dev/keys [forms] :keys [ref]} printer]
{:body [:group
(v/-block "Invalid Reference" (v/-visit [:ref ref] printer) printer) :break :break
(v/-block "Reason" (-ref-text printer) printer) :break :break
(v/-block "More information" (v/-link "https://cljdoc.org/d/metosin/malli/CURRENT" printer) printer)]})

(defmethod v/-format ::m/invalid-schema [_ {:keys [schema form]} printer]
(defmethod v/-format ::m/invalid-schema [_ {:malli.dev/keys [forms] :keys [schema form]} printer]
(let [proposals (seq (me/-most-similar-to #{schema} schema (set (keys (mr/schemas m/default-registry)))))]
{:title "Schema Creation Error"
:body [:group
(v/-block "Invalid Schema" (v/-visit form printer) printer) :break :break
(when forms
[:group
(v/-block "Surrounding Syntax"
(interpose :break (map (fn [{:keys [form level]}]
[:align level (v/-text (pr-str form) printer)])
forms))
printer)
:break :break])
(when proposals
[:group (v/-block "Did you mean" (->> (for [proposal proposals] (v/-visit proposal printer)) (interpose :break)) printer)
:break :break])
Expand Down
Loading