Fork me on GitHub
#specter
<
2023-05-06
>
sh5411:05:22

Given some hiccup like structure I want to find all things with a tag and a "particular" path to get to the thing. I have a solution but I am not too keen its use of collected.

(require '[com.rpl.specter :as S])
(require '[com.rpl.specter.impl :as i])
(require '[com.rpl.specter.util-macros :as sum])

(defn tag?
  [tag-name]
  #(and (vector? %) (= tag-name (first %))))

;; Based on INDEXED-VALS
(S/defnav
  INDEXED-HEADLINES
  []
  (select* [this structure next-fn]
           ;; could be more efficient with a primitive mutable field
           (let [ih (i/mutable-cell -1)
                 io (i/mutable-cell -1)]
             (sum/doseqres S/NONE [e structure]
                           (let [cell (if ((tag? :headline) e) ih io)]
                             (i/update-cell! cell inc)
                             (next-fn [(i/get-cell cell) e])))))
  ;; TODO: update this like the select*. Outside of scope
  (transform* [this structure next-fn]
              (throw (ex-info "not implemented" {}))))

;; Based on code from 
(def headline-path-walker
  (S/recursive-path [] p
    (S/if-path sequential?
      [INDEXED-HEADLINES
       (S/if-path [S/LAST (tag? :headline)]
         (S/multi-path
          [(S/collect-one S/FIRST)
           S/LAST]
          [(S/collect-one S/FIRST)
           S/LAST
           p])
         [(S/collect-one S/FIRST) S/LAST p])])))
So evaluating
(let [s [:doc
         [:ignore-this]
         [:headline
          [:title [:text [:text-normal "headline"]]]
          [:section]
          [:headline
           [:title [:text [:text-normal "child"]]]
           [:section]
           [:headline
            [:title [:text [:text-normal "grandchild"]]]
            [:section]]
           [:headline
            [:title [:text [:text-normal "grandchild2"]]]
            [:section]]]]
         [:headline
          [:title [:text [:text-normal "other headline"]]]
          [:section]]]]
  (S/select [headline-path-walker
             (S/nthpath 1)]
            s))
Results in
[[0 [:title [:text [:text-normal "headline"]]]]
 [0 0 [:title [:text [:text-normal "child"]]]]
 [0 0 0 [:title [:text [:text-normal "grandchild"]]]]
 [0 0 1 [:title [:text [:text-normal "grandchild2"]]]]
 [1 [:title [:text [:text-normal "other headline"]]]]]
One issue I have is that headline-path-walker results in stuff getting added to collected. I would prefer if it used collect internally but returned a collection of [[0 1 2 ...] [:headline ...]]. Is there any way to do this? I have played around with S/transformed but to no avail.