Skip to content

Commit

Permalink
Merge pull request #15 from liquidz/dev
Browse files Browse the repository at this point in the history
Next release
  • Loading branch information
liquidz committed Dec 17, 2022
2 parents 1e3b525 + 76b09cd commit 89e8c2f
Show file tree
Hide file tree
Showing 9 changed files with 216 additions and 116 deletions.
46 changes: 15 additions & 31 deletions Makefile
@@ -1,67 +1,51 @@
CP=$(shell clojure -A:dev -Spath)

.PHONY: repl
repl:
iced repl -A:dev --with-kaocha --force-clojure-cli
.PHONY: help
help:
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'

.PHONY: repl-cljs
repl-cljs:
@npx nbb --classpath "$(shell clojure -A:dev -Spath)" nrepl-server

.PHONY: inspect
inspect:
node --inspect target/js/compiled/index.js
repl-cljs: ## Start cljs REPL via nbb
@npx nbb --classpath "$(CP)" nrepl-server

.PHONY: test
test: test-clj test-bb test-cljs test-nbb
test: test-clj test-bb test-cljs test-nbb ## Run all tests

.PHONY: test-clj
test-clj:
test-clj: ## Run clj tests
clojure -M:dev:1.9:test-clj
clojure -M:dev:1.10:test-clj
clojure -M:dev:test-clj

.PHONY: test-bb
test-bb:
test-bb: ## Run babashka tests
@bb --classpath "$(CP)" bb_test_runner.clj

.PHONY: test-cljs
test-cljs:
test-cljs: ## Run cljs tests
@clojure -M:dev:test-cljs

.PHONY: test-nbb
test-nbb:
test-nbb: ## Run nbb tests
@npx nbb --classpath "$(CP)" bb_test_runner.clj

.PHONY: clean
clean:
clean: ## Clean
rm -rf node_modules target .cpcache .clj-kondo/.cache

.PHONY: lint
lint:
lint: ## Check lint errors and format errors
clj-kondo --lint src:test
cljstyle check

.PHONY: outdated
outdated:
outdated: ## Show and upgrade outdated dependencies
clojure -M:outdated --upgrade

.PHONY: pom
pom:
clojure -T:build pom

.PHONY: jar
jar:
clojure -T:build jar

.PHONY: install
install: clean
install: clean ## Install jar to the local Maven repository
clojure -T:build install

.PHONY: deploy
deploy: clean
clojure -T:build deploy

