Skip to content

Instantly share code, notes, and snippets.

@duarten
Created February 14, 2013 11:47
Show Gist options
  • Save duarten/4952302 to your computer and use it in GitHub Desktop.
Save duarten/4952302 to your computer and use it in GitHub Desktop.
A function to search Hiccup data structures.
(def ^{:private true} not-nil? (comp not nil?))
(defprotocol Matcher
"Protocol for types that can be used to search a Hiccup data structure."
(matches [this elem]))
(defn- comp-name [x y]
(and (not-nil? y) (= (name x) (name y))))
(extend-protocol Matcher
nil
(matches [this [tag & _]]
(= nil tag))
clojure.lang.Keyword
(matches [this [tag & _]]
(comp-name this tag))
java.lang.String
(matches [this [tag & _]]
(comp-name this tag))
clojure.lang.Symbol
(matches [this [tag & _]]
(comp-name this tag))
java.util.regex.Pattern
(matches [this [tag & _]]
(and (not-nil? tag) (not-nil? (re-find this (name tag)))))
clojure.lang.IPersistentSet
(matches [this [tag & _]]
(and (not-nil? tag) (not-nil? (this (keyword (name tag))))))
clojure.lang.IPersistentMap
(matches [this [tag attrs & children]]
(and
(map? attrs)
(every?
(fn [[k v]]
(let [target (attrs k)]
(and
(or (not-nil? target) (nil? v))
(if (fn? v)
(v target)
(matches v [target {}])))))
this)))
java.lang.Object
(matches [this [tag & _]]
(= this tag)))
(defn- is-match? [matcher value]
(if (fn? matcher)
(matcher value)
(matches matcher value)))
(defn text [t]
(reify
Matcher
(matches [_ [tag attrs text]]
(= t text))))
(defn match-any [& matchers]
(reify
Matcher
(matches [_ elem]
(not-nil? (some #(is-match? % elem) matchers)))))
(defn find [x & matchers]
(letfn [(rf [ret [tag attrs & children :as source]]
(if (every? #(is-match? % source) matchers)
(conj! ret source))
(reduce rf ret (filter coll? children)))]
(persistent! (reduce rf (transient []) [x]))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment