diff --git a/docs/cloverage/codecov.json b/docs/cloverage/codecov.json
index 8a65628..def2f8e 100644
--- a/docs/cloverage/codecov.json
+++ b/docs/cloverage/codecov.json
@@ -1,18 +1,55 @@
{"coverage":
- {"dog_and_duck/quack/quack.clj":
+ {"dog_and_duck/quack/picky/constants.clj":
+ [null, 1, null, null, null, null, null, null, null, null, null, null,
+ null, null, null, null, null, null, null, null, 1, null, null, null,
+ null, 1, null, null, null, null, 1, null, null, null, null, null, 1,
+ null, null, null, 1, null, 1, null, null, null, null, null, null,
+ null, null, null, null, null, 1, null, 1, null, 1, 1, 1, 1, 1, 1,
+ null, 1, null, null, null, null, 1, null, null, null, null, 1, null,
+ null, null, null],
+ "dog_and_duck/quack/picky/utils.clj":
+ [null, 1, null, null, null, null, null, null, null, null, null, null,
+ null, null, null, null, null, null, null, null, null, null, null,
+ null, null, null, null, null, null, null, null, null, null, null, 1,
+ null, null, 7, null, 1, null, null, null, 0, null, 1, null, null,
+ null, null, null, 0, 0, 0, 0, 0, null, 1, null, null, 0, 0, 0, null,
+ null, null, null, true, null, null, 0, null, null, 1, null, null,
+ null, 4, null, 1, null, null, null, 0, 0, 0, 0, null, 1, null, null,
+ null, 3, 3, true, 3, null, 1, null, null, null, true, true, 45, 45,
+ 45, 70, 45, null, 0, 0, null, 0, 0, null, 1, null, null, null, null,
+ null, null, true, 162, 66, true, 6, null, null, null, 53, null,
+ null, 3, null, 1, null, null, null, null, null, null, null, null,
+ 220, 220, 220, 220, null, 220, null, 220, null, 220, 220, true,
+ null, 0, 0, null, 53, null, null, 2, 2, null, 1, null, null, null,
+ true, null, 1, null, null, null, null, null, null, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, null, 0, 0, 0, 0, 0, 0, null, 1, null, null, null, null,
+ null, null, null, null, null, 0, 0, null, 0, null, 3, null, null,
+ null, 3, null, 1, null, null, null, null, null, 0, null, 0, 0, null,
+ null, 1, null, null, null, null, null, null, true, 53, 53, 53, 3,
+ 53, 53, 41, 53, 9, 53, 16, null, 0, 0, 0, 0, 0, null, null, 1, null,
+ null, null, null, null, null, null, null, null, null, null, null,
+ null, null, null, null, null, 0, 0, 0, 0, 0, 0, null, 0, 0, 0, null,
+ null, 0, 0, 0, null, 0, 0, 0, 0, 0, null, 0, 0, 0, 0, 0, null, 1,
+ null, null, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, null, 0, 0, 0, 0],
+ "dog_and_duck/quack/quack.clj":
[null, 1, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, 1, null, null, null, null, null, null, null,
+ null, null, null, null, null, null, 1, null, null, null, null, null,
+ null, null, null, null, null, null, null, null, null, null, null,
+ null, null, null, 34, null, 34, null, 1, null, null, null, null,
+ null, 4, null, 4, null, 1, null, 3, null, 3, null, 1, null, null,
+ null, null, null, null, null, 0, 0, 0, 0, null, null, null, null, 1,
+ null, 0, null, 0, null, 1, null, 0, null, 0, null, 1, null, null,
+ null, null, null, 0, 0, 0, null, null, 1, null, null, null, null,
+ null, true, true, true, true, 2, 1, null, 1, 1, true, true, 1, null,
+ null, null, null, null, null, null, null, 0, 0, 0, 0, null, null, 1,
+ null, null, 0, null, 1, null, null, 0, null, 1, null, null, 0, null,
+ 1, null, null, 2, null, null],
+ "dog_and_duck/quack/picky/fault_messages.clj":
+ [null, 1, null, null, null, null, null, null, null, null, null, null,
+ null, null, null, null, null, null, null, null, null, 1, null, 1,
null, null, null, null, null, null, null, null, null, null, null,
- null, 35, null, 35, null, 1, null, null, null, null, null, 4, null,
- 4, null, 1, null, 3, null, 3, null, 1, null, null, null, null, null,
- null, null, 0, 0, 0, 0, null, null, null, null, 1, null, 0, null, 0,
- null, 1, null, 0, null, 0, null, 1, null, null, null, null, null, 0,
- 0, 0, null, null, 1, null, null, null, null, null, true, true, true,
- true, 2, 1, null, 1, 1, true, true, 1, null, null, null, null, null,
- null, null, null, 0, 0, 0, 0, null, null, 1, null, null, 0, null, 1,
- null, null, 0, null, 1, null, null, 0, null, 1, null, null, 2, null,
null],
"dog_and_duck/utils/process.clj":
[null, 1, null, null, null, null, null, null, null, null, null, null,
@@ -30,8 +67,15 @@
"dog_and_duck/scratch/parser.clj":
[null, 1, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null,
- null, 1, null, null, null, 11, 11, 11, 11, 11, true, 0, null, 1,
- null, 1, 0, 0, 0, 0, 0, 0, 1, null, 1],
+ null, 1, null, null, null, 9, 9, 9, 9, 9, true, 0, null, null, null,
+ null, null, null, null, null, null, null, null, null, null],
+ "dog_and_duck/quack/picky/required_properties.clj":[null, 1],
+ "dog_and_duck/quack/picky/control_variables.clj":
+ [null, 1, null, null, null, null, null, null, null, null, null, null,
+ null, null, null, null, null, null, null, null, 1, null, null, null,
+ null, null, null, null, null, null, null, null, null, null, null,
+ null, null, null, null, null, null, 1, null, null, null, null, null,
+ null, null, null],
"clj_activitypub/core.clj":
[null, 1, null, null, null, null, null, null, null, null, null, 1,
null, null, null, null, null, 0, 0, 0, 0, 0, 0, 0, 0, null, 1, null,
@@ -53,44 +97,19 @@
null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null,
- null, 1, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, 1, null,
- null, null, null, null, null, null, null, null, 1, null, null, null,
- 1, null, 1, null, null, null, null, null, null, null, null, null,
- null, null, 1, null, 1, null, 1, 1, 1, 1, 1, 1, null, 1, null, null,
- 0, null, 1, null, null, null, null, null, 0, 0, 0, 0, 0, null, 1,
- null, null, null, true, true, 46, 46, 46, 72, 46, null, 0, 0, null,
- 0, 0, null, 1, null, null, null, null, 1, null, null, null, null, 1,
- null, null, null, null, null, null, true, 261, 123, true, 6, null,
- null, null, 89, null, null, 3, null, 1, null, null, null, null,
- null, null, null, null, 372, 372, 372, 372, null, 372, null, 372,
- null, 372, 372, true, null, 0, 0, null, true, null, null, 6, 6,
- null, 1, null, null, null, null, null, null, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, null, 0, 0, 0, 0, 0, 0, null, 1, null, null, null, null, null,
- true, 89, 89, 89, 5, 89, 89, 66, 89, 13, 89, 23, null, 0, 0, 0, 0,
- 0, null, null, 0, null, 1, null, null, null, null, 0, null, 16, 16,
- null, 0, null, 0, null, 12, null, 1, null, null, true, 17, 17, 17,
- 17, 17, 14, 13, 8, null, 1, null, 0, 3, null, 1, null, null, null,
- null, 1, null, null, null, null, null, 1, null, null, 12, null, 1,
- null, null, null, 8, 8, true, 8, null, 1, null, null, true, 3, 3, 3,
- 3, 1, 3, 3, 3, 3, null, 1, null, null, null, null, 1, null, null,
- null, null, 1, null, null, null, 4, null, 1, null, null, null, 0, 0,
- 0, 0, null, 1, null, null, null, null, null, 0, null, 0, 0, null, 1,
- null, null, null, null, 0, 0, 0, 0, null, null, null, 1, null, null,
- null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, 0, 0, 0, 0, 0, 0, null, 0, 0, 0, null, null, 0, 0,
- null, 0, 0, 0, 0, 0, null, 0, 0, 0, 0, 0, null, 1, null, null, 0,
- null, 1, null, null, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, null, 0,
- 0, 0, 0, null, 1, null, null, null, true, 0, true, true, null, 1,
- null, null, null, 1, null, 1, null, null, 1, null, 1, 0, null, null,
- null, 1, null, null, 1, 1, 1, 1, null, 1, 1, 1, 1, 1, 1, null, 1, 1,
- 1, 0, null, null, null, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, null, 1, null, null, null, null, null, null, null, 0, 0, 0, null,
- 0, 0, 0, 0, null, 1, null, 0, 0, 0, 0, 0, 0, 0, 0, 0],
- "dog_and_duck/quack/fault_messages.clj":
- [null, 1, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, 1, null, 1, null, null,
- null, null, null, null, null, null, null, null],
+ null, null, null, null, null, null, 1, null, null, null, null, 0,
+ null, 6, 6, null, 0, null, 0, null, 4, null, 1, null, null, 12, 12,
+ 12, 12, 9, 8, 4, null, 1, null, 0, 3, null, 0, 0, 0, 0, null, 1,
+ null, null, 3, 3, 3, 3, 1, 3, 3, 3, 3, null, 1, null, null, null,
+ null, 0, 0, 0, 0, 0, 0, null, null, null, 1, null, null, null, true,
+ 0, true, true, null, 1, null, null, null, 1, null, 1, null, null, 1,
+ null, 1, 0, null, null, null, 1, null, null, 1, 1, 1, 1, null, 1, 1,
+ 1, 1, 1, 1, null, 1, 1, 1, 0, null, null, null, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, null, 1, null, null, null, null, null,
+ null, null, 0, 0, 0, null, 0, 0, 0, 0, null, 1, null, 0, 0, 0, 0, 0,
+ 0, 0, null, 1, null, null, null, null, null, null, null, null, null,
+ null, null, null, null, null, null, 0, 0, 0, 0, 0, 0, null, null,
+ null, null, null, 0, 0, 0, 0, null, null, 0, 0],
"dog_and_duck/scratch/core.clj":
[null, 1, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, 1, null, null, 0],
@@ -103,4 +122,10 @@
null, null, null, null, null, null, null, null, null, null, null,
null, null, 1, null, 1, 1, 1, null, null, null, null, 1, null, 1, 1,
1, 1, null, null, 1, null, null, null, null, null, null, null, 1, 1,
- 1, 1, 1, 1, null, null]}}
+ 1, 1, 1, 1, null, null],
+ "dog_and_duck/quack/picky/collections.clj":
+ [null, 1, null, null, null, null, null, null, null, null, null, null,
+ null, null, null, null, null, null, null, null, null, null, null,
+ null, 1, null, null, null, 0, 0, 0, 0, 0, 0, null, 1, null, null,
+ null, 0, 0, 0, 0, 0, 0, 0, null, 1, null, 0, 0, 0, 0, 0, null, null,
+ 0, 0]}}
diff --git a/docs/cloverage/coverage.xml b/docs/cloverage/coverage.xml
index 6417165..a91e783 100644
--- a/docs/cloverage/coverage.xml
+++ b/docs/cloverage/coverage.xml
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/cloverage/dog_and_duck/quack/picky.clj.html b/docs/cloverage/dog_and_duck/quack/picky.clj.html
index 18b3ae0..9364e58 100644
--- a/docs/cloverage/dog_and_duck/quack/picky.clj.html
+++ b/docs/cloverage/dog_and_duck/quack/picky.clj.html
@@ -92,1558 +92,670 @@
029 ActivityStreams spec."
- 030 (:require [clojure.set :refer [intersection]]
+ 030 (:require [dog-and-duck.quack.picky.collections :refer [collection-page-faults
- 031 [dog-and-duck.quack.fault-messages :refer [messages]]
+ 031 paged-collection-faults
- 032 [dog-and-duck.utils.process :refer [get-hostname get-pid]]
+ 032 simple-collection-faults]]
- 033 [taoensso.timbre :as timbre
+ 033 [dog-and-duck.quack.picky.constants :refer [actor-types]]
- 034 ;; Optional, just refer what you like:
+ 034 [dog-and-duck.quack.picky.utils :refer [any-or-faults
- 035 :refer [warn]]
+ 035 coll-object-reference-or-fault
- 036 [clojure.data.json :as json])
+ 036 concat-non-empty
- 037 (:import [java.net URI URISyntaxException]))
-
-
- 038
+ 037 has-activity-type?
- 039 ;;; Copyright (C) Simon Brooke, 2022
-
-
- 040
+ 038 has-actor-type? has-type?
- 041 ;;; This program is free software; you can redistribute it and/or
+ 039 has-type-or-fault
- 042 ;;; modify it under the terms of the GNU General Public License
+ 040 make-fault-object
- 043 ;;; as published by the Free Software Foundation; either version 2
+ 041 object-faults
- 044 ;;; of the License, or (at your option) any later version.
+ 042 object-reference-or-faults
+
+
+ 043 string-or-fault]])
+
+
+ 044 (:import [java.net URI URISyntaxException]))
045
- 046 ;;; This program is distributed in the hope that it will be useful,
-
-
- 047 ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-
-
- 048 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-
-
- 049 ;;; GNU General Public License for more details.
+ 046 ;;; Copyright (C) Simon Brooke, 2022
- 050
+ 047
- 051 ;;; You should have received a copy of the GNU General Public License
+ 048 ;;; This program is free software; you can redistribute it and/or
- 052 ;;; along with this program; if not, write to the Free Software
+ 049 ;;; modify it under the terms of the GNU General Public License
- 053 ;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ 050 ;;; as published by the Free Software Foundation; either version 2
+
+
+ 051 ;;; of the License, or (at your option) any later version.
- 054
+ 052
- 055 ;; ERRATA
+ 053 ;;; This program is distributed in the hope that it will be useful,
+
+
+ 054 ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+
+
+ 055 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+
+
+ 056 ;;; GNU General Public License for more details.
- 056
-
-
- 057 (def ^:dynamic *reify-refs*
+ 057
- 058 "If `true`, references to objects in fields will be reified and validated.
+ 058 ;;; You should have received a copy of the GNU General Public License
- 059 If `false`, they won't, but an `:info` level fault report will be generated.
+ 059 ;;; along with this program; if not, write to the Free Software
- 060
+ 060 ;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+
+ 061
+
+
+ 062 (defn uri-or-fault
- 061 There are several things in the spec which, in a document, may correctly be
+ 063 "If `u` is not a valid URI, return a fault object with this `severity` and
- 062 either
+ 064 `if-invalid-token`. If it's `nil`, return a fault object with this `severity`
- 063
+ 065 and `if-missing-token`. Otherwise return nil."
- 064 1. a fully fleshed out object, or
+ 066 ([u severity if-missing-token]
+
+
+ 067 (uri-or-fault u severity if-missing-token if-missing-token))
- 065 2. a URI pointing to such an object.
+ 068 ([u severity if-missing-token if-invalid-token]
+
+
+ 069 (try
+
+
+ 070 (if (uri? (URI. u))
- 066
+ 071 nil
+
+
+ 072 (make-fault-object severity if-invalid-token))
- 067 Obviously to fully validate a document we ought to reify all the refs and
+ 073 (catch URISyntaxException _
+
+
+ 074 (make-fault-object severity if-invalid-token))
- 068 check that they are themselves valid, but
+ 075 (catch NullPointerException _
-
- 069
-
-
- 070 a. in some of the published test documents the URIs do not reference a
-
-
- 071 valid document;
-
-
- 072 b. there will be performance costs to reifying all the refs;
-
-
- 073 c. in perverse cases, reifying refs might result in runaway recursion.
-
-
- 074
-
-
- 075 TODO: I think that in production this should default to `true`."
-
-
- 076 false)
+
+ 076 (make-fault-object severity if-missing-token)))))
077
-
- 078 (def ^:dynamic *reject-severity*
-
-
- 079 "The severity at which the binary validator will return `false`.
-
-
- 080
-
-
- 081 In practice documents seen in the wild do not typically appear to be
-
-
- 082 fully valid, and this does not matter. This allows the sensitivity of
-
-
- 083 the binary validator (`dog-and-duck.quack.quack`) to be tuned. It's in
-
-
- 084 this (`dog-and-duck.quack.picky`) namespace, not that one, because this
-
-
- 085 namespace is where concerns about severity are handled."
-
-
- 086 :must)
-
-
- 087
-
- 088 (def ^:const context-key
+ 078 (defn persistent-object-faults
- 089 "The Clojure reader barfs on `:@context`, although it is in principle a valid
+ 079 "Return a list of faults found in persistent object `x`, or `nil` if none are."
- 090 keyword. So we'll make it once, here, to make the code more performant and
+ 080 ([x]
-
- 091 easier to read."
+
+ 081 (concat-non-empty
- 092 (keyword "@context"))
+ 082 (object-faults x)
-
- 093
+
+ 083 (list
-
- 094 (def ^:const severity
-
-
- 095 "Severity of faults found, as follows:
-
-
- 096
-
-
- 097 0. `:info` not actually a fault, but an issue noted during validation;
-
-
- 098 1. `:minor` things which I consider to be faults, but which
-
-
- 099 don't actually breach the spec;
-
-
- 100 2. `:should` instances where the spec says something SHOULD
-
-
- 101 be done, which isn't;
-
-
- 102 3. `:must` instances where the spec says something MUST
-
-
- 103 be done, which isn't;
-
-
- 104 4. `:critical` instances where I believe the fault means that
-
-
- 105 the object cannot be meaningfully processed."
+
+ 084 (if (contains? x :id)
- 106 #{:info :minor :should :must :critical})
+ 085 (try (let [id (URI. (:id x))]
-
- 107
-
-
- 108 (def ^:const severity-filters
-
-
- 109 "Hack for implementing a severity hierarchy"
-
-
- 110 {:all #{}
-
-
- 111 :info #{}
-
-
- 112 :minor #{:info}
-
-
- 113 :should #{:info :minor}
+
+ 086 (when-not (= (.getScheme id) "https")
- 114 :must #{:info :minor :should}
-
-
- 115 :critical severity})
-
-
- 116
-
-
- 117 (defn truthy?
+ 087 (make-fault-object :should :id-not-https)))
- 118 "Return `true` if `x` is truthy, else `false`."
+ 088 (catch URISyntaxException _
+
+
+ 089 (make-fault-object :must :id-not-uri))
- 119 [x]
+ 090 (catch NullPointerException _
- 120 (if x true false))
+ 091 (make-fault-object :must :null-id-persistent)))
-
- 121
-
-
- 122 (defn has-type?
+
+ 092 (make-fault-object :must :no-id-persistent)))))
- 123 "Return `true` if object `x` has type `type`, else `false`.
-
-
- 124
-
-
- 125 The values of `type` fields of ActivityStreams objects may be lists; they
-
-
- 126 are considered to have a type if the type token is a member of the list."
-
-
- 127 [x type]
-
-
- 128 (assert (map? x) (string? type))
-
-
- 129 (let [tv (:type x)]
+ 093 ([x types severity token]
- 130 (cond
-
-
- 131 (coll? tv) (truthy? (not-empty (filter #(= % type) tv)))
+ 094 (concat-non-empty
- 132 :else (= tv type))))
+ 095 (persistent-object-faults x)
-
- 133
-
-
- 134 (defn filter-severity
-
-
- 135 "Return a list of reports taken from these `reports` where the severity
-
-
- 136 of the report is greater than this or equal to this `severity`."
-
-
- 137 [reports severity]
-
-
- 138 (cond (nil? reports) nil
-
-
- 139 (and
-
-
- 140 (coll? reports)
-
-
- 141 (every? map? reports)
-
-
- 142 (every? :severity reports)) (remove
-
-
- 143 #((severity-filters severity) (:severity %))
-
-
- 144 reports)
-
-
- 145 :else
-
-
- 146 (throw
-
-
- 147 (ex-info
-
-
- 148 "Argument `reports` was not a collection of fault reports"
+
+ 096 (list
- 149 {:arguments {:reports reports
-
-
- 150 :severity severity}}))))
+ 097 (has-type-or-fault x types severity token)))))
- 151
-
-
- 152 (def ^:const activitystreams-context-uri
-
-
- 153 "The URI of the context of an ActivityStreams object is expected to be this
-
-
- 154 literal string."
-
-
- 155 "https://www.w3.org/ns/activitystreams")
-
-
- 156
-
-
- 157 (def ^:const validation-fault-context-uri
-
-
- 158 "The URI of the context of a validation fault report object shall be this
-
-
- 159 literal string."
-
-
- 160 "https://simon-brooke.github.io/dog-and-duck/codox/Validation_Faults.html")
-
-
- 161
+ 098
- 162 (defn context?
+ 099 (defn actor-faults
- 163 "Returns `true` iff `x` quacks like an ActivityStreams context, else false.
+ 100 "Return a list of faults found in actor `x`, or `nil` if none are."
- 164
+ 101 [x]
-
- 165 A context is either
-
-
- 166 1. the URI (actually an IRI) `activitystreams-context-uri`, or
-
-
- 167 2. a collection comprising that URI and a map."
-
-
- 168 [x]
-
-
- 169 (cond
+
+ 102 (concat-non-empty
- 170 (nil? x) false
+ 103 (persistent-object-faults x)
-
- 171 (string? x) (and (= x activitystreams-context-uri) true)
+
+ 104 (list
-
- 172 (coll? x) (and (context? (first (remove map? x)))
+
+ 105 (when-not (has-actor-type? x)
- 173 (= (count x) 2)
-
-
- 174 true)
-
-
- 175 :else false))
-
-
- 176
-
-
- 177 (defmacro has-context?
-
-
- 178 "True if `x` is an ActivityStreams object with a valid context, else `false`."
-
-
- 179 [x]
-
-
- 180 `(context? (context-key ~x)))
-
-
- 181
-
-
- 182 (defn make-fault-object
-
-
- 183 "Return a fault object with these `severity`, `fault` and `narrative` values.
-
-
- 184
-
-
- 185 An ActivityPub object MUST have a globally unique ID. Whether this is
-
-
- 186 meaningful depends on whether we persist fault report objects and serve
-
-
- 187 them, which at present I have no plans to do."
-
-
- 188 ;; TODO: should not pass in the narrative; instead should use the :fault value
-
-
- 189 ;; to look up the narrative in a resource file.
-
-
- 190 [severity fault]
-
-
- 191 (assoc {}
-
-
- 192 context-key validation-fault-context-uri
+ 106 (make-fault-object :must :not-actor-type))
- 193 :id (str "https://"
-
-
- 194 (get-hostname)
-
-
- 195 "/fault/"
-
-
- 196 (get-pid)
-
-
- 197 ":"
+ 107 (uri-or-fault
- 198 (inst-ms (java.util.Date.)))
+ 108 (:inbox x) :must :no-inbox :invalid-inbox-uri)
-
- 199 :type "Fault"
+
+ 109 (uri-or-fault
+
+
+ 110 (:outbox x) :must :no-outbox :invalid-outbox-uri))))
+
+
+ 111
- 200 :severity severity
-
-
- 201 :fault fault
-
-
- 202 :narrative (or (messages fault)
+ 112 (defn link-faults
- 203 (do
+ 113 "A link object is required to have an `href` property. It may have all of
-
- 204 (warn "No narrative provided for fault token " fault)
+
+ 114 `rel` | `mediaType` | `name` | `hreflang` | `height` | `width` | `preview`
+
+
+ 115 but I *think* they're all optional."
+
+
+ 116 [x]
+
+
+ 117 (concat-non-empty
+
+
+ 118 (object-reference-or-faults x "Link" :critical :expected-link)
+
+
+ 119 (list
+
+
+ 120 (uri-or-fault
- 205 (str fault)))))
+ 121 (:href x) :must :no-href-uri :invalid-href-uri)
+
+
+ 122 (string-or-fault (:mediaType x) :minor :no-media-type #"\w+\/[-+.\w]+")
+
+
+ 123 ;; TODO: possibly more here. Audit against the specs
+
+
+ 124 )))
+
+
+ 125
+
+
+ 126 (def ^:const base-activity-required-properties
+
+
+ 127 "Properties most activities should have. Values are validating functions, each.
+
+
+ 128
+
+
+ 129 See https://www.w3.org/TR/activitystreams-vocabulary/#dfn-activity"
+
+
+ 130 {:summary (fn [v] (when-not (string? v)
+
+
+ 131 (list (make-fault-object :should :no-summary))))
+
+
+ 132 :actor (fn [v] (object-reference-or-faults v actor-types :must :no-actor))
+
+
+ 133 :object (fn [v] (object-reference-or-faults v nil :must :no-object))})
+
+
+ 134
+
+
+ 135 (def ^:const intransitive-activity-required-properties
+
+
+ 136 "Properties intransitive activities should have.
+
+
+ 137
+
+
+ 138 See https://www.w3.org/TR/activitystreams-vocabulary/#dfn-intransitiveactivity"
+
+
+ 139 (dissoc base-activity-required-properties :object))
+
+
+ 140
+
+
+ 141 (def ^:const accept-required-properties
+
+
+ 142 "As base-activity-required-properties, except that the type of the object
+
+
+ 143 is restricted."
+
+
+ 144 (assoc base-activity-required-properties
+
+
+ 145 :object
+
+
+ 146 (fn [v]
+
+
+ 147 (object-reference-or-faults v #{"Invite" "Person"}
+
+
+ 148 :must
+
+
+ 149 :bad-accept-target))))
+
+
+ 150
+
+
+ 151 (def ^:const activity-required-properties
+
+
+ 152 "Properties activities should have, keyed by activity type. Values are maps
+
+
+ 153 of the format of `base-activity-required-properties`, q.v."
+
+
+ 154 {"Accept" accept-required-properties
+
+
+ 155 "Add" base-activity-required-properties
+
+
+ 156 "Announce" base-activity-required-properties
+
+
+ 157 "Arrive" intransitive-activity-required-properties
+
+
+ 158 ;; TODO: is `:location` required for arrive?
+
+
+ 159 "Block" base-activity-required-properties
+
+
+ 160 "Create" base-activity-required-properties
+
+
+ 161 "Delete" base-activity-required-properties
+
+
+ 162 "Dislike" base-activity-required-properties
+
+
+ 163 "Flag" base-activity-required-properties
+
+
+ 164 "Follow" base-activity-required-properties
+
+
+ 165 ;; TODO: is `:object` required to be an actor?
+
+
+ 166 "Ignore" base-activity-required-properties
+
+
+ 167 "Invite" (assoc base-activity-required-properties :target
+
+
+ 168 (fn [v]
+
+
+ 169 (coll-object-reference-or-fault v #{"Event" "Group"}
+
+
+ 170 :must
+
+
+ 171 :bad-accept-target)))
+
+
+ 172 ;; TODO: are here other things one could meaningfully be invited to?
+
+
+ 173 "Join" base-activity-required-properties
+
+
+ 174 "Leave" base-activity-required-properties
+
+
+ 175 "Like" base-activity-required-properties
+
+
+ 176 "Listen" base-activity-required-properties
+
+
+ 177 "Move" base-activity-required-properties
+
+
+ 178 "Offer" base-activity-required-properties
+
+
+ 179 "Question" intransitive-activity-required-properties
+
+
+ 180 "Reject" base-activity-required-properties
+
+
+ 181 "Read" base-activity-required-properties
+
+
+ 182 "Remove" base-activity-required-properties
+
+
+ 183 "TentativeReject" base-activity-required-properties
+
+
+ 184 "TentativeAccept" accept-required-properties
+
+
+ 185 "Travel" base-activity-required-properties
+
+
+ 186 "Undo" base-activity-required-properties
+
+
+ 187 "Update" base-activity-required-properties
+
+
+ 188 "View" base-activity-required-properties})
+
+
+ 189
+
+
+ 190 (defn activity-type-faults
+
+
+ 191 "Return a list of faults found in the activity `x`; if `type` is also
+
+
+ 192 specified, it should be a string naming a specific activity type for
+
+
+ 193 which checks should be performed.
+
+
+ 194
+
+
+ 195 Some specific activity types have specific requirements which are not
+
+
+ 196 requirements."
+
+
+ 197 ([x]
+
+
+ 198 (if (coll? (:type x))
+
+
+ 199 (map #(activity-type-faults x %) (:type x))
+
+
+ 200 (activity-type-faults x (:type x))))
+
+
+ 201 ([x type]
+
+
+ 202 (let [checks (activity-required-properties type)]
+
+
+ 203 (map
+
+
+ 204 #(apply (checks %) (x %))
+
+
+ 205 (keys checks)))))
206
-
- 207 (defmacro nil-if-empty
-
-
- 208 "if `x` is an empty collection, return `nil`; else return `x`."
-
-
- 209 [x]
-
-
- 210 `(if (and (coll? ~x) (empty? ~x)) nil
-
- 211 ~x))
-
-
- 212
-
-
- 213 (defn has-type-or-fault
+ 207 (defn activity-faults
- 214 "If object `x` has a `:type` value which is `acceptable`, return `nil`;
-
-
- 215 else return a fault object with this `severity` and `token`.
-
-
- 216
-
-
- 217 `acceptable` may be passed as either nil, a string, or a set of strings.
-
-
- 218 If `acceptable` is `nil`, no type specific tests will be performed."
-
-
- 219 [x acceptable severity token]
-
-
- 220 (when acceptable
-
-
- 221 (let [tv (:type x)]
-
-
- 222 (when-not
+ 208 [x]
- 223 (cond
+ 209 (concat-non-empty (persistent-object-faults x)
-
- 224 (and (string? tv) (string? acceptable)) (= tv acceptable)
-
-
- 225 (and (string? tv) (set? acceptable)) (acceptable tv)
-
-
- 226 (and (coll? tv) (string? acceptable)) ((set tv) acceptable)
-
-
- 227 (and (coll? tv) (set? acceptable)) (not-empty
-
-
- 228 (intersection (set tv) acceptable))
-
-
- 229 :else
-
-
- 230 (throw (ex-info "Type value or `acceptable` argument not as expected."
-
-
- 231 {:arguments {:x x
-
-
- 232 :acceptable acceptable
-
-
- 233 :severity severity
-
-
- 234 :token token}})))
-
-
- 235 (make-fault-object severity token)))))
-
-
- 236
-
-
- 237 (defn object-faults
-
-
- 238 "Return a list of faults found in object `x`, or `nil` if none are.
-
-
- 239
-
-
- 240 If `expected-type` is also passed, verify that `x` has `expected-type`.
-
-
- 241 `expected-type` may be passed as a string or as a set of strings."
-
-
- 242 ([x]
-
-
- 243 (nil-if-empty
-
-
- 244 (remove empty?
-
-
- 245 (list
-
-
- 246 (when-not (map? x)
-
-
- 247 (make-fault-object :critical :not-an-object))
-
-
- 248 (when-not
-
-
- 249 (has-context? x)
-
-
- 250 (make-fault-object :should :no-context))
-
-
- 251 (when-not (:type x)
-
-
- 252 (make-fault-object :minor :no-type))
-
-
- 253 (when-not (and (map? x) (contains? x :id))
-
-
- 254 (make-fault-object :minor :no-id-transient))))))
-
-
- 255 ([x expected-type]
-
-
- 256 (nil-if-empty
-
-
- 257 (remove empty?
-
-
- 258 (concat
-
-
- 259 (object-faults x)
-
-
- 260 (list
-
-
- 261 ;; TODO: should resolve the correct `-faults`function for the
-
-
- 262 ;; `expected-type` and call that; but that's for later.
-
-
- 263 (has-type-or-fault x expected-type :critical :unexpected-type)))))))
-
-
- 264
-
-
- 265 (defn uri-or-fault
-
-
- 266 "If `u` is not a valid URI, return a fault object with this `severity` and
-
-
- 267 `if-invalid-token`. If it's `nil`, return a fault object with this `severity`
-
-
- 268 and `if-missing-token`. Otherwise return nil."
-
-
- 269 ([u severity if-missing-token]
-
-
- 270 (uri-or-fault u severity if-missing-token if-missing-token))
-
-
- 271 ([u severity if-missing-token if-invalid-token]
-
-
- 272 (try
-
-
- 273 (if (uri? (URI. u))
-
-
- 274 nil
-
-
- 275 (make-fault-object severity if-invalid-token))
-
-
- 276 (catch URISyntaxException _
-
-
- 277 (make-fault-object severity if-invalid-token))
-
-
- 278 (catch NullPointerException _
-
-
- 279 (make-fault-object severity if-missing-token)))))
-
-
- 280
-
-
- 281 (defn persistent-object-faults
-
-
- 282 "Return a list of faults found in persistent object `x`, or `nil` if none are."
-
-
- 283 [x]
-
-
- 284 (nil-if-empty
-
-
- 285 (remove empty?
-
-
- 286 (concat
-
-
- 287 (object-faults x)
-
-
- 288 (list
-
-
- 289 (if (contains? x :id)
-
-
- 290 (try (let [id (URI. (:id x))]
-
-
- 291 (when-not (= (.getScheme id) "https")
-
-
- 292 (make-fault-object :should :id-not-https)))
-
-
- 293 (catch URISyntaxException _
-
-
- 294 (make-fault-object :must :id-not-uri))
-
-
- 295 (catch NullPointerException _
-
-
- 296 (make-fault-object :must :null-id-persistent)))
-
-
- 297 (make-fault-object :must :no-id-persistent)))))))
-
-
- 298
-
-
- 299 (def ^:const actor-types
-
-
- 300 "The set of types we will accept as actors.
-
-
- 301
-
-
- 302 There's an [explicit set of allowed actor types]
-
-
- 303 (https://www.w3.org/TR/activitystreams-vocabulary/#actor-types)."
-
-
- 304 #{"Application"
-
-
- 305 "Group"
-
-
- 306 "Organization"
-
-
- 307 "Person"
-
-
- 308 "Service"})
-
-
- 309
-
-
- 310 (defn actor-type?
-
-
- 311 "Return `true` if the `x` is a recognised actor type, else `false`."
-
-
- 312 [^String x]
-
-
- 313 (if (actor-types x) true false))
-
-
- 314
-
-
- 315 (defn has-actor-type?
-
-
- 316 "Return `true` if the object `x` has a type which is an actor type, else
-
-
- 317 `false`."
-
-
- 318 [x]
-
-
- 319 (let [tv (:type x)]
-
-
- 320 (cond
-
-
- 321 (coll? tv) (truthy? (not-empty (filter actor-type? tv)))
-
-
- 322 :else (actor-type? tv))))
-
-
- 323
-
-
- 324 (defn actor-faults
-
-
- 325 "Return a list of faults found in actor `x`, or `nil` if none are."
-
-
- 326 [x]
-
-
- 327 (nil-if-empty
-
-
- 328 (remove empty?
-
-
- 329 (concat (persistent-object-faults x)
-
-
- 330 (list
-
-
- 331 (when-not (has-actor-type? x)
-
-
- 332 (make-fault-object :must :not-actor-type))
-
-
- 333 (uri-or-fault
-
-
- 334 (:inbox x) :must :no-inbox :invalid-inbox-uri)
-
-
- 335 (uri-or-fault
-
-
- 336 (:outbox x) :must :no-outbox :invalid-outbox-uri))))))
-
-
- 337
-
-
- 338 (def ^:const verb-types
-
-
- 339 "The set of types we will accept as verbs.
-
-
- 340
-
-
- 341 There's an [explicit set of allowed verb types]
-
-
- 342 (https://www.w3.org/TR/activitystreams-vocabulary/#activity-types)."
-
-
- 343 #{"Accept" "Add" "Announce" "Arrive" "Block" "Create" "Delete" "Dislike"
-
-
- 344 "Flag" "Follow" "Ignore" "Invite" "Join" "Leave" "Like" "Listen" "Move"
-
-
- 345 "Offer" "Question" "Reject" "Read" "Remove" "TentativeAccept"
-
-
- 346 "TentativeReject" "Travel" "Undo" "Update" "View"})
-
-
- 347
-
-
- 348 (defn verb-type?
-
-
- 349 "`true` if `x`, a string, represents a recognised ActivityStreams activity
-
-
- 350 type."
-
-
- 351 [^String x]
-
-
- 352 (if (verb-types x) true false))
-
-
- 353
-
-
- 354 (defn has-activity-type?
-
-
- 355 "Return `true` if the object `x` has a type which is an activity type, else
-
-
- 356 `false`."
-
-
- 357 [x]
-
-
- 358 (let [tv (:type x)]
+
+ 210 (activity-type-faults x)
- 359 (cond
-
-
- 360 (coll? tv) (truthy? (not-empty (filter verb-type? tv)))
+ 211 (list
- 361 :else (actor-type? tv))))
+ 212 (when-not
-
- 362
-
-
- 363 (defn string-or-fault
-
-
- 364 "If this `value` is not a string, return a fault object with this `severity`
-
-
- 365 and `token`, else `nil`. If `pattern` is also passed, it is expected to be
-
-
- 366 a Regex, and the fault object will be returned unless `value` matches the
-
-
- 367 `pattern`."
-
-
- 368 ([value severity token]
-
-
- 369 (when-not (string? value) (make-fault-object severity token)))
-
-
- 370 ([value severity token pattern]
-
-
- 371 (when not (and (string? value) (re-matches pattern value))
+
+ 213 (has-activity-type? x)
- 372 (make-fault-object severity token))))
-
-
- 373
-
-
- 374 (defn link-faults
-
-
- 375 "A link object is required to have an `href` property. It may have all of
-
-
- 376 `rel` | `mediaType` | `name` | `hreflang` | `height` | `width` | `preview`
-
-
- 377 but I *think* they're all optional."
-
-
- 378 [x]
-
-
- 379 (list
-
-
- 380 (uri-or-fault
-
-
- 381 (:href x) :must :no-href-uri :invalid-href-uri)
-
-
- 382 (string-or-fault (:mediaType x) :minor :no-media-type #"\w+\/[-+.\w]+")
-
-
- 383 ;; TODO: possibly more here. Audit against the specs
-
-
- 384 ))
-
-
- 385
-
-
- 386 (defn object-reference-or-faults
-
-
- 387 "If this `value` is either
-
-
- 388
-
-
- 389 1. an object of `expected-type`;
-
-
- 390 2. a URI referencing an object of `expected-type`; or
-
-
- 391 3. a link object referencing an object of `expected-type`
-
-
- 392
-
-
- 393 and no faults are returned from validating the linked object, then return
-
-
- 394 `nil`; else return a sequence comprising a fault object with this `severity`
-
-
- 395 and `token`, prepended to the faults returned.
-
-
- 396
-
-
- 397 As with `has-type-or-fault` (q.v.), `expected-type` may be passed as a
-
-
- 398 string or as a set of strings.
-
-
- 399
-
-
- 400 **NOTE THAT** if `*reify-refs*` is `false`, referenced objects will not
-
-
- 401 actually be checked."
-
-
- 402 [value expected-type severity token]
-
-
- 403 (let [faults (cond
-
-
- 404 (string? value) (try (let [uri (URI. value)
-
-
- 405 object (when *reify-refs*
-
-
- 406 (json/read-str (slurp uri)))]
-
-
- 407 (when object
-
-
- 408 (object-faults object expected-type)))
-
-
- 409 (catch URISyntaxException _
-
-
- 410 (make-fault-object severity token)))
-
-
- 411 (map? value) (if (has-type? value "Link")
-
-
- 412 (cond
-
-
- 413 ;; if we were looking for a link and we've
-
-
- 414 ;; found a link, that's OK.
-
-
- 415 (= expected-type "Link") nil
-
-
- 416 (and (set? expected-type) (expected-type "Link")) nil
-
-
- 417 :else
-
-
- 418 (object-reference-or-faults
-
-
- 419 (:href value) expected-type severity token))
-
-
- 420 (object-faults value expected-type))
-
-
- 421 :else (throw
-
-
- 422 (ex-info
-
-
- 423 "Argument `value` was not an object or a link to an object"
-
-
- 424 {:arguments {:value value}
-
-
- 425 :expected-type expected-type
-
-
- 426 :severity severity
-
-
- 427 :token token})))]
-
-
- 428 (when faults (cons (make-fault-object severity token) faults))))
-
-
- 429
-
-
- 430 (defn link-faults
-
-
- 431 "Return a list of faults found in the link `x`, or `nil` if none are found."
-
-
- 432 [x]
-
-
- 433 (object-reference-or-faults x "Link" :critical :expected-link))
-
-
- 434
-
-
- 435 (defn coll-object-reference-or-fault
-
-
- 436 "As object-reference-or-fault, except `value` argument may also be a list of
-
-
- 437 objects and/or object references."
-
-
- 438 [value expected-type severity token]
-
-
- 439 (cond
-
-
- 440 (map? value) (object-reference-or-faults value expected-type severity token)
-
-
- 441 (coll? value) (nil-if-empty
-
-
- 442 (remove nil?
-
-
- 443 (reduce concat
-
-
- 444 (map
-
-
- 445 #(object-reference-or-faults
-
-
- 446 % expected-type severity token)
-
-
- 447 value))))
-
-
- 448 :else (throw
-
-
- 449 (ex-info
-
-
- 450 "Argument `value` was not an object, a link to an object, nor a list of these."
-
-
- 451 {:arguments {:value value}
-
-
- 452 :expected-type expected-type
-
-
- 453 :severity severity
-
-
- 454 :token token}))))
-
-
- 455
-
-
- 456 (def ^:const base-activity-required-properties
-
-
- 457 "Properties most activities should have. Values are validating functions, each.
-
-
- 458
-
-
- 459 See https://www.w3.org/TR/activitystreams-vocabulary/#dfn-activity"
-
-
- 460 {:summary (fn [v] (when-not (string? v)
-
-
- 461 (list (make-fault-object :should :no-summary))))
-
-
- 462 :actor (fn [v] (object-reference-or-faults v actor-types :must :no-actor))
-
-
- 463 :object (fn [v] (object-reference-or-faults v nil :must :no-object))})
-
-
- 464
-
-
- 465 (def ^:const intransitive-activity-required-properties
-
-
- 466 "Properties intransitive activities should have.
-
-
- 467
-
-
- 468 See https://www.w3.org/TR/activitystreams-vocabulary/#dfn-intransitiveactivity"
-
-
- 469 (dissoc base-activity-required-properties :object))
-
-
- 470
-
-
- 471 (def ^:const accept-required-properties
-
-
- 472 "As base-activity-required-properties, except that the type of the object
-
-
- 473 is restricted."
-
-
- 474 (assoc base-activity-required-properties
-
-
- 475 :object
-
-
- 476 (fn [v]
-
-
- 477 (object-reference-or-faults v #{"Invite" "Person"}
-
-
- 478 :must
-
-
- 479 :bad-accept-target))))
-
-
- 480
-
-
- 481 (def ^:const activity-required-properties
-
-
- 482 "Properties activities should have, keyed by activity type. Values are maps
-
-
- 483 of the format of `base-activity-required-properties`, q.v."
-
-
- 484 {"Accept" accept-required-properties
-
-
- 485 "Add" base-activity-required-properties
-
-
- 486 "Announce" base-activity-required-properties
-
-
- 487 "Arrive" intransitive-activity-required-properties
-
-
- 488 ;; TODO: is `:location` required for arrive?
-
-
- 489 "Block" base-activity-required-properties
-
-
- 490 "Create" base-activity-required-properties
-
-
- 491 "Delete" base-activity-required-properties
-
-
- 492 "Dislike" base-activity-required-properties
-
-
- 493 "Flag" base-activity-required-properties
-
-
- 494 "Follow" base-activity-required-properties
-
-
- 495 ;; TODO: is `:object` required to be an actor?
-
-
- 496 "Ignore" base-activity-required-properties
-
-
- 497 "Invite" (assoc base-activity-required-properties :target
-
-
- 498 (fn [v]
-
-
- 499 (coll-object-reference-or-fault v #{"Event" "Group"}
-
-
- 500 :must
-
-
- 501 :bad-accept-target)))
-
-
- 502 ;; TODO: are here other things one could meaningfully be invited to?
-
-
- 503 "Join" base-activity-required-properties
-
-
- 504 "Leave" base-activity-required-properties
-
-
- 505 "Like" base-activity-required-properties
-
-
- 506 "Listen" base-activity-required-properties
-
-
- 507 "Move" base-activity-required-properties
-
-
- 508 "Offer" base-activity-required-properties
-
-
- 509 "Question" intransitive-activity-required-properties
-
-
- 510 "Reject" base-activity-required-properties
-
-
- 511 "Read" base-activity-required-properties
-
-
- 512 "Remove" base-activity-required-properties
-
-
- 513 "TentativeReject" base-activity-required-properties
-
-
- 514 "TentativeAccept" accept-required-properties
-
-
- 515 "Travel" base-activity-required-properties
-
-
- 516 "Undo" base-activity-required-properties
-
-
- 517 "Update" base-activity-required-properties
-
-
- 518 "View" base-activity-required-properties})
-
-
- 519
-
-
- 520 (defn activity-type-faults
-
-
- 521 "Return a list of faults found in the activity `x`; if `type` is also
-
-
- 522 specified, it should be a string naming a specific activity type for
-
-
- 523 which checks should be performed.
-
-
- 524
-
-
- 525 Some specific activity types have specific requirements which are not
-
-
- 526 requirements."
-
-
- 527 ([x]
-
-
- 528 (if (coll? (:type x))
-
-
- 529 (map #(activity-type-faults x %) (:type x))
-
-
- 530 (activity-type-faults x (:type x))))
-
-
- 531 ([x type]
-
-
- 532 (let [checks (activity-required-properties type)]
-
-
- 533 (map
-
-
- 534 #(apply (checks %) (x %))
-
-
- 535 (keys checks)))))
-
-
- 536
-
-
- 537 (defn activity-faults
-
-
- 538 [x]
-
-
- 539 (nil-if-empty
-
-
- 540 (remove empty?
-
-
- 541 (concat (persistent-object-faults x)
-
-
- 542 (activity-type-faults x)
-
-
- 543 (list
-
-
- 544 (when-not
-
-
- 545 (has-activity-type? x)
+ 214 (make-fault-object :must :not-activity-type))
- 546 (make-fault-object :must :not-activity-type))
+ 215 (when-not (string? (:summary x)) (make-fault-object :should :no-summary)))))
-
- 547 (when-not (string? (:summary x)) (make-fault-object :should :no-summary)))))))
+
+ 216
+
+
+ 217 (defn collection-faults
+
+
+ 218 "Return a list of faults found in the collection `x`; if `type` is also
+
+
+ 219 specified, it should be a string naming a specific collection type for
+
+
+ 220 which checks should be performed.
+
+
+ 221
+
+
+ 222 Every collection *should*(?) have a `totalItems` field (an integer).
+
+
+ 223
+
+
+ 224 Beyond that, collections are either 'just collections' (in which case
+
+
+ 225 they *should* have an `items` field (a sequence)), or else they're paged
+
+
+ 226 collections, in which case they *must*(?) have a `first` field which is
+
+
+ 227 a collection page or a URI pointing to a collection page, and *should*
+
+
+ 228 have a `last` field which is similar.
+
+
+ 229
+
+
+ 230 The pages of collections *should* be collection pages; the pages of
+
+
+ 231 ordered collections *should* be ordered collection pages."
+
+
+ 232 ([x]
+
+
+ 233 (collection-faults
+
+
+ 234 x
+
+
+ 235 (first
+
+
+ 236 (remove nil?
+
+
+ 237 (map #(when (has-type? x %) %)
+
+
+ 238 ["Collection"
+
+
+ 239 "OrderedCollection"
+
+
+ 240 "CollectionPage"
+
+
+ 241 "OrderedCollectionPage"])))))
+
+
+ 242 ([x type]
+
+
+ 243 ;; (log/info "collection-faults called with argumens " x ", " type)
+
+
+ 244 (case type
+
+
+ 245 ("Collection" "OrderedCollection") (any-or-faults
+
+
+ 246 (list (simple-collection-faults x type)
+
+
+ 247 (paged-collection-faults x type))
+
+
+ 248 :must
+
+
+ 249 :no-items)
+
+
+ 250 ("CollectionPage" "OrderedCollectionPage") (collection-page-faults x type)
+
+
+ 251 (list (make-fault-object :critical :expected-collection)))))