.PHONY: coverage
coverage:
coverage: ## Show coverage
clojure -M:coverage:dev --src-ns-path=src --test-ns-path=test --codecov
47 changes: 39 additions & 8 deletions README.adoc
Expand Up @@ -27,10 +27,10 @@ This library is based on https://adambard.com/blog/acceptable-error-handling-in-
;; (require '[merr.core :as merr :include-macros true])
(merr/let +err+ [foo 1
bar (merr/err)
bar (merr/error)
baz (* bar 2)]
{:+err+ +err+ :foo foo :bar bar :baz baz})
;; => {:+err+ (merr/err), :foo 1, :bar nil, :baz nil}
;; => {:+err+ (merr/error), :foo 1, :bar nil, :baz nil}
----

== Usage
Expand All @@ -55,7 +55,7 @@ This library is based on https://adambard.com/blog/acceptable-error-handling-in-
;; => "c = 12"
(merr/let err [a 10
b (merr/err {:message "ERROR"})
b (merr/error {:message "ERROR"})
c (inc b)]
(if err
(merr/message err)
Expand All @@ -70,8 +70,8 @@ This library is based on https://adambard.com/blog/acceptable-error-handling-in-
(merr/-> 10 (+ 1) (- 1))
;; => 10
(merr/-> 10 ((fn [_] (merr/err))) (- 1))
;; => (merr/err)
(merr/-> 10 ((fn [_] (merr/error))) (- 1))
;; => (merr/error)
----

=== `->>`
Expand All @@ -81,16 +81,47 @@ This library is based on https://adambard.com/blog/acceptable-error-handling-in-
(merr/->> 10 (+ 1) (- 1))
;; => -10
(merr/->> 10 ((fn [_] (merr/err))) (- 1))
;; => (merr/err)
(merr/->> 10 ((fn [_] (merr/error))) (- 1))
;; => (merr/error)
----

=== `try`

[source,clojure]
----
(merr/try (throw (ex-info "ERROR" {})))
;; => merr/err?
;; => merr/error?
----

== Custom error

You can specify any keywords as `:type`, but you may want to define and use specific `:type` of errors in your project.
In that case, `merr.helper` is useful.

[source,clojure]
----
(require '[merr.helper :as merr.h])
;; => nil
(def custom-error (partial merr.h/typed-error ::custom-error-type))
;; => var?
(def custom-error? (partial merr.h/typed-error? ::custom-error-type))
;; => var?
(custom-error? (custom-error {:message "custom error"}))
;; => true
----

`typed-error?` will check child error types, so you can define sub errors via `clojure.core/derive`.

[source,clojure]
----
(derive ::sub-custom-error-type ::custom-error-type)
;; => nil
(custom-error? (merr/error {:type ::sub-custom-error-type}))
;; => true
----

== Integration
Expand Down
4 changes: 3 additions & 1 deletion bb_test_runner.clj
@@ -1,6 +1,8 @@
(ns bb-test-runner
(:require
[clojure.test :as t]
[merr.core-test]))
[merr.core-test]
[merr.helper-test]))

(t/run-tests 'merr.core-test)
(t/run-tests 'merr.helper-test)
2 changes: 1 addition & 1 deletion deps.edn
Expand Up @@ -5,7 +5,7 @@
{:dev
{:extra-paths ["dev" "test"]
:extra-deps {lambdaisland/kaocha {:mvn/version "1.71.1119"}
com.github.liquidz/testdoc {:mvn/version "1.5.106"}
com.github.liquidz/testdoc {:mvn/version "1.5.109"}
orchestra/orchestra {:mvn/version "2021.01.01-1"}}}

:1.9
Expand Down
72 changes: 40 additions & 32 deletions src/merr/core.cljc
Expand Up @@ -9,35 +9,35 @@
(defrecord MerrError
[type message data cause])

(defn err?
(defn error?
"Returns `true` if x is `MerrError`.
```
=> (err? \"foo\")
=> (error? \"foo\")
false
=> (err? (err {:message \"foo\"}))
=> (error? (error {:message \"foo\"}))
true
```"
[x]
(instance? MerrError x))

(defn err
(defn error
"Returns value as `MerrError`.
**NOTE** Default error type is `:error`
```
=> (:type (err {:message \"hello\"}))
=> (:type (error {:message \"hello\"}))
:error
=> (:type (err {:type :custom-error :message \"hello\"}))
=> (:type (error {:type :custom-error :message \"hello\"}))
:custom-error
=> (:data (err {:data {:foo \"bar\"}}))
=> (:data (error {:data {:foo \"bar\"}}))
{:foo \"bar\"}
```"
([] (err {}))
([] (error {}))
([{:keys [type message data cause]
:or {type default-error-type} :as m}]
(clojure.core/-> m
Expand All @@ -53,7 +53,7 @@
(if (some #(% v) ignore-checkers)
[v nil]
`(clojure.core/let [v# ~v]
(if (err? v#) [nil v#] [v# nil]))))
(if (error? v#) [nil v#] [v# nil]))))

(defmacro let
"binding => binding-form init-expr
Expand All @@ -64,12 +64,12 @@
```
=> (let +err+ [a 1
=> b (inc a)]
=> [a b (err? +err+)])
=> [a b (error? +err+)])
[1 2 false]
=> (let +err+ [a (err {:message \"ERR\"})
=> (let +err+ [a (error {:message \"ERR\"})
=> b (inc a)]
=> [a b (err? +err+)])
=> [a b (error? +err+)])
[nil nil true]
```"
{:style/indent 2}
Expand All @@ -93,41 +93,41 @@
"Get error type.
```
=> (type (err {:type :foo :message \"bar\"}))
=> (type (error {:type :foo :message \"bar\"}))
:foo
```"
[e]
(when (err? e) (:type e)))
(when (error? e) (:type e)))

(defn message
"Get error message.
```
=> (message (err {:type :foo :message \"bar\"}))
=> (message (error {:type :foo :message \"bar\"}))
\"bar\"
```"
[e]
(when (err? e) (:message e)))
(when (error? e) (:message e)))

(defn data
"Get error custom data.
```
=> (data (err {:message \"bar\" :data {:hello \"world\"}}))
=> (data (error {:message \"bar\" :data {:hello \"world\"}}))
{:hello \"world\"}
```"
[e]
(when (err? e) (:data e)))
(when (error? e) (:data e)))

(defn cause
"Get error cause.
```
=> (cause (err {:message \"foo\" :cause (err {:message \"bar\"})}))
(err {:message \"bar\"})
=> (cause (error {:message \"foo\" :cause (error {:message \"bar\"})}))
(error {:message \"bar\"})
```"
[e]
(when (err? e) (:cause e)))
(when (error? e) (:cause e)))

(defmacro ->
"Threads the expr through the forms. Inserts x as the
Expand Down Expand Up @@ -181,23 +181,23 @@

(defn- ex->err
[cljs? ex & [m]]
`(err {:type (or (:type ~m)
(:merr/type (ex-data ~ex))
default-error-type)
:message (or (:message ~m)
(if ~cljs?
(.-message ~ex)
(.getMessage ~ex)))
:data (merge (:data ~m)
(ex-data ~ex))
:cause ~ex}))
`(error {:type (or (:type ~m)
(:merr/type (ex-data ~ex))
default-error-type)
:message (or (:message ~m)
(if ~cljs?
(.-message ~ex)
(.getMessage ~ex)))
:data (merge (:data ~m)
(ex-data ~ex))
:cause ~ex}))

#?(:clj (defmacro try
"Returs `MerrError` when Exceptions/Errors are thrown.
```
=> (merr.core/try (throw (ex-info \"hello\" {})))
err?
error?
=> (type (merr.core/try {:type :test} (throw (ex-info \"hello\" {}))))
:test
Expand Down Expand Up @@ -235,3 +235,11 @@
~@body
(catch js/Error ~ex-sym
~(ex->err true ex-sym m))))))

(def ^:deprecated err?
"DEPRECATED"
error?)

(def ^:deprecated err
"DEPRECATED"
error)
46 changes: 46 additions & 0 deletions src/merr/helper.cljc
@@ -0,0 +1,46 @@
(ns merr.helper
(:require
[merr.core :as core]))

(defn typed-error
"Returns type specified `merr.core.MerrError`
```
=> (def custom-error (partial typed-error ::custom))
var?
=> (:type (custom-error))
::custom
=> (:message (custom-error {:message \"hello\"}))
\"hello\"
```"
([error-type]
(typed-error error-type {}))
([error-type {:as m :keys [message data cause]}]
(core/error (assoc m :type error-type))))

(defn typed-error?
"Returns `true` if x is type specified `merr.core.MerrError`
```
=> (def custom-error? (partial typed-error? ::custom))
var?
=> (custom-error? (merr.core/error {:type ::custom}))
true
=> (custom-error? (merr.core/error {:type ::another}))
false
=> (derive ::derived ::custom)
nil
=> (custom-error? (merr.core/error {:type ::derived}))
true
```
"
[error-type err]
(boolean
(some-> (core/type err)
(isa? error-type))))

0 comments on commit 89e8c2f

Please sign in to comment.