# walkmap/tag.clj

001 (ns walkmap.tag
002 "Code for tagging, untagging, and finding tags on objects. Note the use of
003 the namespaced keyword, `:walkmap.tag/tags`, denoted in this file `::tags`.
004 This is in an attempt to avoid name clashes with other uses of this key."
005 (:require [clojure.set :refer [difference union]]))
006
007 (defn tagged?
008 "True if this `object` is tagged with each of these `tags`. It is an error
009 (and an exception will be thrown) if
010
011 1. `object` is not a map;
012 2. any of `tags` is not a keyword."
013 [object & tags]
014 (if
015 (map? object)
016 (if
017 (every? keyword? tags)
018 (let [ot (::tags object)]
019 (and
020 (set? ot)
021 (every? ot tags)))
022 (throw (IllegalArgumentException.
023 (str "Must be keyword(s): " (map type tags)))))
024 (throw (IllegalArgumentException.
025 (str "Must be a map: " (type object))))))
026
027 (defn tag
028 "Return an object like this `object` but with these `tags` added to its tags,
029 if they are not already present. It is an error (and an exception will be
030 thrown) if
031
032 1. `object` is not a map;
033 2. any of `tags` is not a keyword."
034 [object & tags]
035 (if
036 (map? object)
037 (if
038 (every? keyword? tags)
039 (assoc object ::tags (union (set tags) (::tags object)))
040 (throw (IllegalArgumentException.
041 (str "Must be keyword(s): " (map type tags)))))
042 (throw (IllegalArgumentException.
043 (str "Must be a map: " (type object))))))
044
045 (defmacro tags
046 "Return the tags of this object, if any."
047 [object]
048 `(::tags ~object))
049
050 (defn untag
051 "Return an object like this `object` but with these `tags` removed from its
052 tags, if present. It is an error (and an exception will be thrown) if
053
054 1. `object` is not a map;
055 2. any of `tags` is not a keyword."
056 [object & tags]
057 (if
058 (map? object)
059 (if
060 (every? keyword? tags)
061 (assoc object ::tags (difference (::tags object) (set tags)))
062 (throw (IllegalArgumentException.
063 (str "Must be keywords: " (map type tags)))))
064 (throw (IllegalArgumentException.
065 (str "Must be a map: " (type object))))))