Much more work on duck-typing.

This commit is contained in:
Simon Brooke 2022-12-19 21:38:12 +00:00
parent 032dcb7536
commit b2ff133e4a
3 changed files with 275 additions and 20 deletions

View file

@ -20,7 +20,7 @@
;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
(defn object?
"Return `true` iff `x` is recognisably an ActivityStreams object.
"Returns `true` iff `x` is recognisably an ActivityStreams object.
**NOTE THAT** The ActivityStreams spec
[says](https://www.w3.org/TR/activitystreams-core/#object):
@ -34,9 +34,6 @@
[x]
(and (map? x) (:type x) true))
(object? nil)
(object? {:type "test"})
(defn persistent-object?
"`true` iff `x` is a persistent object.
@ -49,31 +46,142 @@
(persistent-object? {:type "test" :id "https://mastodon.scot/@barfilfarm"})
(defn actor?
"TODO!"
[x]
true)
(def ^:const actor-types
"The set of types we will accept as actors.
There's an [explicit set of allowed actor types]
(https://www.w3.org/TR/activitystreams-vocabulary/#actor-types)."
#{"Application"
"Group"
"Organization"
"Person"
"Service"})
(def verb?
(defn actor-type?
;; TODO: better as a macro
[x]
(if (actor-types x) true false))
(def ^:const verb-types
"The set of types we will accept as verbs.
There's an [explicit set of allowed verbs]
There's an [explicit set of allowed verb types]
(https://www.w3.org/TR/activitystreams-vocabulary/#activity-types)."
#{"Accept" "Add" "Announce" "Arrive" "Block" "Create" "Delete" "Dislike"
"Flag" "Follow" "Ignore" "Invite" "Join" "Leave" "Like" "Listen" "Move"
"Offer" "Question" "Reject" "Read" "Remove" "TentativeAccept"
"TentativeReject" "Travel" "Undo" "Update" "View"})
(defn activity?
"`true` iff `x` is an activity, else false.
(defn verb-type?
;; TODO: better as a macro
[x]
(if (verb-types x) true false))
see "
(def ^:const activitystreams-context-uri
"The URI of the context of an ActivityStreams object is expected to be this
literal string."
"https://www.w3.org/ns/activitystreams")
(defn context?
"Returns `true` iff `x` quacks like an ActivityStreams context, else false.
A context is either
1. the URI (actually an IRI) `activitystreams-context-uri`, or
2. a collection comprising that URI and a map."
[x]
(cond
(nil? x) false
(string? x) (and (= x activitystreams-context-uri) true)
(coll? x) (and (context? (first (remove map? x)))
(= (count x) 2)
true)
:else false))
(defmacro has-context? [x]
`(context? ((keyword "@context") ~x)))
(defn actor?
"Returns `true` if `x` quacks like an actor, else false."
[x]
(and
(object? x)
(has-context? x)
(uri? (URI. (:inbox x)))
(uri? (URI. (:outbox x)))
(actor-type? (:type x))
true))
(defn activity?
"`true` iff `x` quacks like an activity, else false."
[x]
(try
(and (object? x)
(uri? (URI. ((keyword "@context") x)))
(has-context? x)
(string? (:summary x))
(actor? (:actor x))
(verb? (:type x))
(or (object? (:object x)) (uri? (URI. x))))
(catch URISyntaxException _ false)))
(verb-type? (:type x))
(or (object? (:object x)) (uri? (URI. (:object x))))
true)
(catch URISyntaxException _ false)))
(defn link?
"`true` iff `x` quacks like a link, else false."
[x]
(and (object? x)
(= (:type x) "Link")
(uri? (URI. (:href x)))
true))
(defn link-or-uri?
"`true` iff `x` is either a URI or a link, else false.
There are several points in the specification where e.g. the `:image`
property (if present) may be either a link or a URI."
[x]
(and
(cond (string? x) (uri? (URI. x))
:else (link? x))
true))
(defn collection?
"`true` iff `x` quacks like a collection of type `type`, else `false`.
With one argument, will recognise plain collections and ordered collections,
but (currently) not collection pages."
([x type]
(let [items (or (:items x) (:orderedItems x))]
(and
(cond
(:items x) (nil? (:orderedItems x))
(:orderedItems x) (nil? (:items x))) ;; can't have both properties
(object? x)
(= (:type x) type)
(coll? items)
(every? object? items)
(integer? (:totalItems x))
true)))
([x]
(or (collection? x "Collection")
(collection? x "OrderedCollection"))))
(defn unordered-collection?
"`true` iff `x` quacks like an unordered collection, else `false`."
[x]
(collection? x "Collection"))
(defn ordered-collection?
"`true` iff `x` quacks like an ordered collection, else `false`."
[x]
(collection? x "OrderedCollection"))
(defn collection-page?
"`true` iff `x` quacks like a page in a paged collection, else `false`."
[x]
(collection? x "CollectionPage"))
(defn ordered-collection-page?
"`true` iff `x` quacks like a page in an ordered paged collection, else `false`."
[x]
(collection? x "OrderedCollectionPage"))