From b554920282613cab0595830c474b812e30301b40 Mon Sep 17 00:00:00 2001 From: Tommi Reiman Date: Fri, 29 Dec 2023 02:07:29 +0200 Subject: [PATCH] better errors --- src/malli/dev/pretty.cljc | 14 +++++------ src/malli/dev/virhe.cljc | 53 +++++++++++++++++++++++---------------- 2 files changed, 39 insertions(+), 28 deletions(-) diff --git a/src/malli/dev/pretty.cljc b/src/malli/dev/pretty.cljc index 288a5000d..cb5494e5c 100644 --- a/src/malli/dev/pretty.cljc +++ b/src/malli/dev/pretty.cljc @@ -26,7 +26,7 @@ ;; formatters ;; -(defmethod v/-format ::m/explain [_ _ {:keys [schema] :as explanation} printer] +(defmethod v/-format ::m/explain [_ {:keys [schema] :as explanation} printer] {:body [:group (v/-block "Value:" (v/-visit (me/error-value explanation printer) printer) printer) :break :break @@ -34,7 +34,7 @@ (v/-block "Schema:" (v/-visit schema printer) printer) :break :break (v/-block "More information:" (v/-link "https://cljdoc.org/d/metosin/malli/CURRENT" printer) printer)]}) -(defmethod v/-format ::m/invalid-input [_ _ {:keys [args input fn-name]} printer] +(defmethod v/-format ::m/invalid-input [_ {:keys [args input fn-name]} printer] {:body [:group (v/-block "Invalid function arguments:" (v/-visit args printer) printer) :break :break @@ -43,7 +43,7 @@ (v/-block "Errors:" (-explain input args 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-output [_ _ {:keys [value args output fn-name]} printer] +(defmethod v/-format ::m/invalid-output [_ {:keys [value args output fn-name]} printer] {:body [:group (v/-block "Invalid function return value:" (v/-visit value printer) printer) :break :break @@ -53,7 +53,7 @@ (v/-block "Errors:" (-explain output value 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-arity [_ _ {:keys [args arity schema fn-name]} printer] +(defmethod v/-format ::m/invalid-arity [_ {:keys [args arity schema fn-name]} printer] {:body [:group (v/-block (str "Invalid function arity (" arity "):") (v/-visit args printer) printer) :break :break @@ -61,7 +61,7 @@ #?(:cljs (v/-block "Function Var:" (v/-visit fn-name 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-schema [_ _ {:keys [schema form]} printer] +(defmethod v/-format ::m/invalid-schema [_ {:keys [schema form]} printer] (let [proposals (seq (me/-most-similar-to #{schema} schema (set (keys (mr/schemas m/default-registry)))))] {:body [:group @@ -71,7 +71,7 @@ :break :break]) (v/-block "More information:" (v/-link "https://cljdoc.org/d/metosin/malli/CURRENT" printer) printer)]})) -(defmethod v/-format ::m/child-error [_ _ {:keys [type children properties] :as data} printer] +(defmethod v/-format ::m/child-error [_ {:keys [type children properties] :as data} printer] (let [form (m/-raw-form type properties children) constraints (reduce (fn [acc k] (if-let [v (get data k)] (assoc acc k v) acc)) nil [:min :max]) size (count children)] @@ -83,7 +83,7 @@ ", expected " (v/-visit constraints printer)] printer) :break :break (v/-block "More information:" (v/-link "https://cljdoc.org/d/metosin/malli/CURRENT" printer) printer)]})) -(defmethod v/-format ::m/invalid-entry [_ _ {:keys [entry naked-keys]} printer] +(defmethod v/-format ::m/invalid-entry [_ {:keys [entry]} printer] {:body [:group (v/-block "Invalid Entry" (v/-visit (vec entry) printer) printer) :break :break diff --git a/src/malli/dev/virhe.cljc b/src/malli/dev/virhe.cljc index 8d8f6b1c9..d7aa606af 100644 --- a/src/malli/dev/virhe.cljc +++ b/src/malli/dev/virhe.cljc @@ -25,9 +25,9 @@ (let [colors (:colors printer -dark-colors) color (get colors color (:error colors))] #?(:cljs [:span body] - :clj (if color - [:span [:pass (str "\033[38;5;" color "m")] body [:pass "\u001B[0m"]] - [:span body])))) + :clj (if color + [:span [:pass (str "\033[38;5;" color "m")] body [:pass "\u001B[0m"]] + [:span body])))) ;; ;; EDN @@ -131,16 +131,24 @@ #?(:clj (defn -location [e ss] - (let [start-with (fn [f s] (-> f first str (str/starts-with? s))) - [target _ file line] (loop [[f :as fs] (-> e Throwable->map :trace), [s :as ss] ss] - (cond (start-with f s) (recur (rest fs) ss) - (seq (rest ss)) (recur fs (rest ss)) - :else f))] - (try (let [file-name (str/replace file #"(.*?)\.\S[^\.]+" "$1") - target-name (name target) - ns (str (subs target-name 0 (or (str/index-of target-name (str file-name "$")) 0)) file-name)] - (str ns ":" line)) - (catch Exception _))))) + (try + (let [start-with (fn [f s] (-> f first str (str/starts-with? s))) + [target _ file line] (loop [[f :as fs] (-> e Throwable->map :trace), [s :as ss] ss] + (cond (start-with f s) (recur (rest fs) ss) + (seq (rest ss)) (recur fs (rest ss)) + :else f))] + (let [file-name (str/replace file #"(.*?)\.\S[^\.]+" "$1") + target-name (name target) + ns (str (subs target-name 0 (or (str/index-of target-name (str file-name "$")) 0)) file-name)] + (str ns ":" line))) + (catch Exception _)))) + +#?(:clj + (defn hierarchy [^Class k] + (loop [sk (.getSuperclass k), ks [k]] + (if-not (= sk Object) + (recur (.getSuperclass sk) (conj ks sk)) + ks)))) (defn -title [message source {:keys [width] :as printer}] (let [between (- width (count message) 8 (count source))] @@ -172,20 +180,23 @@ ;; formatting ;; -(defmulti -format (fn [type _ _ _] type) :default ::default) +(defmulti -format (fn [e _ _] (-> e (ex-data) :type)) :default ::default) -(defmethod -format ::default [_ message data printer] - {:body - [:group - (-block "Message:" (-color :string message printer) printer) :break :break - (-block "Data:" (-visit data printer) printer)]}) +(defmethod -format ::default [e data printer] + (if-let [format #(:clj (some (methods -format) (hierarchy (class e))), :cljs nil)] + (format e data printer) + {:body + [:group + (-block "Type:" (-visit (type e) printer) printer) :break :break + (-block "Message:" (-color :string (ex-message e) printer) printer) + (when-let [data (ex-data e)] + [:group :break :break (-block "Ex-data:" (-visit data printer) printer)])]})) ;; ;; documents ;; (defn -exception-doc [e printer] - (let [{:keys [type data]} (ex-data e) - {:keys [title body] :or {title (:title printer)}} (-format type (ex-message e) data printer) + (let [{:keys [title body] :or {title (:title printer)}} (-format e (-> e (ex-data) :data) printer) location #?(:clj (-location e (:throwing-fn-top-level-ns-names printer)), :cljs nil)] (-section title location body printer)))