/
errors.cljc
156 lines (147 loc) · 5.21 KB
/
errors.cljc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
(ns com.yetanalytics.persephone.template.errors
(:require [clojure.string :as string]
#?@(:cljs [[goog.string :as gstring]
[goog.string.format]])))
;; Format function
(def fmt #?(:clj format :cljs gstring/format))
;; Statement Template error messages
(def error-msgs-map
{:all-matchable? "failed: all values need to be matchable"
:none-matchable? "failed: no values can be matchable"
:any-matchable? "failed: at least one matchable value must exist"
:some-any-values? "failed 'any' property: evaluated values must include some values given by 'any'"
:only-all-values? "failed 'all' property: evaluated values must only include values given by 'all'"
:no-unmatch-vals? "failed 'all' property: evaluated values must not include unmatchable values"
:no-none-values? "failed 'none' property: evaluated values must exclude values given by 'none'"
:every-val-present? "failed: all values given by rule must be found in evaluated values."})
(defn- val-str
"Create a pretty-print string representation of a vector, where each
entry is on a new line. Expound if no values are found."
[val-arr]
;; Same predicate as `non-matchable?` in template-validation
(if (or (empty? val-arr) (every? nil? val-arr))
(str " no values found at location") ; redundant `str` for aesthetics
(str " " (string/join "\n " val-arr))))
(defn- rule-str
"Create a pretty-print string representation of a rule, with each
property and its value on a new line."
[rule]
(str " " (-> rule str (string/replace #", (?=:)" ",\n "))))
(defn prop-error-str
"Return an error message string for a Determining Property error.
Format:
```
Template <property> property was not matched.
template <property>:
<value A>
statement <property>:
<value B>
```"
[prop prop-vals real-vals]
(fmt (str "Template %s property was not matched.\n"
" template %s:\n"
"%s\n"
" statement %s:\n"
"%s\n")
prop
prop
(val-str prop-vals)
prop
(val-str real-vals)))
(defn sref-error-str
"Return an error message string for a Statement Ref Template error.
Format:
```
<error message>
<error data>
```"
[failure sref value]
(let [sref-name
(cond
(= "$.object" sref)
"Object Statement Ref"
(= "$.context.statement" sref)
"Context Statement Ref"
:else
"Statement Ref")
err-msg
(case failure
:sref-not-found
(fmt "No %s is present in the Statement:"
sref-name)
:sref-object-type-invalid
(fmt "The objectType of the following %s is not \"StatementRef\":"
sref-name)
:sref-id-missing
(fmt "The following %s has no id value:"
sref-name)
:sref-stmt-not-found
(fmt "Cannot find Statement given by the id in the %s:"
sref-name))]
(fmt (str "%s\n"
"%s")
err-msg
(str value))))
(defn rule-error-str
"Return an error message string for a rule error. Format:
```
Template rule was not followed:
{:location ...,
:property ...}
failed: <reason for error>
statement values:
<value 1>
<value 2>
```"
[rule pred values]
(fmt (str "Template rule was not followed:\n"
"%s\n"
" %s\n"
" statement values:\n"
"%s\n")
(rule-str rule)
(get error-msgs-map pred "failed: unknown error occured")
(val-str values)))
(defn error-msg-str
"Create a pretty error log output when a property or rule is not
followed."
[{:keys [pred vals rule prop sref] :as _error}]
(cond
;; Statement Ref Templates
sref
(let [{fail :sref-failure sref :location} sref]
(sref-error-str fail sref (first vals)))
;; Determining Properties
prop
(let [{prop :det-prop mvals :match-vals} prop]
(prop-error-str prop mvals vals))
;; Rules
:else
(rule-error-str rule pred vals)))
(defn print-errors
"Print all the errors in `error-vec`, grouped by Statement and
Template ID."
[error-vec]
;; Not exactly the most optimized way of doing things, but that's not
;; really a priority when printing
(let [error-vec' (->> error-vec
(reduce (fn [acc {:keys [temp stmt] :as err}]
(update
acc
[temp stmt]
(fn [errs] (if err (conj errs err) [err]))))
{})
(mapv (fn [[header errs]] [header (reverse errs)])))]
(doseq [[[temp-id stmt-id] error-subvec] error-vec']
(print (fmt (str "----- Statement Validation Failure -----\n"
"Template ID: %s\n"
"Statement ID: %s\n"
"\n")
temp-id
stmt-id))
(doseq [error error-subvec]
(println (error-msg-str error))))
(print (fmt (str "-----------------------------\n"
"Total errors found: %d\n"
"\n")
(count error-vec)))))