error.clj

Branches > main > cdf8636 > files / src / dev / grahp / error.clj

(ns dev.grahp.error
  "Error handling stuff idk sometimes it's annoying in clojure lol"
  (:require [clojure.spec.alpha :as s]
            [clojure.string :as str]))

(defmacro raise
  "Throws an error with msg and an optional map and cause"
  {:arglists '([msg]
               [msg m]
               [msg m cause]
               [exception-class msg]
               [exception-class msg cause])}
  ([msg] `(throw (RuntimeException. ~msg)))
  ([msg m]
   (if (symbol? msg)
     `(throw (~msg ~m))
     `(throw (ex-info ~msg ~m))))
  ([msg m cause]
   (if (symbol? msg)
     `(throw (~msg ~m ~cause))
     `(throw (ex-info ~msg ~m ~cause)))))

#_(defmacro guard
    "If form throws an exception, evaluates handler with the exception as an argument."
    ([form] `(guard ~form (constantly nil)))
    ([form handler]
     `(try ~form
           (catch Exception e# (~handler e#)))))

(defmacro assert!
  "Yields x if it is truthy, else throws an ex-info with optional message and map.
   Only run when *assert* is truthy, otherwise yields x"
  ([x] `(assert! ~x nil))
  ([x msg] `(assert! ~x ~msg {}))
  ([x msg m]
   (if *assert*
     `(or ~x
          (raise (str "Assert Failed!" (when ~msg (str " " ~msg)) "\n"
                      '~x)
                 ~m))
     x)))

#_(defmacro assert2!
    "Yields x if it is truthy, else throws an ex-info with optional message and map.
    Only run when *assert* is truthy, otherwise yields x"
    ([x] `(assert2! ~x {}))
    ([x m & msg]
     (if *assert*
       `(or ~x
            (raise (str "Assert Failed! " ~@(interpose \space msg) \newline (pr-str '~x))
                   ~m))
       x)))

(defmacro ensure!
  "Yields x if it conforms to spec, else throws and ex-info with optional message and map"
  ([spec x] `(ensure! ~spec ~x nil))
  ([spec x msg] `(ensure! ~spec ~x ~msg {}))
  ([spec x msg m]
   `(let [x# ~x]
      (if (s/valid? ~spec x#)
        x#
        (raise (str "Assertion failed: " (pr-str '~x) "\n"
                    (when-let [msg# ~msg]
                      (str msg# "\n"))
                    (str/trim-newline (s/explain-str ~spec x#)))
               (assoc ~m '~x x#))))))

(defmacro ensure-not!
  "Yields x if it does not conform to spec, else throws and ex-info with optional message and map"
  ([spec x] `(ensure-not! ~spec ~x nil))
  ([spec x msg] `(ensure-not! ~spec ~x ~msg {}))
  ([spec x msg m]
   `(let [x# ~x]
      (if (s/valid? ~spec x#)
        (raise (str "Assertion failed: " (pr-str '~x) "\n"
                    (when-let [msg# ~msg]
                      (str msg# "\n"))
                    (str/trim-newline (s/explain-str ~spec x#)))
               (assoc ~m '~x x#))
        x#))))